{"_id":"@keep-network/random-beacon","_rev":"165-99296fc33520b8c03a981e464916b490","name":"@keep-network/random-beacon","dist-tags":{"mainnet":"2.0.0","latest":"2.0.0","goerli":"2.1.0-goerli.6","dapp-development-goerli":"2.1.0-dapp-dev-goerli.4","sepolia":"2.1.0-sepolia.1","dapp-development-sepolia":"2.1.0-dapp-dev-sepolia.0","development":"2.1.0-dev.18"},"versions":{"2.0.0-dev.0":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.0","_id":"@keep-network/random-beacon@2.0.0-dev.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ea5f450256ea42c842c22fce39611fdb77b0bb49","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.0.tgz","fileCount":102,"integrity":"sha512-8bjljuCp9ZpGAuWtdVOkYT/HslAbnoI3DZdNbhCfp9BGPtWQa32XvOHHu25oqY9xj4IgKURoNqGhSbgb4+m5ww==","signatures":[{"sig":"MEQCIFhpZ53tRvOHH9TSimUbMM4CzS5GTm3R7z2ZZsUkJEvnAiA1cy8rri1M91B6PWyAEVzBGTv/OzrLN3W9gYJozm1HJA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14721733,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiMPhcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmooMhAAo3eVTZmCBXQnAiAs7FibPfPYkrpLHxSfV5bmmPYSGxwTZz8+\r\nmDtMR++SZoGbCoT8llWcMpDRDVqofYWAZ11QfVe4WQ/znXKGi7UBaLILd/LM\r\nIWpzhoatBxq/AaI5g8qiWT8CBDtelTUHDno8r2E1RPDwITDC4BJ2Y7pKbDQR\r\nqnPUyN/OLPbctoodBG9VpBh2TvgAsWuOrsnMb1MKJmsZfQ0rH5WjM2VndCHs\r\n7va7IfZJFcGVAgN0yMjO54/7rBTSiFQSE4htBRWzcQBbVze8HD/Bfn717dBX\r\nEU0Sd7x0l+E+6yLRrQTbNUnsPO9SHqZwlZVWJm+nmOMzJ8s7hFeVCAw8J0H4\r\nO5jQ8pFQNrtRtrVWJ6I0dcb8qPoUTrFazRblPvJEY/fwTJpA1F2rRfBYFQa0\r\nUYGzgk1fFvJ2nSg9QHs1bZY319JZt8LqCKChehQzU1cpvDJeQcVyIv73lwlz\r\nHGC5kATN1pyPapjwtbwujXNMV5TZqMZIw5DCD02TN3tXOzS1MJ4gxVdVucsU\r\nMhn5ncsxV9yxUz1WE42se5DeQQknxrHm5fl1e0+kN3q7vr2fDYWO0X7YMr9y\r\nfZX69+PuD5prIOPi3odl2LUsErgXOTcJqpvr8d8bsu09UtlJOPDSEWhcrhtg\r\nBfQaK5koFGY6/TaIyrZkXl8ZA0txj7Q2tXI=\r\n=Nbst\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.0_1647376476545_0.2924265594331854","host":"s3://npm-registry-packages"}},"2.0.0-dev.1":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.1","_id":"@keep-network/random-beacon@2.0.0-dev.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9853a7cf958e1fe3c8beca775d5ccff08b65f818","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.1.tgz","fileCount":106,"integrity":"sha512-76+SBfAD1nJp0oZaTBHUrl1v9E69v6l2cz2MhrBnq4+F1FJU7RSPnXu8/snX87k/xhHXuKwnBDWfpsBugmwTaA==","signatures":[{"sig":"MEYCIQD8bVQRuhJ0gqlaDhqqJ2XI+0sqQ7Uscmh1s2vm8N68uwIhAIPEZIj6GxCK9EcCpNfYuiQY3obzckJKNGJtkUxUmH5W","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14869180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiPHcHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmod3g/7BHJHufLljo14ujXerltlC4tXcPhWKoeQN0ACEoBy3JWvTgf+\r\nN0ysb4e8kURM1gn0ksFT7ycL1T3SvtDJS3csrubwTdRy8t3EkQtcJ00FFqSn\r\n+oJShbrbBEEN1NZa0nZ83r8/hQgWvJiCDBiYPNaT/3efTIqu+j3hBY50cgmz\r\nzrbgxzVPpqp8YOZNTiquB9XhgEMKE53lxep+lEfyhF77+xq6u/JPbp1ttgL2\r\nmN+EkwvpMHqcRCdfK4y3/9USqVZG47jDSuzGZKVW5sd8fQ6IyH36ectiZlsG\r\n2mepOeYy/J39NRdy1546iFc00XT6tgx9l1kPhF6SpKHgOIsdRGTzGIwqasXa\r\nReNnDChKQ+Ek1ad6JaamDpFzI7FhkU/fRo2G3S141Fn1IC+UYCrdwExnuN2R\r\nTcwlSs9vpcqJ6KHrrEi7E7188hKLQsyvsmht6eEahQ/BPua8vgl9uJZ77ofE\r\nslhDzNMdH9M663NnGDy/LiQvx5QK0QJVCpQNl7cxCVPlL8wHs1WIc3APtAxV\r\nAF75QuFmTHqBUA11kege4zROYfaqok4RKfCn0nPYnKmZXNBWhr3NvOQ1OR/n\r\njEDKNRIlOmIS16iJNrAjrp12DKr5V2uSD4C1fv5f7U0UPe3m8kBpOK4QWdSy\r\nkJ9DjYlOEP52rW5WlJMfVryVwkfs9kWUpF8=\r\n=nDXf\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\nfailed <<heartbeats,heartbeat>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[heartbeats]]\n=== Heartbeats\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members that failed the heartbeat and issue a\nheartbeat failure claim. If the required threshold of group members signed\nthe heartbeat failure claim, they can submit it to\n`RandomBeacon.notifyFailedHeartbeat(Heartbeat.FailureClaim calldata claim, uint256 nonce)`\nfunction and have the group members who failed the heartbeat excluded from\nthe sortition pool rewards for a governable time period.\n\nThe submitter of the failed heartbeat claim receives a reward from a separate\nnotifier reward pool, funded by DAO for heartbeat failure claims specifically.\nThis pool is expected to be funded by DAO with tokens saved from sortition pool\nrewards as a result of having operators marked as ineligible for rewards due to\nfailing a heartbeat.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit a heartbeat fail report and mark someone as ineligible for rewards. For\nexample, marking an operator ineligible for rewards for the next two weeks have\na higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.1_1648129799124_0.5525162085762778","host":"s3://npm-registry-packages"}},"2.0.0-dev.2":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.2","_id":"@keep-network/random-beacon@2.0.0-dev.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c010ac4d37d9c75903cac152407bb6dec683435a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.2.tgz","fileCount":106,"integrity":"sha512-0Mx6QZioacmpSzJjX0QIxPpjYeu8uhk+GNHzjgqKMZfIlfUMPLudJZwumVgsQRIH53ElYkLreToxt7JDATAbDQ==","signatures":[{"sig":"MEUCIFnaO0ZWbJxVY/ZUI/3HuLnW/l1GLzmaYta3JMy1UcuPAiEAsLk5Vvmbx2ABEchbp2SPV/jPFfH8yjA+1LzC2tXULT0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14869167,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiPHzxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqcIw//eBYqH7wwTaNkUJcR0Xqd37sRAQOcNrlNblokd7MJ08lJnH4g\r\nM7sbQrX+qIvSbGRF/ThhC1qIjpErk3u/r6oti74+bBhP6RwcIse3dzjOD2ko\r\n5tRVDZPEDhHgSHGXw/EMeLqpREZ6FT4NO5IcHXes9COpUIGE8MiZkcbmvn7k\r\nX601Np3I+zltbSkn55/uwgz9JTyOaD8Zv9dZm+eu5wwy6x+sjdERv74ZVjuM\r\nr2dZFjqSx35DGemnzS3YhACJWMr/1sZ4Q3988Cl8tCcXiASvEd8q0LgzclZY\r\nUgzILMksq12vuaDtFqZRbe4OkJJJjEh7VgOPILLzaOsD5M+RiCVvfbucBmVS\r\nAyjuMiQrmf1m4k4uLr1TPntoikWWRz1RHTpeXL/0h3eaFLscUiztAJdHwhf0\r\nBoEZZIV6IQITdm3+HR+GXHwNxO0hktmGGCmSbC4iYNgNXfN20JFCkvdfPA/5\r\nd9D4oPvQcEBQjsnu3tPtAXjBaP8m5dTMBqTXOvOF+D/ehifDWmqf8GrR/Fvb\r\nlqSRBUig8y3xoAqqTZ0eYVcxs/P7V0HdszkXhB8OOgvI5PEkYQflyHajxfzE\r\n3hvDrhOVk3IExbRuVvCm5tVFkZ6zp6cpeYm3+utu2kfYS+8sCaWelC/eKF5t\r\nj9NtUgsJEOdOOdlRKpSs7rA+GMl7EKLJSO4=\r\n=vSR4\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.2_1648131313395_0.8418841975290801","host":"s3://npm-registry-packages"}},"2.0.0-dev.3":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.3","_id":"@keep-network/random-beacon@2.0.0-dev.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0aee6913ffde3806d3652297155fb2c1bbff6a50","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.3.tgz","fileCount":106,"integrity":"sha512-cqpvGGOBoXqoUxVAqcF5rmlEg8lbmj+LRWzOYW4jaweWVxzRBG2DbaaZ7frHXfJHZ3sicr4cLhlhcqbIuiubYA==","signatures":[{"sig":"MEUCIDtHLoho02RmwxMcNy3BokyecGikYIUGWaC5V/WMXraPAiEAsus4jF2Ss5rzzxTxYcD5IBypMmu5fT/Xa/iup0J6JDg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14869167,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiPKjUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrfjQ//UUhb6P67D16qq0Pjt0QxZOWsdZQn7ZZE72riFw8rIgA12B8L\r\n+uVtIcImaX/d/I8hy49BHNZXf1SwijtLoTmrKX+8MXuFQKfUcYOY2CK9WuHI\r\nxH/XZF8MHONlOBE5zwbUOfwFz2QKZ0YDYG1gFUziqUheONkE+an8ZI2sYG1y\r\ns6kQ6tW7LL2w2f/DmSnhBjvfIWxyfYJAiSaLUkr9CtBA7nvJKhBFjxcx0T8P\r\n/+Ai6M0tj/LzRcgRpKCXbidU7wgmXTkzV0JOOeroUkfrLhiZc6yKsVJvavc5\r\nQz5v1/hP+3soj117p0HsIjCw7J9gdIyr7CtUQI3DIeKn/6d0FXwzmyzm/x0M\r\n25aOxMB3DALoIPRd3K2agdPwNI5dCZn2FYa1f+SWmdaeWuL9v485rRLLchn4\r\nhqViHSbzxmpN1IACMhMuITuHP0VevABNbe9qC33f+mMXTdCKbgCIVho0+Gqb\r\nkkaJKImD1Qhgh2a49sNK1gqFzKpGdd8VaQXAYGI/eAynoLitQ9o9QvtZSydx\r\nDEtn+5Q7blnwbUYRUTL77kXlf7EX7JTCBh9Fas2OolO6rNFG9c0OHggXyB22\r\nA+IeNCiDqXuemI6R3hwXE6vv7sRJdV3qy6vW3+3PpnYGdk+p43rw+XZLj6j/\r\njNqxgO9fn5IewsaPd7YDaq5GPbcQQWOa5Ro=\r\n=SjFP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\nfailed <<heartbeats,heartbeat>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[heartbeats]]\n=== Heartbeats\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members that failed the heartbeat and issue a\nheartbeat failure claim. If the required threshold of group members signed\nthe heartbeat failure claim, they can submit it to\n`RandomBeacon.notifyFailedHeartbeat(Heartbeat.FailureClaim calldata claim, uint256 nonce)`\nfunction and have the group members who failed the heartbeat excluded from\nthe sortition pool rewards for a governable time period.\n\nThe submitter of the failed heartbeat claim receives a reward from a separate\nnotifier reward pool, funded by DAO for heartbeat failure claims specifically.\nThis pool is expected to be funded by DAO with tokens saved from sortition pool\nrewards as a result of having operators marked as ineligible for rewards due to\nfailing a heartbeat.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit a heartbeat fail report and mark someone as ineligible for rewards. For\nexample, marking an operator ineligible for rewards for the next two weeks have\na higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.3_1648142547960_0.9268463384520067","host":"s3://npm-registry-packages"}},"2.0.0-dev.4":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.4","_id":"@keep-network/random-beacon@2.0.0-dev.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7a6e37482304ba8f92db8f1e28aa88b9842af8bd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.4.tgz","fileCount":106,"integrity":"sha512-c5SDmkKYyiUXK2QY2aVzi3gj51qk1y0WAXwA3ZJtHJUXE1ZP9y6hPtvgx8GVQhDOBLuEMY1OjLRJhDexZb5j5A==","signatures":[{"sig":"MEUCIQDMjdDHTCLlPjgo+PlZuJAjJQc487Xvv0Wg/+70vlTuAAIgX2qI2WoIwHnd71jsgo/64hFdhyudU55zQ9Jd0ig8aqg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14847662,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQWjhACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpVWA//X8WvUo2zTHGlRzHNPC6BfwC+VBp5n8lr8zQNgfD5IVRClGJj\r\n6l0+4ij3enbZg8y5uTF/1bQbjDEbfeHW+g2Qo7xwPFnDLaKaCJSO4orBlibD\r\nQ0+4vtUhyudMhrl3nhIAL2b9H+FKwZU9BjbMPgTpgey/I5pWTYKa1jNnWEyS\r\nUVkeZgWyOgWiClc3Cg3/X4CHfRxZNf+L/QvXAC2jezSSN4VYYby29Ct22dgg\r\n8Nq/25kIjY28cs2gEYzzth+LfDQIjzXt7xblyqYDILqe/G7CddqPCmHGaHFa\r\n0ECoI++WHmDQMKUgT3FfSliebWkaCL4ImN3sg+yWTIvkvikEkXqc3SHQ9ePa\r\nzPzMsSymjrHki5rJWYxSyJ9Y1S7cWnyGMvGuCR1QiASyRe9rYLW2GeeorpzS\r\nU8fiJUX0Et5TsY/vlzXi/zAXEDLhHGkhM/yxoFbSQhONnciXrbeGxPIheGHl\r\n/9jappAW90yhdmWZjuZ+EAwCzbxFv2twloAHbJ6cqKukmkj3J0mvKApTxxtr\r\nVwDbnMbBHIZvtPDc3f3YHDk98LNe9e8bz3Nos9e8v1gos6dvZVkaGk0oDpIY\r\nBRflSWwVgab3JLdz7JNjcC7XVTgDN1TOULMCRnfZVLPGa5kre/4/Mxq0md0w\r\nokokFM8QObp7yZb+nl9sbq3gOBF70BVewB8=\r\n=P3k4\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.4_1648453857483_0.5610722346260917","host":"s3://npm-registry-packages"}},"2.0.0-dev.5":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.5","_id":"@keep-network/random-beacon@2.0.0-dev.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bc1f3e0109bb9acebd9c3ccde0bddd22ac5780ab","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.5.tgz","fileCount":106,"integrity":"sha512-2HucYmIweW95E5crC2GEMo3YafQbnW4DWsFrXnZwMbAAKoJhGcVO6dgz2slGjKAJVu4eHxnLuMxIFMJOUpBnZA==","signatures":[{"sig":"MEUCIHcLgYNGhaZ21bLcYYY1fVXNJsGNluKx1yXBxue1PoosAiEAzBqYtN9WkuOye0CLzcn24mcTQIRJbqQxChKOjfZkC4M=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14957979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQxATACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpprQ/+NUOrh1YAQA/AXSJ0pODMiyWK4BM8QeHckaQtRSy1jCndz4pM\r\nYWgWEzz1OUcIZTNuJdJDDXKQio/kpFp8p65TYu+2WQAYg24qxn+XqLWj28W1\r\nwSrE+lJaaIniEm5zWdOnLcfCIH0W6XkMDIyzAZ/XXbW18jRlCu+qN208HF0z\r\nzHgxxslzgzyJlkOZLlSbgw44M1kkM9zAtEUNe4YYHMBpoD4be0J2+94iDLbB\r\nlQCSsoGIsKiSHmdlLK6jf69rqoBNzuM0ajxiGcdj7yvO7T4qBN0ZgPXY4AJV\r\nbOKKlZhHsMTe5mDTkz6i7CkjWKMFLi22XTWRmdWxHQki65O1WiipMNKMMDrR\r\nlYw7ffIPC6l7iMwClc0IKgF8vJFXXt1ZUbmYf5HBCBeklirtPuu5065xl5Xi\r\nO51/kHc3ovofQdMJYblerKtrC6nrI0VHHlJ6bL7LZurNGG+bDbP6D3rq2MTY\r\nj3cNBUjRoZCZXyYb3653YZUap7VtsFgigHpj9g+ehZdBLfiYwqEneFQ1n6VD\r\nmU0y5vqj8ecrJSGW3dTyrfjPag18NYh52bPLi6GR3srTYUM5wyPWPxjf/oA0\r\nUp8ls7mmGnWtlmkqcSo1Lb6K6j6Mi2ksE/uzUQ/1ho0zXw8xpVQ/q2tZjRy7\r\nBrtFl3SK5bISNvZZFtnpp4Jb3mBiN4Mo+RE=\r\n=WU5u\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.5_1648562195657_0.31031191472966824","host":"s3://npm-registry-packages"}},"2.0.0-dev.6":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.6","_id":"@keep-network/random-beacon@2.0.0-dev.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d562f8e3cd67341b7fe30beb9a7e4e735e4a71a5","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.6.tgz","fileCount":106,"integrity":"sha512-57iPXeIVjQLui/m3Rfb9+5JY8FbDj874V++9/E8XKWubbku3TSYta948+kCP/ltihZzIN3CwKP1tRww2+aHHWA==","signatures":[{"sig":"MEQCIFuIAVYgnwP4/doN4SYLxqhmhjpHhohMs1OEKfpQ61C3AiBRpHFe+zgVKypCcFdHUkpOl1zfkSID7UMZErD0hwYvRQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14934738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiREKNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpaLw/+MkQjNDEKBnwpAFRj70JTV2bJt+mips1K/rpr0mZLbOdznJhT\r\nkgcoBT4EZDuX0t0qJ0sIX3W3rYX3P3EPHncOhggLie/p8/t3Ad6t7BTZzyCJ\r\nT5Fa0eY/l9n1tGzgtfwEdynFlExV3FeRfbjU/zrWAcDub2eKMOJis67FjXTR\r\nGgDcRCrwWz4V6HNBtQODxZ7/ykHs+eP8Ttr1kuKytfUdqUNES6vL0LKlFNGU\r\nBZpBnn0Q5qomDI8lMVCJov5iYIg34qR8I/O8c4u7bDMJpYtVQs2/giu5mpa3\r\nkERRT1yiqIrQVccPDGQeB4eKzHfN3/8C434qdity79gKjhj1T2xTSIWGjWrg\r\n03bCfbsrJA7AFyrzYjoXVb78Xd+RzFs08ejhgaCtUe5w8jk39MHy6WxYMww1\r\nwz3rLmyLhLkfaDvtNuAOmC9AFQJPuI0SkY8sDcNsmGOfUihwUFtbKaciT3W6\r\nJzIMgMmo+fFh9ULjYAipJIJQ/2VNI+EQYnIjejHz3VTh7/HP2b9fki0olMUY\r\nP+UJIxc3xQvw4wKdK/QFHBa4Agp0LymsiUdk0FvTqwHZeVs+MTLaBp8z7RIk\r\nDicEGWxKnZ0g8D5uV3i6Feb+jmnqkEUwR6CohDvwCVrkRve8U6zVSxEMivCg\r\nHdBrbpj7JHZ9vqWgHGrhCrxMqRQNijpYA8k=\r\n=WxMP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.6_1648640652898_0.10950506252743564","host":"s3://npm-registry-packages"}},"2.0.0-dev.7":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.7","_id":"@keep-network/random-beacon@2.0.0-dev.7","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"de0808931719c93ffdb67ca0029f444574b4e06f","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.7.tgz","fileCount":106,"integrity":"sha512-CvBJvyHfZUFyTHa4oz2SpOuV2oexe7qG53+HTMBcJeeUyK/+ABvf0FuKloCx+DUtV82g3uKjlf1ZZKQZF/LO0Q==","signatures":[{"sig":"MEYCIQCPttRuDk4eJQY7I8e7bmepEEoxV5daA8py6RqYXJvMMAIhANwG502S2O4d1ACjPB9kozr1EmFT1UepweM+tqSmMI8E","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14910975,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRYOpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrW4hAAmUltdH+WhPiv5oMcJocBEVtaRVDcoez9T+zUI66JVo56PM/S\r\nOu8FbXmfiX8zOM7/r5E1RfyRtoIX+r8e3fhLlK3Qz/j0RqHMSsdrZc5CzyQB\r\nywO9PzmlDZfFSQ+0XR97tRhQQ39phgAGY74t5yRDfSrJ2PuedPPA2dkmOY27\r\no+AhUw37Nn0NzmDy4zXxBP1Dt+qQPpT/ipTWSh98Kua11XaJy5EAJs9D+Ocj\r\nx+fWDWXLt5TKI8OQ/1s+KYtvncmHIbFk0e5ZKYVTI9uZyPhVmXJ0i5NGhmT0\r\nbTRN/lH2PkGVLOmKffwxGEpPJxxqvwVLBeEYNsg9bIBZIZRrF0/S9q6G9laC\r\nZnRRM85/edDtVORFl6Um/WXkiCMEkpY8NxWFoGok3WPPz44leBfZnZHa2/YL\r\nM+uWeVMFnHrTiXYjW4OxnZFEgFAWexXLJ/+Opf3MBT6RsSj9bFUy5NWHi6lU\r\n/QA8q7x51ZgQCUSkiQvWiKedjVSrxzIfu/3R3arRYhq0WqgDG5XQhB95CcCJ\r\n26x3YSdAs0LHECpsVTQbmSAjc/ngi9SfKj+XsmQq1b0KkIkJtMkQEIiW2NM5\r\nIyznCp1YtM8dBF8yqliqqPNkKI/gRCO3ikUQO7AZdUCha1sR0QZMuAsH+hxF\r\nRZT0mbf77wLIfZC0TjrGpdtYmH0LMDkTZHs=\r\n=kO9q\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.7_1648722856941_0.30795095008092677","host":"s3://npm-registry-packages"}},"2.0.0-dev.8":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.8","_id":"@keep-network/random-beacon@2.0.0-dev.8","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"47264fdb720f79b0e31c0c4c4f2d60e4bcbf0cab","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.8.tgz","fileCount":106,"integrity":"sha512-5AEkPxrXsfofFN0kRHj6vTamMRWm1EX8Lmle8bqYRsF9AUeaN2nJJl7tAgnLC/a+ue6NZ8L5HJkk2mcnjodCbg==","signatures":[{"sig":"MEQCIEfuAXjrg47fwmYFD/3/4KMM6sVR3L/+G7kMZb5omvp+AiA/j3nTZbYzC8n3hKGq50WqW7Ty8q+g4VgN9NciLD1+Rw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14910975,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRY1gACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrs3Q/+Kw16EVjHFTioVjm8xWNo5PYCd91YDbPS99cby/CbxFiZ0wJj\r\nx+NtuKUearuy+l1sAGx1kZpwpYjW5mUxei5S/3ZfwUrhs4DLzZnKE5WhFUf2\r\nAV8+7fssWWegoKiOwWd3Apkth5p8xCzl4KaD/HMMMA1Ta8ND2ZumsZqg5Dzi\r\nyOs3lp8KcIHM5N7qe9JkJmQw0zqPyrsN0RxDoPBRS9uoxp2qnH/jkLnrIw0A\r\nQ/3CaarTjcOZn5guJUWYnjxCCUO+aJrXV1aG/lrhoSJ0FGyxKlDPuhWnS4Jt\r\na8NDxkXXkDKm8jSrflDE+naq3roaPDGK5g3X/PpxfEyUDKQPodnNw8zL6IUM\r\nCkyhsHH/i4YeVoEnQKEqTydCsw8ashRJornPgvJSE8p/8BGqlRTjgDOlqFRc\r\nd9AtSEJswGDtPM3/hbkiCEPSXFwwVCEvyxfZPtmezxfO7bycsoROu9JyNbpB\r\nQ7Ggv2qkxIpG23HbUjFlMkfn52vRfpfc32B4s4K5I75nf50699HbW9mfdVQ4\r\nLZ+WWjpJkmNWP43cmDZkbqyF/8YybJ5nviPZ/Lxxcdw9vitRfbwctFBsFw/k\r\n7A2X8cElsYaLdyX+Xdix6qeoT1fpg2IA189HLnTTSW1NnWxe1HZZ5t63Ve8j\r\nl9pJieLxeeXP6/S93cK+m2GAsYhSaWuoaWA=\r\n=+idq\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.8_1648725344549_0.5128454935838949","host":"s3://npm-registry-packages"}},"2.0.0-dev.9":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.9","_id":"@keep-network/random-beacon@2.0.0-dev.9","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"09815afa500f7f57d9b990c6de21fbdfacc37e24","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.9.tgz","fileCount":106,"integrity":"sha512-qXtMGfkKvYvVmsCcNyyrBzbT28GZmZ0ucsDdkOFLB2vS+03msO7jWnJsrnYUWCvVvom6ewBVJM1njo8I+b0LrA==","signatures":[{"sig":"MEUCIQCMoUMD8CxAQFRrAEC9XFo0fT8uXH2WDcQTLanFCtmKgAIgFRVEqI3srbzr4odo0SCjkA7Zmsa9chg6xV3fDUlfSpU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14984940,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRt44ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHjQ//QdoCd81Sk/lsL++Y1pLULQ8TgL18jzVudcCIeEvGgCrdA0AJ\r\nAE4AHjdO9Bw/WAFQyDvNaTMZE3BzgPIeon1n0M1lhC/2kG9EqsIjg4zer4P4\r\nCeYZiZyWK5Jk1biBOSOLAFyAJULs7Ih90+BdqJZ+fp2CAv20UTeh5vW98ziX\r\ngazDD9hW7UxvjSNlA8ry2k87zwa2RvMaGVbFzq4hXMoOSxwWKAlUl/ZoY85o\r\nbmnnMJy3qlHTbZCkcFt2Lb6Jj9OZF+qY7eiohGqRmqwvT6y3WZnFyKqd7p8t\r\nD0XsDPem9dX/8rT3lnM9eziL3SlwnY6yPRx1Ki1OZEdVJL7H6+bI636L3qo4\r\nNuBhtOnf17j+e2hd/VqWa884XE0hoFUUOG9iiy6in6OlngWcWsSITnnR4S82\r\n+AnA19rl5CCe0x1/t1uoTmbeoflP9La2pHB/e0NX31r6HFPu2BHpiKgEupcZ\r\ncQSUFaSTiK5TG3vC23QmbeQlQpPjj4Ten7kq4y+5hpTtqbeFpUZIpAW5uNrJ\r\n4jC7m6e18Whymnp8S9+Tu8VxN2D/2KD2tmn3L27Br9x6qslWKzYIu8yFJyUV\r\n88uwTmhMrPkTHBPY5yboozq2ePiFtE+vrUdVkzXjCOx+i7aY+QRiccWTb3Ng\r\n5UXVei6y6sut6303GaqmtCw203VvK17QBN8=\r\n=CXQA\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed, governable frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded. The length of the challenge period\nand slashing amount are governable parameters.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific governable number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain, governable, period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain, governable period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit being a governable parameter.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event. Callback gas limit is a governable value.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe governable slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The value of the fee is a governable\nparameter. The entire fee is deposited in the DKG rewards pool that is used to\nreimburse for different actions related to DKG.\n\nThere is a fixed, governable reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed, governable reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the governable eligibility period, `(new_entry % group_size) + 1` and\nso on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.9_1648811576660_0.4212081338040936","host":"s3://npm-registry-packages"}},"2.0.0-dev.10":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.10","_id":"@keep-network/random-beacon@2.0.0-dev.10","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2320d3bd3d286a041050394aa6971effb3f173c9","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.10.tgz","fileCount":106,"integrity":"sha512-oO9xfsPd/c34xVKU+C3wq2KK7O5YQVSBaVNfNYdDlNZJ1h+aYxY9aQclJnW2HaDaN0dOeLiwL6y72EDhsKB7GQ==","signatures":[{"sig":"MEUCIQC0JYnvPj+T5iUGKoSkui+59ivEpTjT6mSg6i6qLkUPIwIgJPgXoGxyXyz+y0MNO7P+6IKcED1E6ni6e4fQ4d1zbbc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14984941,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRuc3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqeRRAAoHCj+O/GHorZOvMFHyuSP9fcFpyPjlggKq4V2aW4w74QgP2f\r\nKj1dfXsLLc6EqgKPBV62FlH40UzKZpM9wtgbN7j15GwrVWLD9vQAD7jfJ2NH\r\n6xJs/uvSwkyDVIT4Qa4yV1lhKnVpvRlrWjsNZQqvgZan2sFkzKt9QeEh6u6F\r\nfDwmiAmiy2aoy/l1feQfIjEkidrD2JoBFrUrAataA77614oxFQbUmVnRSAMX\r\n27C50VFziqnbdZZTgbZm4s0MkdvoqC7ApS36w9UwmGOQ36Im+Kl6H3kQHbgc\r\n26snloGgrmc0laBAgOYoQSYlAKLHoUulzr+j48O4svlhIIz5NrLBqsM539bv\r\nTT4kxl/eGHiJZ6+Oz/L8CoHRMbuCbIg8ABcjGIkTToMImr2oWmTMWjSt7IRQ\r\nSYdu5Sfm5d2gfgV/YGbxy+AIFHizLw9AmPZ4hLa0gCWsFTpczllQOFfkEsiG\r\ni82odJvbSCGN3ERic3x+KeDKfBH/HmRZN9IY3UihKjfnlf/uEL0T5tfWQfkw\r\nlwIkCzYa0pl2zPo9xouAXU6yAezypHC3/gqnnNeaMil9922Ty6HVDFoObOuu\r\nQ0aEZ1ubtJ9j1/vHGtrPMv5+zwS/+MmVoJ2KK50NE0xa0wI/JMISLs8khdY8\r\naJqr62p7v9LG8EsIF4xHocteK3R13aq8vK0=\r\n=cOxT\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"1.2.0-dev.24"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.9.1","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.1.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.2","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.10_1648813879523_0.8485142283093601","host":"s3://npm-registry-packages"}},"2.0.0-dev.11":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.11","_id":"@keep-network/random-beacon@2.0.0-dev.11","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"28b5e2a4ed8d3d0d0a498a206bc18f73b50fde1b","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.11.tgz","fileCount":124,"integrity":"sha512-TOF5slqqbzAenbT0U2llt3THbao8HaChDjFD3OISq9wtxvMIM6Fj74ZTVptTdGvE8S9g73W40E8bHHnfqNSQUQ==","signatures":[{"sig":"MEUCIQCWqd6tN/0kD0Z5ByPM3GVMZyuPX+DzxWXWHew5yH3IJAIgdRpycS2C6E8EwHI4aT8jKu09mSG9yR/2WEyLySE8ttg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19145199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTZmCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrxLRAAhRrDULyjYHWUgx8w1txJ4lBaRVHor5vBD/KrsiWeNpmoYDi+\r\njPVXMYVwBGa80jAYyb/T/JgTY9IYgs8cfJWW710NFGh9V43TXdKo9GwV/1GY\r\nWyDjL3PDj2apAQtWEiFBW9X9q712wNaB2pJNaIG6sChYB0beQYRd8QwYoaw5\r\ntUw8SsHEe7WFVrUQTXKcb2pNiU0JGnjE18tLSqpx4Icf6QY0b81ihvq95qV0\r\ny1Q3cfH9dAAPFQiwORuD4ikraWsEVzNqIJh36wF1zgSdV+aI28RWMxfda3+W\r\nx6x+qoyOvP6rp475utI9WvPdD+398ZcB1j0iDigWUGfU2Cbxqcc39RUxzSYF\r\n6rV6d+8MTFsjacV0ByzYrRblXTd/jl60AXrSufLNlSmec/+jUYFHz992IFBL\r\nM/MZWRcCAe1LG05dosSkz0HFk0A6x8dHbXY9AUHBVCC9PoFCKLjWHzOEUiQJ\r\n8138A1yK1jS5KkorCwKhQ1KFXJscDf08Q3NIS9IrB37x13MOgD/Gln63zlE5\r\npmLyfp7wAjb7FcuApn1KqJyCz1+3Ov/9I0O0HmYJHV9v++tvClbqZjNOp70h\r\nGQPD7e8Os/zQjt5sPa4RotpTQ5j6/Q78YhIWqecRcPmRa8rwiSsYYdxfUCqr\r\n25/T0AdMVdU7B5iZRUVDBZD6XFS0bi7txDI=\r\n=85su\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.11_1649252738577_0.8584083332310395","host":"s3://npm-registry-packages"}},"2.0.0-dev.12":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.12","_id":"@keep-network/random-beacon@2.0.0-dev.12","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"281316be1cb60bfa874f0a7e5c355c62b2a2f3f8","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.12.tgz","fileCount":123,"integrity":"sha512-4xpKIu0h1KzrOo8mVRBIPOvdVPQwJVvr/RTqU5Bq4b4MN9WVSwfosw47nc6TutnO6YqxlOtQfkuWQgwOjlEZbg==","signatures":[{"sig":"MEQCIEz9hBUd6qCH2Z0+eDu+HQf8g4BMBTWgsrdH8UgiSfkyAiBALj1S6mj1ovy4pfPBQwquN+0UJhb2eH1p6WZcq64GQw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19195888,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiUB7hACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoJkQ//YFJ6x4XkibYbH0gKy+y/UTjQp+V420Hva9fPnZSYirOFzKxx\r\n4GdJPkzU3NBehaKSoRq9ttv5VB26q/cgX39sWYDr98ksW/rYq+sFwFQaB278\r\n44oll141weCsgAPT2onlj26e78cIsqc7gODI6p/CyPGQ3JIeTocbxIhJ4aOe\r\nQ497IOvah3TH3MTw3yerKcvNSnc70cmU/T07OoXZMvLlVmUkB6ZpHekaOZRJ\r\njn4PJ0BkN1w+x+wBr4ayOimFe1vPWN5YcHuHtS00DBiO1iXqRn9gLT3+NVit\r\no0r9efj33alGcGW4VuycSbEvyPSlzLBl8MtqLycT7xu0J6yjlYz4PUlljMuX\r\nBWK7ufIttw42s8yFlD8rLLayg4/P4c3DiLs4y7Bsba4S0pbYiJ6f8XGFP7Or\r\nLU52v86zNFqciyJ1LAS526w09uW2ONlIfXNdpv6aFX+3sb863DhNSCuwIx99\r\nLXJpQxpsjMgPc8A7vrmsLPmsCzNLJ0zHJ/ehD4g4Qm4bziDq5g4Otsd7CBmp\r\nfiuTpKwz7hlYHzRnK/JYShv2w7I+wsEnv/RFqVCNG48HJmzxh12vMwFz8/kC\r\nKtHKcI1dNZh79Dlan0PsLBIgiugWmvP+qikfFfPHlU+r8eP9OoB8hOFNkbf6\r\n5si/M7vWJJ94xUWqQXjD+26zdeK5/rolRGQ=\r\n=ulR/\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.12_1649417952790_0.10263492978827471","host":"s3://npm-registry-packages"}},"2.0.0-dev.13":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.13","_id":"@keep-network/random-beacon@2.0.0-dev.13","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6ca0aeb424d757fde103f581787913023c47261c","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.13.tgz","fileCount":123,"integrity":"sha512-yWYWHtNM40rWkX2UQg0wzCVq3Z43RZAn0cQnvT+XRky9n4TtQbLc2ZtJu/vmWhDA+nHj5lvwEt2aZ5hgM4Pckw==","signatures":[{"sig":"MEQCIHA0iXVqcPn5BV5hxLr4H2cIbzYRvUkRPwrB+B2+uUDwAiA9lMsfVH5wftICNT5vjaj5ArbThxy6Go/T+BrvnmGh+w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19073566,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiVE+iACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrXbRAAoxA8GB0gynjPVh+X+s4o34z/ySNSsmFrDHI6aaJZ+f5v+wcU\r\nOV8ICJGl32LqxytL/P8w5mTWWfCQ2/5+05f8RnwMhoF2OMJ8npc11udD/1OM\r\nxNFMjjbMOZRrc/26TfdwHi4ZpJDc9UksAITzzhv2pI+n2uMkzpkoePC4aTjl\r\nEt0VI7QdOjbMVzklJZ98RWwxp4ntd1yW5b8WvD+rbOWwOed7/qur4zTmjoW5\r\n4RLVN3CcCv+lZ7xQ2hBh1+br9Z9IVe34xJ8s2W6hKA5mJ2CCa0lgJNYcwQl1\r\n+AQOO76olhKAoToBg7ijD0H9qtj9Vq2YxP+VIA98GmnOGL+aHKT62vmPmQmr\r\nLCPMI5+/mQJFjTc1ngjObdwD/ad4p+P22TWX9f60dmsnGk2rv4WltvH6AAT5\r\nCw0L9nbs3xELwcOHS2KdeXXXziYPpg4YrRVzhy5xglrex9gabJyXwsVFudeL\r\niTCA8MYhDxcde27pQASn5Fi5Ue+95SKOstppirtU+EdV0Eyy7NCdCRMtZtcv\r\n5sqDmKKkdxHVUl75Kh3DNFS/rRbeXVRO7fEGdjHIibiFDXrFGNghIe96kHAT\r\nVvBAqeK5Ksi24vroch5CQmCW8QuSMk2XHILOMyXHooqhFLdr3cG3cAN9MVWv\r\netP1vwZzVYIc9N2yhREGLdCwhQ9C+MrnuwY=\r\n=3lg0\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.13_1649692578488_0.3968194971662451","host":"s3://npm-registry-packages"}},"2.0.0-dev.14":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.14","_id":"@keep-network/random-beacon@2.0.0-dev.14","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"73eee75d64a3dc181e3051ad12f33f1f0dcc2439","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.14.tgz","fileCount":123,"integrity":"sha512-Su6c4QORvVU3LmFmsWaiZDKgPi5kKKecnwPC2lVFPUk6Yru8yRaL8EIekyFGI8aR5QEvCLeMPFiFaVMC9UBrKw==","signatures":[{"sig":"MEUCICaLJCY31XZNiNfXNpja9ry4C1weiebN4Ju0aWAB8hVcAiEA6jYCg4jwOU8cPdTokWPwVRKENaOJXgnaxuod2mtLbLI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19077315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYX75ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZJQ//fZYO5vX0fGq496avrgHHd9bY7zOkbO+2URe0PEFP7K/sqdke\r\nh2TDBmj2OvN3HTQo8ARmL/4Z4XAwfhEAjk1jcRk6M6fRa56EWTq739bZs8BA\r\n38P7HSkLB/hg/8tFGAMjoC7MoXzokUsilZBFkq/aK6WNqSnEZLtYDN8c8BwL\r\nGVlIW7NLB0pi5tcRro39jNNRvboU1PZQMSJaBK9qMRphkwW+AsZ7S6lckqh3\r\nUsxpzGlrwX53Eq/WRM5HCl4SqkLc5lpsGRbMnMvMqVVP4I43hxSBa4nICn3b\r\nEuZr4NzWbbY8hZsS7FKl8sEUGtqdEBe/zEZZ6Rb5uDl0aMO/SnUCCxekA+Gp\r\nT15GtGP7Nkr50eguGb2WcMY0XizIGtG1RvuDZxXgz9x7ah00aAPinHLKAfuK\r\nA10nynOWLqL3EWZv/0wd7YQ5L7s5PESwJdSKPRxQsYdoneW763keIxGEoLxz\r\nKnZdMBcX0hs9rGLWE3loODs8/VnMb4aaCs3COPN5EX4qVXuxlud0wMbhnDqY\r\nXbN7jNbj8vDP/VALorX/psvlYMXzCRosV1YoKlZC7xDi+Zz+4IUraSlpNhUS\r\nXafbnWi6oiSx8W/FopLxPZSX7dTwlHXl8QiHir3PtbNh+ovPgv49ujt1FsNz\r\n+1UMrlUkqEAMj5GSYte7q0dZaZpyNqppJhw=\r\n=5QYZ\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.14_1650556665376_0.664922099470914","host":"s3://npm-registry-packages"}},"2.0.0-dev.15":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.15","_id":"@keep-network/random-beacon@2.0.0-dev.15","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"eb026bc8ce80ba1800c0e24d31b3d093416dcffc","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.15.tgz","fileCount":123,"integrity":"sha512-QDF4PBv2//pguqjEvmMVsjJnbuasqMxiNRKreOMCK+MPfEJeY2x7x8SByplxK4qxc5aQVUWSmLmIGHJAw25UqA==","signatures":[{"sig":"MEQCIGPk42nP9snJho+jwHzRosLr7NRxm7ecuAEDylcgRzd8AiAUvEaf3hU6+8GIK2yslZRiS5uUcBQvIUpaFNnN0eadtQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19076529,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYqoxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqTEhAAowFPUfGMMhkGnQnBR4xTrIW+v+LFkjgQ19hgqe8JEGXK7x3S\r\nCMNtXDcePafoJIKhYnXTRGA8ubu2CMZlxqKV7vFupCGm3pO1JzJdPEszanFe\r\nSOUK2lKhraLcqjo4mOvSFBZ7rdULNpCXn7c6OghIP2vL67gGOCIkAhmuVPPC\r\n6M08ArwYNbh1TFmQa7Q19aCjD0o8fPSEzNdFdkeDQzSr33GNtrDR8HFDI7kl\r\nr3awhkSZ/6pg1LV3J1SQI4vsAuptLR1J4Dz9HGqXvlOR5qvbfpAzjYv2TdVr\r\nqgpoigTc3K4rO841uRJWIdCKa3O/UUvp2CAEa86M5XRx0ENxm9VFBS3QJlFJ\r\nM/FnfnbkIvqLYvWVG83Wc1J9tdyEbW59z+nd2WTarnkjug8jdt/JTpMgs7jo\r\nx4kMj0lZ8cWWh4msP5d4F5tdYmoIHF/aD+SCY7as8NkVaCKVeFxJeIMpqFR6\r\nY8GNJFG2hMbsQLP4NVlvyg1s8wip6olA+0RAKajvq6B49keMKPjb2MrS6J8d\r\nTx+7BPlzlgbzFqp281d05Czln6IkkCBL6d0xBMQz+jYLVJDbhbAo/lfqrpix\r\nf5wS1Viz2+AipAf76hh9hu5O+etIBx14XxukzEcGGUv7UU4/Vyn+ruxvhABL\r\nNJrBG0uCIYGE0aFCLac7eyo6V3qzVVCizYM=\r\n=AJ1p\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.15_1650633265396_0.1856126346447493","host":"s3://npm-registry-packages"}},"2.0.0-dev.16":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.16","_id":"@keep-network/random-beacon@2.0.0-dev.16","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ccc010f8156ff8b82c3cef026fe78c1ae4f65df4","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.16.tgz","fileCount":123,"integrity":"sha512-OrTyHuPRXpZwB+pfwrYV9pMqjUlsUJ58gv7lwCXYBZh2kI7IHjXG5mOh2M/vXoz1KhgJIq1oQokVEAGlT4W4fA==","signatures":[{"sig":"MEUCIQCAjEEPPskZd1EwS8vpgPLqaqtPEjgWF9PVDx857e/2AQIgTpzCm3qCMKviabBraussgDM8REjPsFS+qlsL660J/7c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19115651,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYrRJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq4pw//SedFklOzvNIAoAFRTaaDMEtM9EzFYb2DcgNCJh0pRPcDs0O4\r\neQiKum9TcaBzlk38n8HmnaqM3BQkS81JUMPdMLv+WH5vDYNz0rd3IUiQ/VbQ\r\nt7HylHyId5qh+DD5CS7x6traOI3YcImr80fIAHxZrK9Wlp/8ymwvVTHQiXaa\r\nc8514aWjF1SfVmjIKZMI4ZwXbt5VGq3d88ZQx2C0FlhQwEofGap9cif3DYQ3\r\nakCZCZpC4xhDaI3bLy4cqQJLGbK44f+L3vmWNNaas5biPJIb4zaN/vIx4WYU\r\nLkp15f4Z74sGFSAas1hboAaYa+Xg1TItYBaVO5p33vTpYCOwYVoAIJ+UTeIX\r\nmQirnyRKNefYcDhLV1EulFDP5m6aRswsrMpH/CHvSFO6gX+WslXSTO9oQw4L\r\nD1qdJcF9p3zHZpp03cAPoYOdEETp3nxvTj/68m8G3GnrjvNCak2mrrpyDFwK\r\nbX20kx+pBTTU3WV9lk6xG1TIB1o6AR87lM7DNyW8njynHjRqEXROGq2HTDUN\r\n5TdVIZEdM0YNRMOolGIBeYkBR67YIXbjH5V9KYJ8q6wrfj+UAyxYBmkyzY+2\r\ndJ7hv1TsSjH5bWWsBezRK4vvUPIyhGN31uA//fwPZduZWK1EKsnRIWfUzNuG\r\nHK5N7X/edzDFD292PmcCcukvDSJT84i9HxM=\r\n=mH2U\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.16_1650635849583_0.12318761656436572","host":"s3://npm-registry-packages"}},"2.0.0-dev.17":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.17","_id":"@keep-network/random-beacon@2.0.0-dev.17","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f4bb7ae8533683b341393c601176dc74b0dba997","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.17.tgz","fileCount":123,"integrity":"sha512-r9tksVLajHWzUE/kxOpo5LMCxQSvPRH4ahmnFc8DbJ29GHXFw1I97jK1a3hhKDr14k85zhFw8bxHkXy2z6XmRw==","signatures":[{"sig":"MEUCIGArFIQkQApuvdj9waRLVjaF5F6R76GMUKgBHJk2j/2BAiEAkmr4x2vMLZnM9vPiaAEgqJIXZiUp3bUplWRp7lCIds4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19113206,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYrY4ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqcfBAAmyJpIY9d/0wqpz2qtPtuQbDh25Sy8JR9BYzcqHayTdN8RLGX\r\nm+HqIPGwNxwn2rmqFz1ryVtix48Xdz692pFznM4rfk1joe8pnsXlWX83+iWC\r\nGX5UpYSgrinsl/kA3UtFw47tSdwgnbFFm6f1XHt+6OFqS0da+do7Z+MuG3Ng\r\nhe4wG5oj2GJMz5O9mKwpj46HFMpYO0jLUBsXCcS7WLXRWmdQutT/KyO7l17v\r\noY3XBWZTJB/NjFMyiyssF9DAgW1obUK1Cyo/oVZAY7Ov+ZeOfc11ix4e8T90\r\ngShP3ZLEzPEL1XHQboFsyBFz/A0rtPIDdIF+nt9Y30/X9P+scX/tGFfYrctf\r\nr4O7HTTe71eaOwtSvTgXvnKTikr7p7CfQx64vmQzbRUArZUeB7r93tQlzsO3\r\nrPaKelxhqLmyV19cCcGvUnmySimU6mVBFa2atyTiccoS3ZJI3WeXLlxUmMHJ\r\nKWIuLo5OiuzKmptkM83dkWJ5AAN78HVLqT0Ab62zdHKbHeDIyKnLGVg6iDvd\r\nM+1/LXbZXAcU0HFxfKMbx1G6aSwWoeuXF7mYCbVDuDgn2a34kb0s8bcxh2d0\r\n6RuUtUVW7FS+AR8rVxg0bomjCdPPaNAZhqHNi12kslDgLhr9oX3v+872XUgB\r\nifhzKWFx7Uv/pmyEVD/4re+ttlMbG/B03pc=\r\n=1VED\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.17_1650636344308_0.9430874720951856","host":"s3://npm-registry-packages"}},"2.0.0-dev.18":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.18","_id":"@keep-network/random-beacon@2.0.0-dev.18","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"445a657a20f957f933bfe04b54d7183582867a11","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.18.tgz","fileCount":123,"integrity":"sha512-c7qhP3HCRR42GXtL1071WX0z0dQ4JDfCseko4NfX6kPxuFZWB57gGLIFYu7NQLjkzXevhEar6wM27zQthQrihw==","signatures":[{"sig":"MEUCIHsdU9Z2Mm9RnII1gY5av+wmX7WEry1sHdoI6fd/J5QiAiEA5k8ZerEx5EcSVrQguFuEamWiNgTsQkRkxqLhmFvGpZ4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19113206,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYvOFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoh6Q/+NrY7D8y2LhBFsZRhE6zu735zAkRlsnhAXPWnS2ogtwYfQmCc\r\nD2X0pwqgCOxDi5i3TW8IglxZCg2Bt+gJRAvBsSY0YqOd/wppOcgVvhA7dlNf\r\nM0KFtVDRm9ZYvOoK283+yBJ7TnctZJvEzgfrpBaqPdC2+EbdaPGlt+bG/gBi\r\nDfAlC4bgVt0mX90TYeZOSwhw/Vl37snu9VmieEV89PVfAtHP73O8s96n3AAx\r\nmYhFKLlAazizM1l6qXOLb/LcLplFHZNzxwLG1dawcGQGHxox6q/r7dftLpdh\r\nwAS8zKukahU0Nulk2iDR8+uJClLv8yGqZrl0md+c5gm5xNkR4jCaPpjmvB4Q\r\n0eulkY5Ae+RbBNsidAKCpBNvbo9qTW/VdGsIcnl3Zb3IH+kJQR1fonW7JdeJ\r\nlPWoYUJBzdny6SHQyIxD93fYHseA0V3xc30ouk25JBUsj0psrxpK2+WO62QV\r\nBHfIgVI7Ijy4sJJ3JZZR6hVUoLipWdoR7xjs7KsPTO5kDUAIZoQPL80vs63z\r\nGl7zWboWUq1skYIALv7+ND2JASk+eWu1ww5Dm9aPN0yNfE9zGZm/VLV38e0q\r\nAMoHLIZpXKhkejjMkGJit/VZjARi6RmW99zh2XwxsZydUxW635ZM4UDTE8uo\r\nHqJLHKDsN5ijLzQEaMIDnvnLOFpJmTaXk24=\r\n=mwt1\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The entire fee is deposited in the DKG\nrewards pool that is used to reimburse for different actions related to DKG.\n\nThere is a fixed reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the eligibility period, `(new_entry % group_size) + 1` and so on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n4+s|Random Beacon\n\n|_owner\n|Address of the RandomBeacon contract owner.\n|Yes\nd|_deployer's address_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n|groupCreationFrequency\n|The frequency of new group creation.\n|Yes\n|`5`\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG, misbehaved during the DKG result\nsubmission or were voted by the group as notoriously failing heartbeats.\n|Yes\n|`2 weeks`\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks 15s block time_\n\n4+s|Governance\n\n|governanceDelay\n|Time in blocks after which initiated change of governable parameters can be\nfinalized.\n|Yes\n|`0`\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.18_1650652037101_0.0446432592857442","host":"s3://npm-registry-packages"}},"2.0.0-dev.19":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.19","_id":"@keep-network/random-beacon@2.0.0-dev.19","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c30329d323a14cad1a6575d19a4f40e35c4e8dd4","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.19.tgz","fileCount":123,"integrity":"sha512-ewuoFLCvCfVvKo8ryplAyNsf4Mv0DDk2vw4W00z3nFhrRRm3t7Acwk2EHyLefLV5IMyG7Wlpha61z/dbDzuvAw==","signatures":[{"sig":"MEUCIQD6wvdvHXcKlVgxHcOSqmNUSHgUtCyvGhkZl7vv1cZ18AIgA2wrjvZtoFB68oFKvInAGoyvYoq4S627Y8wf412y1ng=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19008363,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZEGMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoV8w/7BS9BlHz/fIQ7IOjyezjVfk1N7IxLjT6l0i0bBfhICmh32V3R\r\nDUR9zoBS2QLEP06O6szpJe1YTLWC/BSwEx3NS+VjhqXV5BrcrUmjhidc2f8O\r\ntw7qmo9CblMqPex0qxPAjzMGhLqD+rPLD3K4NeZCPWZ1RLRBU+FceARmymsW\r\npnP70wsiTud5Ne9W2Yw+YyogElqMZNa5RfZLH/zBrIRchAVB7HQc4oM84FxJ\r\nj+jTXq/gko+HchJTt9lcAeWxKDzWkx4lGo34X4WRRF1m/I6Q7kHVjk7qaGcj\r\nLSrwhbWLW0PgqlkMcnkPAd3iQl6P0qL8NyR2o5nKQPcbH1umtfefOIs7RDxz\r\n1d5eGoSPMEtOLV6z01qaipzR10nei6ed4iKnh5rYvMibfbdG7cL71cxr38Xn\r\nIXjOvg0IWXYxeRlnrF78Ci+ue7J+h6Q3vgyBOugTBKUvmVb7Q2CUggMrEYQQ\r\n9qKjIztySaK8Gx/VzRf0I6eFSOmMToQVYIZd51v5r/0ECRSVG0Mtgzgonw+A\r\nPx/VGdvFyZ7a+6cidAROku4bBhZj8+xfQ/CUGnUSwRWnU5tRMhjOwyLPXpvS\r\nNzx3Nl5gO/97bszTikKPWw8F7rsHz3fcYBgxFMmpeoMnUvBanAmhGjENoc0P\r\nXQcLd2ST9zoHn4g4Xfr1eDkaIoT3O2+fEw8=\r\n=s/GW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection optimistically calling\n`RandomBeacon.selectGroup(seed)` view function for free. Seed is available in\n`DkgStarted` event emitted when the group creation starts. After determining\ngroup members, clients should perform off-chain distributed key generation (DKG).\n <<dkg-submit-eligibility,Eligible group member>> submits the result to the chain\n calling `RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\n Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets slashed and the\nmalicious result is immediately discarded.\n\nOnce the challenge period passes, and no challenges are reported,\nthe DKG result submitter should unlock the sortition pool and mark the DKG result as\naccepted calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)` to receive a\nreward. In case the submitter does not call the approve function within a\nspecific number of blocks, anyone can do that and receive the\nsubmitter's reward as described in <<fees-and-rewards,Fees and Rewards>> section.\n\nThere is a timeout before which a DKG result should be submitted. The timeout\nequals the group size multiplied by the number of blocks for a member to become\neligible to submit a DKG result. The timer starts at the moment when the first\nmember becomes eligible.\n\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out calling `RandomBeacon.notifyDkgTimeout()` and receive a reward, as\ndescribed in <<fees-and-rewards,Fees and Rewards>> section. DKG timeout includes\nthe situation when no new relay entry was produced and sortition could not be\nperformed.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for rewards for a certain period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAnyone can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter. The requester needs to\napprove enough tokens for a fee, as described in\n<<fees-and-rewards,Fees and Rewards>> section.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the order when submitting relay entry\nto minimize and distribute costs evenly, as described in\n<<fees-and-rewards,Fees and Rewards>> section but no ordering is enforced\non-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)`\nfunction.\n\n=== Callbacks\n\nRandom Beacon supports simple, low gas budget callbacks from a relay entry\nsubmit a transaction with a gas limit.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 50k gas\nwhich is enough to `SSTORE` new relay entry, block height in which the entry was\nsubmitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nThe soft timeout is the group size multiplied by the number of blocks for a\nmember to become eligible to submit a relay entry. Eligibility is not enforced\non-chain but off-chain clients are expected to agree and follow it.\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe time for a single group member to become eligible to submit a result and the\nhard relay entry timeout are governable parameters. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a notifier\nreward. The group which failed to submit a relay entry is terminated, group\nmembers are slashed, and if there are still active groups in the beacon, another\ngroup is selected and tasked with producing relay entry for the given relay\nrequest. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes for all group members to become\neligible to submit the result. Note that unlike in the case of relay entry, \n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction enforces the eligibility of submitters on-chain. When DKG timeout is\nhit, anyone can call `RandomBeacon.notifyDkgTimeout()` function and receive the\nnotifier's reward. The function unlocks the sortition pool and clears up DKG\ndata but no slashing for DKG timeout is executed and no one is losing any\nrewards.\n\n[[fees-and-rewards]]\n=== Fees and Rewards\n\nRelay requester should provide a fee in T. The entire fee is deposited in the DKG\nrewards pool that is used to reimburse for different actions related to DKG.\n\nThere is a fixed reward for submitting and approving a DKG result\npaid from the DKG rewards pool. The reward is paid\nto the DKG result submitter in the transaction approving the DKG result. If the\nDKG result submitter failed to approve the result after the challenge period,\nanyone can do that and receive the submitter's reward.\n\nThe logic triggering new group selection is embedded in relay request\ntransaction and is as cheap as possible, so no additional reward is paid for\ntriggering DKG.\n\nIn case the DKG result has not been submitted on time, anyone can unlock the\npool and receive a fixed reward for reporting DKG timeout. The\nreward is paid from the DKG reward pool. \n\n[[dkg-submit-eligibility]]\nThe order in which operators are supposed to submit a DKG result is not enforced\non-chain. The first member eligible to submit the DKG result is a member with\nindex `keccak256(new_group_pubkey) % group_size`. Members with subsequent indices\nare becoming eligible one after another, during the result submission period.\n\n[NOTE]\nFor example, if `hash(new_group_pubkey) % group_size = 62`, `group_size = 64`,\ngroup members are becoming eligible in the following order:\n`62, 63, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 61`. \n\nThe transaction submitting relay entry is not reimbursable and implementation\nensures the gas cost of this transaction is as low as possible, below 200k gas\nwhen no callback is executed.\n\nEveryone is eligible to submit relay entry at any time but off-chain clients are\nexpected to agree and follow the following order to minimize the gas cost and\ndistribute costs: the first group member eligible to submit the result is\n`new_entry % group_size`; then, if the selected member does not provide an entry\nwithin the eligibility period, `(new_entry % group_size) + 1` and so on.\n\nIf some group members are notoriously ignoring their duty, the group can vote on\n<<inactivity,inactivity>> notification for these operators.\n\nT rewards will be distributed continuously to all operators registered in the beacon\nsortition pool, excluding operators who were marked as ineligible for rewards\ndue to failing the heartbeat.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup members are alive and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nnth blocks and first making sure the information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`, that is, the signed information can\nnot become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree upon members who are permanently inactive and issue an\noperator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool rewards for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim other than the submitter receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They may mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentry.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n4+s|Random Beacon\n\n|_owner\n|Address of the RandomBeacon contract owner.\n|Yes\nd|_deployer's address_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n|groupCreationFrequency\n|The frequency of new group creation.\n|Yes\n|`5`\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG, misbehaved during the DKG result\nsubmission or were voted by the group as notoriously failing heartbeats.\n|Yes\n|`2 weeks`\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks 15s block time_\n\n4+s|Governance\n\n|governanceDelay\n|Time in blocks after which initiated change of governable parameters can be\nfinalized.\n|Yes\n|`0`\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.19_1650737547750_0.2813784964719759","host":"s3://npm-registry-packages"}},"2.0.0-dev.20":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.20","_id":"@keep-network/random-beacon@2.0.0-dev.20","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"79a852a2695fcd34d9aabe7223a23d7ac5cff6c4","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.20.tgz","fileCount":128,"integrity":"sha512-vpdo+bEy37tsnIhX5vlpq/GtsDvjLPCKKnVpdWHnLBEt/wcR34YHc43O2szGYFURKAzeXgjyJ9wiIE84j4zbjg==","signatures":[{"sig":"MEYCIQComIWuaA16RrJ+k99mUTSL7bWH0Q9OI2UTkEMkBBzPbAIhAIa4tVLwiQoFmji208vWSIOHF2MefzYDc1rXBmjec6WV","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19088845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZnYxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzvQ/5ABdaFBmM/27a/GHrRo/FxHLxB0QHWGeuLXYPeE3Gu6zCGb5k\r\nnAl2k9/6sE1wq3oIo1+flPhgLSD3/b7fy2jzhee61g2gaYdfxgomlUsoffHe\r\nGdSLo7fNJtnTJf7znZxHNE7bgsIh2lm/pH0gr9k+zW7SsDB7kz4p+fcwzYM1\r\nb3BT9rvvWI4Kmbc+PF0eQzzZOcwc6xsjLgYB0mLV8BwX+3ETY6FA54QhBibu\r\nuw22MFWZxSjhwFYavFwbl0Td3q8SXDlX4ALqXQlY0RXX5Ft5/eGUl1Ax/yQz\r\nYjBKegnqDjl+W92RtEns+j7xKXPJE2IYE343Gtxf5IlAqPMyMg02vzSsszD/\r\nyp3vpZ4DMYXSp53IcMMGrnUuiD+4cQWj9Qo1bbWwsrwnNyvjQkTO339zMmNq\r\nFOceFfjuuUcwNbhYkRHKUXoENiHOBGiqmiq456dC0uVh82awzjuYzO1bsDis\r\nmtjH2mAjkpnBKpVW6to8pKaSIIfZ8Tv+MFqDpyBKCXcuvR7uBpgYBHw1OCms\r\nwhwXDD0WoNr0X65pyf3y6Q93ZpjXwWDlNTpsJVuABetwxnC6CdtnqBVqV/Vj\r\ncsOxwrZVWeebdn0wiW23KSKH+F8pNynVPWyW7763oZ/WWSUeY3pHv78kkxiK\r\n6hsNK8y8eGLnIuKKtbYO5Z3OMREEbpFSVGc=\r\n=QLKx\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.20_1650882097285_0.20294927157181109","host":"s3://npm-registry-packages"}},"2.0.0-dev.21":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.21","_id":"@keep-network/random-beacon@2.0.0-dev.21","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8a19c868cc752a934489b86eddbabf8781b55b8a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.21.tgz","fileCount":128,"integrity":"sha512-dAoDCRXu6SokE2zC5t1nkIQ88gLTATIYFw50QfsW90ESClwr/ACnrFeFJVl0sCdXm1yg796YJ/u9NvckgAcvTQ==","signatures":[{"sig":"MEUCIGv0ulkHaYA8ziNtBJAaME3TciQNKbCHPWyhvSTFiv4dAiEAqEA9qVOUY0bfBJ1yBDSoUtaagLGt7GuFrtKvOEnVF6Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19089774,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZpLHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmooUA/+JBvxmb9Ac21CQpKyyHZHOT7tpWnLRuDJa48+HSw4Lz62r//9\r\nwaUqWIf2d5+0Gs53u4NsV2DkRfrwWvcNntiNmqVWU/FbAq7A+G6CjOOPnwsN\r\n4a18LlnCp2pfCX6l7uo6bcM3GNPTp8lu79IA1ktxoLGFRqDDu/YuQwxQlR1b\r\nGcmCUeJ4+uroS3xaCig+488bdlIy3apH0EVxKwWYzftiiPlLDgMLbRidraXa\r\n1P2nbK4LQlLKLogQmwXGEr5jikZWhtH2IACmMa+nUSDXTeqF56NZTWvm0X1k\r\noTjIUPKqaH2S7be+hWWH8EYDYF4EiSPW3ax5aTx4H+omwzMj0WGu7q2Rn8kc\r\nkTV1v53FLgnYkHINddb6ZIlTg9p9p5n3eWYQWa8NxuvuTOPsEpQ5ngvZBiY0\r\nWc1bfFhNcW4eGqTwJ51N8tePT9X2A6RaX3B7OR8ZN1zlrV2XFxLI7eCC4gY3\r\nBRHC68HrUrtwSFui+E2UjmBXkztNcsq8XwAInB44ZJ0YjGkFlkBNFBiZZ5XW\r\no8ilh7GZS1fuYRHRMLiNcD4kXdQQx1mp6tu1Jj6KohxGYWJvMNfnXItVXWCz\r\nb1QMP/A3LyKRH80GKraEJ2fhhnrAnHto7m/mRAtHFuROnAQYARLGWpaAu5qV\r\nkEh9ZApnwYSM/Cvy1dcmegUpTbsi3fCWb9E=\r\n=P/6k\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.21_1650889415692_0.4840060009653109","host":"s3://npm-registry-packages"}},"2.0.0-dev.22":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.22","_id":"@keep-network/random-beacon@2.0.0-dev.22","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e9cb6218764f71e13869f8290a84563424134645","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.22.tgz","fileCount":128,"integrity":"sha512-Ekn3yrLyZPkggaTkM0hE0tPpXa8pVmPBhO2HDov3ZiyM+S0H+qfpa9kli8nhY3MPFS981Z/8XMNC0H7/5nv70g==","signatures":[{"sig":"MEQCIHvN2pbsmY7rU/PSZQhqUx20/Hz5wxmIUFzySMbLAEKLAiAh5oh7q83y243MagRD9XjkAfbWBXr3ksJUSQnTbnCE/g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19148028,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZvZUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoyVxAAks3qhjC54yh90ANJqNOP55XH6eGSTGj3uwv+QYq0J4pBLok7\r\nHFkJUliO7sN3sjsqvnUdto06jm1Lh6+U46HdfceTX3lRfMZlIps0yga6q2U+\r\n+xBRJfR0SefJIcfF/sEkpvLJc3qyH4zxyiVB4TLafKx5DykEOtjiTxuys0/8\r\nobMx/QRq19mra+RN3HispsOqgHdqQKY6KJm6o+T/esjkP8gHqbPB6FwyAuxi\r\n8z5sOUmgbwb5w0MAi0KaIgf/nu1wihmbnTLJADILoT9RZrRfuL1BnIKeal+C\r\nhOWIe8sYx6xFEkgSdt7/8hx+IORhh5b65maehLfK/RmjUUcQ9QPVZJVbPacM\r\nQ0z+zyy1RyUi1/KVYBktHAc0HsHsz/KMGoa/GfnpHIWydVNrOLPmdm+GSAbt\r\nMRIDxW51gJ22xPnSyuUkdHWDLh5LBj7rTlFZSF8PYFRe5oS1S0IQdQ6LZoT8\r\nXW5ys04jucuEE9MOFO1flgGfYSDPLsdrdVt1nRj68iHS/vvZ+N7qkmljb7tJ\r\nVd1RzGM/1je1rVrirv4mmy9lxzJ0rb2lOTxjQfKvtGN4HeQSL+VRWxKsMSMs\r\nLWluYLbtZTOK35o3qEm5v4BhxC/C3tf2vKbPE3VdKD9RJpEfWXCl6hRv/TTx\r\nhOVeteIARuXb+7k1JpgfWKSzj9GNrrkJqIA=\r\n=93Go\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.22_1650914899932_0.05682205905918325","host":"s3://npm-registry-packages"}},"2.0.0-dev.23":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.23","_id":"@keep-network/random-beacon@2.0.0-dev.23","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8012c73bd3f9b4c560bd415a70777e0644f89224","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.23.tgz","fileCount":128,"integrity":"sha512-l5wbTb3nNvNsPwORgTkuXikNtuN7d+IyciVel/PxZt0scfvfwQlXD9oct0XtuBZudgCwDJWyM1/nW9a8QXX3zQ==","signatures":[{"sig":"MEQCIBgRIogl7APlT4mBqcKyNPWyDKdEK4lBlZzgiArZS6snAiAX7jh9MTCRNJc2SfXjQqMcnGJz8Wp/ZO8wA9eYfQ0fgQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19229486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZ7w9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpRoQ/8CX3xWJrs002byoi7eVLzQSVo8LPgBGkkNZApui/imnamVgpp\r\nKMZU95oOcWWUc4z2Iwem0wqD1ArOzTcWOEoPBqR+LXlMAldyjoKx7j3EZKTi\r\nd4jD1l4ybRheY4g9nsvx5QDPcT54p1X998znxH93Bj4L9hyIo9uB1eHyBLeU\r\ndzTxJmk7XFIE9XY6IVJXsK40F4F5hTtugi3I5qRF08ttXZ4y2LQSbAcaIinJ\r\nM9M3ffXgUsUFJ1hIuJrCEkxC2sxUg3tTzt6VLceDa+ArMFJTvmuodbeAi9o8\r\nHpjktsdnrF6mkWIg94Oc2UQoxJ0CWlRqRLe0Cn8ziCnPx4M+HKIvC3ramSaV\r\n2MIuZafhMtQC5xmp7z7+7JHEsY4pAQAeqrcZlzQmBE9DNcssUTlWBeTpMYxN\r\naWGWmsK4vEFH2Cywuiho/0grg50RghiFmgIFyk15e14KSK6HkzE+Ab1Db3YH\r\nhs2yxjxd673cwbJgvoIvJozsPBcy4EXGZWTOgwM6m6gFTVOVi5ioSSs2664/\r\ndW5Gxe2nRDvsYxlB41YDyjwHgv7WObpujRIfTvn4y4ldWXMPmKOVMIMGH0cJ\r\nXVu1NlUawRkcyhuDENBAKZGDIfIAJDWR+qUpxymW47MuQev/jXAcnnL+73d8\r\njozzeWqeev2L+Q3oojv5MTCgnGlvjyExI8A=\r\n=LfPh\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.23_1650965565395_0.7460524741460199","host":"s3://npm-registry-packages"}},"2.0.0-dev.24":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.24","_id":"@keep-network/random-beacon@2.0.0-dev.24","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f05204cda308e1afe3324b5473bd041224cfe42c","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.24.tgz","fileCount":128,"integrity":"sha512-qARatWcNTZH0DdRyHubcoOBjmo5HOX9ko33hLebf+tDANsJpyEdftMIOG3g6YiCe+cg+WaSmQqJhY8wNMuWn1A==","signatures":[{"sig":"MEUCIQCnS9RBpk2/QNmQFsmxYUWDKyyA9lOqqNnSMcWmKrmeJAIgYffQ2ejWeFGwiqvWf3BhSz0L0zZBYiMg5j/A/beFd94=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19250550,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiaAoAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqIZg/7BNJM9hvUHG4wN49GfNG9URPTyRyuLM7AEsqKVETLGgaWv75m\r\naWzNgmti+x/2yYoAfxkdwP0JJNdyld7UPU3yLH7hIVzFdkmcX42i7p+dVniI\r\nY+2CmgM8b+lzwQrcBDPDP02JY2IXAQLl+DeuXZkItjrJj6Y4ATNPjYMsJHT4\r\nn3Askx+KpoqgTid2zLyhJfn7y7Aze6t8pXg8iHSPZJ4Mn3QR88VrTXaukvAI\r\nZdG7tgoczny5355hmBDXb/XpIIZ7Ngfy3WilszhlNY0aAe49Lzq+yRBsvAc9\r\n33e2gDis/NFpNf8RZxh0RmD2gpHAYvf8Sc0wLgh14gSTEbV+DsT4/rP3y3s8\r\nHBUepViJPDq/Xv9VcvRYPGvsGWn1+bsndgCAQD6MU283JW/jp1map2rr8W4K\r\ntBdchTgfGhH17iZwczNI0JHDRTGYo3HbUDPXl5Z3WmtVt3oHemT78x+v0fF3\r\neCK0bCcuxF+Muqq+SYVpHpWmLlgPi0mnsEeL7nY13VFZForra3Ro7ARMyoiy\r\nsDdxxcOQrQBlZeAxEWq5IDbUkIGCcFVsgGD+uL8oW1z8Vtw3vnuoJiz79DfC\r\nyrWLYr91uqmnIM55V0tlkqNOarSGN1v6W2Kn6IazTmBZO1iMrgZPVlXrD+hG\r\ned1GA2DjmfwjU3aAlIg6GkxyYnVb69wRZLg=\r\n=ihpS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.24_1650985472315_0.7367294922781942","host":"s3://npm-registry-packages"}},"2.0.0-dev.25":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.25","_id":"@keep-network/random-beacon@2.0.0-dev.25","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"41009e649db29a2bcf5c4e50e2cb874ebb7ebcfa","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.25.tgz","fileCount":128,"integrity":"sha512-LdJ3ffFJdagycX47HYkDWRKCY0/uil8/DCj+grwjAcM2q9BYn9u9ofKY2fvXje/iK+ec6/2Cf6vbJ6BllIG0/A==","signatures":[{"sig":"MEQCIFoRX0mDKh7R6sQ8DF7jgsvfhuE/PTZZYs1ihK36+3HFAiAlXFYw2Nwv2DCejUnTnDpqQg/6tjf+qNS0CmlZAXJcOw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19243214,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiaNxYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqmlBAAh6KVHhU0cTIklWFijjErsNclNIgk3PrZXn2eX+T3/w+aLonT\r\nDc/UCpp+J4V5DKbivQ/xCMPL17c4rbR7PvXtgJuaL9kh4eUaVmeNI8PjLyfG\r\n6YlHyuszlYJLG6YRiTMkS0YN+dwEynL0vEY8EFzFC9wuBLZT/rleqHRLF2hr\r\np4ySs7WizpJqSRRE5mSOHphOYY1qFblQzss69180fzKYaBA3gArPkqyuX4OM\r\n1F+VTBRRmvaCbiIXnMF3ycIe1yfAxKaoMD1rn01dwC7yaitPcB32v8juArz0\r\nz/OvWqmNYZAJIp7Zq064TICZ4KePvA5ks8pK/NFxWmoEt/HieO/BNih1Ex8J\r\nfW8sT5CfoCYN5QewQwN8ClBftaKM9jB+4c6CTG27GGzItywAuiwzsesypXws\r\nWT47YOn2sS+Lc93lj+eVXoTznh3uRcOyTWX+CjW3410tVqnMjwAST/j+gl89\r\ntKBTejmrTO9GRJo2fsD3iOpNVV17AmpEKUFDUAh1wqf77F4ME6oDm6CGMDm4\r\n6N4I5Bb7PhhYgfyg3gj+LCCkYKiUeaoJZyuGWtY2bnEucaPLdmw5BB7Yy0hn\r\niZKz0fERmK9J0P18qK4Qlq7DyqIQXBnpE1b6DRpn922/XFmf8dhpvgvaydx4\r\nh/BO7Yb6sUAF+r4XaS5LnunHiswYce64lJI=\r\n=xhKq\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.25_1651039319983_0.8472561420184308","host":"s3://npm-registry-packages"}},"2.0.0-dev.26":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.26","_id":"@keep-network/random-beacon@2.0.0-dev.26","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"49c2292d6fe59bf158f44bca9b5f24c11c1bdfe7","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.26.tgz","fileCount":128,"integrity":"sha512-I72FSx5PEU9YGbN46ANq3N8QJdly+FEy8m9o1fnX4NY+xNA3YYy5JWMEk5BFNfM1KM1RVT4OPJ3ILnENjHZ4zw==","signatures":[{"sig":"MEUCICMDimbVw3M0QRUYEBGv6Hni6F2UEaSRuJ15tInUNXwjAiEA5l4jb30UsL+9WN8MY4Bx4oQeGW+rEqZXVK9ELSGV+XE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19243214,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiaSetACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrkQA/+NmizeeJVx5v/sbHikesusuzXYF2BJZX7+FaJGDDHOqVDnpzE\r\nKGDrjjAYJZpXCA9a4S5lhvru0DeUrg8ENLfyJ7YS2lGw8xjZ6Bnd2GFhxtiq\r\ntRla+GBirtnWR61ry6mRA7ZBuy24a2quDnddOlp2KmnGgjpDYtUNTp+9Zj29\r\n0PYFX32gyrKAhr7eEbaBddOdLqWsWM9GkpH0r0Wp2cVGwOfwxzrj28xo10KS\r\nFmZElA11Tf34ZDR/pVngcTtnrhJX5NAW+pp0lkyQEczoob95jWxhB4/93soz\r\n8y2njkVeYMGXNhpyt5YzEfLUgXwBxvQipsnWR0wZvmgjPMpJwiRqTtNfUEmH\r\nJwK3hoiJ3eDhR0oEz12Spm632nFw2R2R/V5O76AcEz0/jzrBi3OZGTFoDJ44\r\n3bzxfReBzseEqmmz8Y2MBBsvKhRd+L9oW9Ee+2ZcALh+cHuarDK6CALm31hr\r\nORfDD4XMjoGInva2l3C4UwmEVgLcu/mU7SDbInSBlz94cwoKQ/3HLWhSXsgY\r\nRWcnQb0F84BbtudwQsHl7m+yfzOQZpPtwCofbHvT+lhaZAzRlEogDnncyBTa\r\nE73eMCKwVMysiT+2cJX3bZ+0H0kelKjdO70rjlppp8uFe2Jj/S6SXY7FgJso\r\nB50NkWUX4q3T19qQJexyBvkuLFArpBr+MZg=\r\n=w2uz\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.26_1651058605202_0.2672519271341114","host":"s3://npm-registry-packages"}},"2.0.0-dev.27":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.27","_id":"@keep-network/random-beacon@2.0.0-dev.27","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"38d13d7bb464ee27153bcd53cadc6242eec90486","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.27.tgz","fileCount":129,"integrity":"sha512-Qv7NWiaaReVN+pIPTdiDWiTCRz8PaTWk2I+8v8M6dla3SdkBbK0pnwwY4wnfB5ldtH/zfz3zN55b7pd0odPi3Q==","signatures":[{"sig":"MEYCIQC0ebFyfSULy8LqysW1/BH0sl9fiLKjDF3zZLggym0XMQIhALEICoxTVNm9dITfDF77/JeuxpHL0G91bCtA6d3qlgQ0","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19227749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiat94ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmocfg/+Pw8LBNs37lL/v+VsahNs4yUxHp3QIt/xEUnXR7vP4pLxaweg\r\nzRoADZT+Wsd56sZcrGXy3xaAXt9+DSZnHzw1ZjRU34//iKNJtXHyD9/6VROL\r\nvHAoFUwawWctquJSnZZKZ2ZIvZaqn3BXXTDSXurAMng+zUQFIO8h+ZkOYi9n\r\nsGc4vJE18LVOKFQnkwsN6h9tpqeGOndfCYFn/jVKjBSMWPgZLPQU4ygLaD/G\r\nXF0RONwbjWhT5ofo07jNI30nnA5+3o5/2LEa6mX926kMc0lTxOZiC7ldV82u\r\ncSGlPqNcVKUESANRR/d0X0OOQtaMTKacfgDdw9LxXT3HZXQjRod1FcmFDfI3\r\nzZnErJzeXLYc0OaE/srAcfIew89Dv9NbMUjGN1X0M35dD5509NTR0/j6Uj7R\r\nOJHNKUKLlJaOQY8kPEU0IgB7McFrxwgyVlPAn+q3VS74EDFFry1lt+jh+ASd\r\n+olpum/VcrcuBM2iyST8X7xZmrtMAwjLSDQJ1irvxn0lCbqEX0qBtyTi1ziC\r\nEqgsDL8b9833B9iS52/m+zJb81zDNbf5u48VtdL1xoAcaCi4tzWL0CarzYY6\r\nfI3ZUMuDqPvewZZP2pv98rqgX3QJbJo9mklkLwZY5n43N48JDKcMT/g99QVu\r\n6wYc6YZgsNsuttXmCnqLvnSLyfQnaeDFzng=\r\n=59vW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.27_1651171192086_0.614719493341433","host":"s3://npm-registry-packages"}},"2.0.0-dev.28":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.28","_id":"@keep-network/random-beacon@2.0.0-dev.28","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c4b1be1809529c18955dde68a02c366037e42f20","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.28.tgz","fileCount":129,"integrity":"sha512-oZ+ltauu5gcfPveDAZKe0lq9DJgSdVQIe3J/HY5OdSF4B6ZyEV02bLs1wZQY0dTV5Fxu2pQdwLlJ8cYv2KMrWQ==","signatures":[{"sig":"MEYCIQDrsnsL+wyJpyV+rVLKF050kb86Cf26Q+V/69boJBPOnQIhAN3qg/sgO0mW7TppO0vIBNW2tVPECZer5CETgZnu2wIR","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19403962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia801ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq0oRAAg1mjLGQmgHHPiiIxRA8G3CadHUe8XQDRJboBeOeK84BfoL09\r\n/8k0DfhXiWy0TL/xtdujwmjUeLap9k/aDNT8fc1VL5lD9ejnyc6LhMP+tLvA\r\nlDJZ4mN5A2aClpmTpjBHvTL9OFxg6ftncpqMVnFvzy2269wQsH1UAPGUvEjL\r\nOC4nNuE6b8OzcNTMG3hD3jGAURTgqeV8ehahRhGr3H7xQdG9eyVButxzZ2DL\r\nnDwMqeW+g1izOZA4o5voXnUevZD/5L10PtxCLyvii9vLbBZSUr/lhHJ3+6Ae\r\nIT0wvWBiPD5SKEUNaXYLLyT8mcJSBI2kUHf7QKdZjXPwen3ySyR9G/+jL8AX\r\nhGgHkzz5EDHWzdbjWjT1vy3tqbAmAXCXEgE7fwmRTaE2rzDgNY5k4CuVpRaJ\r\n7o4l759sgRR7u4RJRIHMqYfk1xaDDuhkrj1vOyF2MJodCGjsJrxBVsi95Ba4\r\nqq+w2T5OmuqWi84oiTPHceIQ3BSn+n4/7yaqoW8d9SnOqRqgfoqwvmoxi0gV\r\nNtg9iH4dEUHa1PVLtBZLZTf4j+do4/U5mhj0FoeBvBSLxjnUz69FTmTTcMZ2\r\nOUyppukdE5g8FgalH46mObyJ1QmBtv3SnWRntSAxBN70A8xvKzu7AMktH6zK\r\nmq0GB06AzhI9P9RCFAieTJhc+rmYqKP/lSA=\r\n=nIrq\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.28_1651232053387_0.28671520507988313","host":"s3://npm-registry-packages"}},"2.0.0-dev.29":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.29","_id":"@keep-network/random-beacon@2.0.0-dev.29","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bc062895c4e2e75e6150be9361cf27eef2e99994","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.29.tgz","fileCount":129,"integrity":"sha512-FUhpp+qiVBw6BBo0KWOc06F4x2VsbZ97YmQakX1vIR8QWHr/7Y3acseeEfxJYFKBCEFci0XHd5ycfGC+fOnWng==","signatures":[{"sig":"MEYCIQCXUBJJAEdISRUzedV430x9j/lY7FMNC3Ha880VlngJ3gIhAPF9NbMbTeIRoVCy1BUN8zyBDfLk93SWv7xccvruB3WL","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19404055,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia9ZNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrOLQ//RL0NamoX/fWmYBLcWAUJMNXmAe6RuRjq+QYt5H0N2gPk/ZI+\r\nIQ8GI7gA8Y8w0Ku0YqTH9sD7+8jFWcFvT9AwMUUaumSLmL9PmizM+tz30ctk\r\n+iRtSJzaY9XhEKI6TdHwQfRr1pHMI8c29dJBVmZhevz714gcXbFQ3Uoc42hh\r\nFKMcaGdTbbjAkMY9uq0j0gJNLY09ynQjvfo/w7jKy9KFn/eNbNkSIJPJ3cQp\r\nR0ATtR4tAZdXAUymVXsCb6Fs33ZMWCj/Q6Lz2aTZDBnHY9niWULN23BS7g9y\r\n7O2NjEhc/tFEQluVycjUCBjuJqPvAksZe2T7w8N3eJ3G7rszDE7sJeiBLXRb\r\nBbkPTwwyLThMGHxM7k84kyqajle7CNO5UOoDRmXt9d9LeJmCm4WWvrbErzqH\r\nJmMxv/m4cbcvp2PrCBgufofnyJQhb/JiaVuCedz2010hdvE72OBbH/A7E3g2\r\nhHhoqn79m3RIDBwbSQhNLtgaJ140K+QHKk3NkQ3sbwdb9UEsvsI8wu7sks2C\r\nq88jEce1hzkCHHAlXGhUZiqmz+sR3aiM1loQmw8FmjZmOUtTpSTvCYUL3olG\r\ndzszGgk6V7e2MMC4a9mDVSuaQZy65dWVnDGqrUZ5bkcdlynLMLaF6RH5iH6c\r\np+4MSY3mmULSlHABLTYl/85x8Qxr3sD1EOQ=\r\n=0VRQ\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for group selection, we use a sortition\npool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG result\nsubmitter should unlock the sortition pool and mark the DKG result as approved\ncalling `RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transaction as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before a timeout, anyone can notify DKG\ntimed out by calling `RandomBeacon.notifyDkgTimeout()` and unlock the sortition\npool. DKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Off-chain DKG protocol executes in\nthe same way as for v1 and inactive/disqualified members during the off-chain\nprotocol are marked as ineligible for <<rewards,rewards>> for a governable period\nof time when the DKG result is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhave a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto provide signatures for the DKG result. \n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which a submitted result can be challenged.\n|Yes\nd|`11520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`5`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`50000e18` +\n_50 000 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`1000e18` +\n_1 000 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`40`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`100e3 * 1e18` +\n_100 000 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`50`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|No\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`100000 * 1e18` +\n_100 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`403200 blocks` +\n_~10 weeks assuming 15s block time_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.29_1651234381113_0.7243854114772939","host":"s3://npm-registry-packages"}},"2.0.0-dev.30":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.30","_id":"@keep-network/random-beacon@2.0.0-dev.30","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0565fef5ce8071ef815740d44c091b6d394c49d1","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.30.tgz","fileCount":129,"integrity":"sha512-heFgutZw2TmRR0AXx6GU3oJoL+pU1nJEkHRHKla0Uuj9MjbhqlKf9wz7CIwpzdSMHS0HOGbBJIVPcKNEAGdAEQ==","signatures":[{"sig":"MEYCIQDdWMumzwnhxOEs8vvi1kPKKFVCYsUs7/um3w7o2UiHsgIhAO5bH9Wsxy7sSchWAM7ON/RB+ST9S6Uk1+gta6HqVqp4","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19416211,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibA8NACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq3Uw//ROv+kfXvcEy6YAPxQUcR4K1Dktf8Ga/b+5D6T/cgW+H4po4D\r\ntWaeGqPXpL0Rq5YBRTYd8fS0gTPco4poRMMGvYL1+XISpsK5THE51QxbyTXh\r\nAmTzWla3cN7YixJOecRc0BaITdvY/3j632Okp4ZD9V5kVqnaQAVuMbqe9+l4\r\n3p864FOLLY5cZS/oJAv9FBkakHqDd/3YGlv4+rbG3ploM+Z+D0QQhXK98tHw\r\nJFInhzkZtZxjdyOGeug6c720AkffLV80jop/42OSjvN2hJYfXadJEuMhqWZt\r\nvaJ87J3E1O7+EXsEiDD5X/AXVhZ5wwM5XiymFUotZXdGL//WE2p1+afeVvaX\r\n6vAEomynTRrQu1WFb2GkNkuhyQXm6botEeyB8DMkyhxC3L7es3BolOJxHihR\r\nqycEhcpPr9UFXloXzPBCu8GfueKVyuhlEiCwyukAIUCoZXblakkVKvcW12F/\r\n2/zeRZ9Z9TwM7KReVD41qQYRj3sBkRJlgceqQhbxaSt26SrxtE4d++CfMMd5\r\nf8fzD2eVQO2Ssa++qJD4VSnT2EEg7CvJS+RITQqkGLjlvCvZ86YWyqPtceKD\r\n7XcG9dxu+eYjsgndO8/HG7UNE3qpdUKM9IlnXhyjh465lQ4yRWLh5SI94MPN\r\nhr8auaiVQxbQcCpGDKfXVuYBnBt/BXwgFc0=\r\n=whuk\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`56_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.30_1651248909018_0.29636060625985516","host":"s3://npm-registry-packages"}},"2.0.0-dev.31":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.31","_id":"@keep-network/random-beacon@2.0.0-dev.31","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"69326af804b6992b13ad9aa810bc09d89352af33","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.31.tgz","fileCount":129,"integrity":"sha512-C3N96BZQmaqowrCGJ+rQnYGzy7roAscIGZrEQabg1WTSkd0xkFODhdgrpRrvr+Y2pvBt+u8bAfP2H2VBC7g5Eg==","signatures":[{"sig":"MEUCIHR+UUBvKWAQwBMzgSpeA/TdieWomXlaTS/m+Jf8SZWFAiEA0Q3fA2FRxsGdv14H7nsuTDMBgjMvZqBVtqULT7hS54o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19423661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibvkKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpOPA/9HNL3BZB6+70iwKdpMJSsnZ1aoOWIDrWZhHn3u6FfG6CyU3o5\r\n56OwN23n7aHtnr2NIQdyFcFRaHZxTIxfPVI1I2E5hQiBZWUYxgpCAyYB7NX1\r\nurHs2cBCwZiWhpp904W3QPzwUniuA4xpeTTSs6wgmMps+UGS/JeFNpmaT+Fs\r\npalVsYGG8OMiLNZ4AotNLWvBaYwCYrlR5mzFaw419Mmz3eLv1ZOzi2h/rwUy\r\nm0pGt2nFOCOFO/Ap+iaUpYI5b4KoJn04W2J/x/7wxrmNrvcTmlI97i/DJcQC\r\nsaiY+SqEUQUbu07TO/P7MBiweDW23fbyX5inUO2brhQHK62Uc2nw6v5xPnn5\r\nVi5Yg7LfL3WMLIe4ghUqK98uEmXON2/fZNRk3LrhIFOtFSNYQ2wkijV6XK0n\r\nP6qK2otFB1IkOh67vFPTNgVqu4WM67T2qC/FIcnGQZOOCt+O5e//IoKaOrAL\r\n+vYYvNKrQbAqSM1tt60VTzS3wxYF+IiKfDOhjaBDhUQKQ6d8fSEpbkgNoNrg\r\n419WPXdIIUrCqf8dENSNHbCdUN07PABfALOe14ndeRygx5s1xCGXhEc5iX1x\r\nCk6ntNd5JHSndk4ioJy48BDWoZZTMHPirBhH0KiPaWHbtAOUvOqmhfZoNkQZ\r\nLQ4pEMXlgUN1qX1Zm0z2Kj4dN6J2aUNjB/Y=\r\n=J13V\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.4","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.31_1651439882530_0.21132610646492433","host":"s3://npm-registry-packages"}},"2.0.0-dev.32":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.32","_id":"@keep-network/random-beacon@2.0.0-dev.32","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"eb7edcc5e5ace484eeb928e20b195b1379c619d5","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.32.tgz","fileCount":129,"integrity":"sha512-rVqUdU5yKfFu9SywTCVlwXqG3dZSJp139wivQG0QlvPAz4TrnO1iIcMgohpeV9Z4DU01Mw+BaBWYD7QF/OLAMQ==","signatures":[{"sig":"MEUCIQCHDk81vOCZL1ARmJ+B5vnTVpIDkzLwWJVhK2AYZD918gIgTL4I5nHLRgzPmIj3yGcSziHiAsIp0n6gmgIc/yxLjqc=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19423708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJib76IACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrDPw//akXksvN3u8v/jT8FU2ku6yVmeAx8AP5WqQQO77QI/sTgJcuv\r\nZ3j/6Fe5Of/fvITYi+HkyMMOeHPyuAndyO1WZ/gvZd7VBGWkg2nDq15vA/Pa\r\ndrG12GAiT+YI7ULYuelmRaYYw1/WjH6ToXRGPTpITKD3cXS/9EGCslPSlBS7\r\ne+bI00O4WY4VoryDZuJVb9kZqVM0BoA68eeSUWaCbNMu6Q68vAKmydYDvhZq\r\n2TU28YLDGhq2ZLqbPrr+wGbuwe4huCQHAQzWjU9g11t3VWcatvKQ7BiNLxeS\r\nvT1uvr1AZiOvw6q+B77AOC6590twFpmLtCaZK4kyjbiCcIjL6NnwJfwpf+ud\r\npY3fX3SWFYjyc8O07HROmBgwlqbNLuXdWsLb7zSY2MWAoxG1kc5RZqGlxDVa\r\nGN0gSitJb+QjlemdmINltjiEusJMlXN6p8VKvh0TtcHR9s1ga9A6dz1DYEa3\r\nbsfpe4E4vAgexZ69EuYI0l6nPFv2l4v6mhd+Srvsap/a4LJr/+3ntgt20hMm\r\nI2M6BzD7uFGBBGO74CjM3nPJCX0w6Wl9ijKVfObNAK/VtasCgG8rrdJS82qj\r\n0TP8vRekmPQri+UunRBE7oJaXflhwqkTxQDEpHvvkTorwU6X4D6n5jfZeQBW\r\n/b58H7Q3+n1QgGOxKacCi+XIa3bRhbtR6/Y=\r\n=sbFL\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.32_1651490439950_0.1614573440867817","host":"s3://npm-registry-packages"}},"2.0.0-dev.33":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.33","_id":"@keep-network/random-beacon@2.0.0-dev.33","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ed6b9d723d9f4b7190bf0eb6cb28b172cc851029","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.33.tgz","fileCount":129,"integrity":"sha512-oI0Zy+kFWgq/E9dpQVfjdp+PqSrxy13FPXxhZrPpUrIWIUrfb6MDofNu7nkS/obI9wBGVRZvClyh2oAOu4u5gQ==","signatures":[{"sig":"MEQCIFqktP2gZjne/9dM/NxR/OI/pe1182Og7dfz8iV3CsIVAiBRvog0k0ZK7IrJI6gzPgEwJWzZ9QzUN9yJSAioaIXwMw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19423708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiepjyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq4HA//TTcZTNEUiBp2JGbFIE0mYnBHEQVbXqgknStLXdikoMP2c6kb\r\nmBkev6rUY02z0WUBaS85uZ5cz30viKbIvBQtyNvJvW55tBXqfqNFE0vRI/B5\r\nUAG78HJ26HCqaEBQDggP/Ia8E4gMsqhTb+FqIXtp2yDjC3ZXzbQ9FnfCZC4N\r\nA/A7R2u/Vu2PIY//V4qAnNE1/JqDKxQk2Ng9lOUvqxkGZrT6s5xja/JCoLBT\r\nKHou2qaAuc+J5ewdiHQLqrPlNTdCbj674vNOkPmmKP4z5DYbco05EufEwqHS\r\n6XOv9urF1ligeIKCo6I7XZbUDFksl2Xbs1T4MuSwfCN6WcgvfImP+cOOVfIz\r\ngmDKYHGaZ0ONSI4LbEiT69o6uMEDBHZdHTzjCKXNLV1NSDFZ1dG3afhZyfrZ\r\nN8331UsY6YGBS8IbVVk2eyq4VRYwcn9jFw0XKWmUu3sOsN3RNJTaeZoUksu5\r\nakXimtMWHnaVvMgbadrSrLutT+zW5RvQ/OZZQ5qPX1HR6635LLIKkNF8aoOn\r\nF9c2oRbzs2EbCzMfbvQ7tEyt6d1IjzlDaeGsui3UXq2ffq2Fo2Q3OtewxhCf\r\nFAU7DIddWGiUV7/qUJO3gpYTRNfTU5T8J4C8LcVGnGcFou7ultPGeTwxoF/j\r\nhhSQfgWC7KG6b7Kt/jxKD9ivvgAsyl970jE=\r\n=+KQx\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.2","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.33_1652201713918_0.3036804610637118","host":"s3://npm-registry-packages"}},"2.0.0-dev.34":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.34","_id":"@keep-network/random-beacon@2.0.0-dev.34","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"145a15d13b64d878286f1c1fb454d335190dddde","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.34.tgz","fileCount":129,"integrity":"sha512-zrprN1qZd8FkNcMRwnzJqqjgr04YUKZzLTGrO+ZCoRhCMvfa3M/eI1fPLhHEDVpCLxl/zpIh9QFmAxxprsinnw==","signatures":[{"sig":"MEQCIDAyV+HbXMn9hs2G2uCDhF4MUEutXPvl/TBMS1HuJkb/AiBm4ciSMeALmAbzo8mBlNNdyEMo+Wxetsl7kjXqKDz5ug==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19428160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifRMWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr7+RAAniHRnio+FmmhDZVKSSXQm/Pb/C79u4xgXNtdsny0IjUqMdb8\r\nWyTEuEaTPBJ3vmaPKe3mg6qpavJjqacpjDcd6sQfVnRd01quUKkCxIOYWRH4\r\nrPW97PlsaADv5bRfaPORJMwAamY907N6NThgmgzhNyxqx/nXf7OrzQORHfWc\r\n/Gh1h2x9826GofhS3rkxdjW7ZjHU67rK+XKp/F+0KUhpq9eI/ITLJb+gxuxO\r\nq23JfrTo5yLUzlWDTXwLRBbTFPdmn6WZWxdjVmv5QjhJfZBSWCgrz396jZjI\r\nIQxj+7E+2VWcqmT76LkPR+jHV3mSAyqsxFu9OmCt2rO/2LMyV5eQ92NavVDF\r\n64ET3ZCdDBAU6INZGbV1Ale/dG3ShNuhF2WCIyVZofEQ9uXfB65H/S+zRdeY\r\nvimbSrH3c5ctuZFIanwiS7BwMiwY2SpcL4mzpdsaPFnndq4zKIUwFJwLE1Km\r\nWqupT6pXuVZ5G8jE6yzcoL2Dxxn3Ej/h1YS30zVGcTtHC416P3ye9T0Cxrfv\r\n7MDoqhX7rcASeKruSnnuo0ptyTPRbg6iyp6wfdtuXy0Ji0/Pq5E8v/g/t0Kg\r\nN44SwIWxhz47zSj/PHBmH+GH9cwHE1eMTJtrmfAuU/Rwzg7At/86KpCXg/TH\r\n4lxhGWngwHLDPE5Eit/vfIUEBIgZB1LZar0=\r\n=9uvB\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.34_1652364054437_0.4216679181608405","host":"s3://npm-registry-packages"}},"2.0.0-dev.35":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.35","_id":"@keep-network/random-beacon@2.0.0-dev.35","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"95a7ebe971d6465ecd39f847a0de53b9f943ee8a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.35.tgz","fileCount":129,"integrity":"sha512-x49m4Ff9k6IG9QMYKIvkODKQhzge4Qzr3XKl5kyMC6oraAirNHvLGqxKHg/1hlT+Tbmnuwd7eppwyL4w2fsTxg==","signatures":[{"sig":"MEYCIQCAQPLu8+B6os0V/SJvAZHsXe7QfzZpcRWElP4jAQVNpAIhAPw6wZpXUpB04OfWB3jnU/1hOMFewao4P3qdy7nG5OmM","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19428160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifS8XACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmowBw/8CFvIjGSaeVqrdJjdcEcu8RzHj8yzDE0GHeNSPEC41OCVaZPs\r\nkVLKy3vIFJ1jIf5PntbUOpsgUdmEV2qnLNUgVgM72ry4d/FcXTjBXpX8sK4B\r\nvYytAzjIs2tv/YhvD0/etrZQ4UIYQYM3yaVGWeytFUNskXK7AsZWfTNFIUth\r\n7iqe47BkEqvgVRDbMSnSY+Lnd84942Uh2oniI0/TVM7C7M0Mkyl8EvNKSaFJ\r\nx/jpSCZfYDVdMzv6g5VWkzggEsN8vMSvXO+AMLRU3tajHQK6AyyXPVdDB+Aj\r\nnDvSmM6lFNErYbYyGK9qE18/BOyyoLORK2qEVDDRqRDuxboGskn/61kdUG8D\r\nPMEwwzD+QmcZHhaK9uqfNwOFrTqQrhgYGhMNFMEoUI+cz72tgEhhG3Hy/Uae\r\nU88Wmznv0mrsNoQxZGn7y3LHS6lXG2SiKOYPkS13ENrgO7NGnfqlx5UFgbv2\r\ngcpueE0f53xWD9CJBYINRvIYzffeoBRtU7fo0wIq/ejc4NL24H7uBjRgHfii\r\nph67OS4Xr6xDJSc0LqRMLe+34IkPrsIvTumtp/S0XwN11IwAaUwl75O9+jj8\r\nzXIcZRBCGhqCUaLj4DntameQQ9rRaBhdZk1fQPCmdJE1KrZKpjI+x+M6kB2m\r\nyn2weXfRRR2R9PDE+SBaCwqXlfoNANzjiSU=\r\n=PuUv\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.35_1652371222798_0.6342007459224719","host":"s3://npm-registry-packages"}},"2.0.0-dev.36":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.36","_id":"@keep-network/random-beacon@2.0.0-dev.36","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c2b62974c655301ef218cd6e7b2aa4ef4e8f7ef0","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.36.tgz","fileCount":129,"integrity":"sha512-a1RiOxFtn8GnNmWzM4vwaQE95PMbvzPcDftbRv+n1s6/69rHMIFzHXLbkcMNQ8IIjUD0p+bDqghNOc8lheLHIQ==","signatures":[{"sig":"MEUCIQCaiiTj+7DXScxiQk0u16tdsa448Ry1VEEhfuFXwyH2ZgIgQs0dOBZ3/JvIOTyks/RnKprjUGIazWsfgOPdLXazEZw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19428174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifTs/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo/7RAAnxJf3j75G7//aZwXsun1z7hx21Z+bqh6kcrFEFnfo/lWzrm2\r\nbTtAchkFt4EKhdkUxkCWagRpE4claUf/gdasPfCCd0Mt+zZ3meW9+Z30knKC\r\nbXZjMCbz72kBuRmPe4YXZUY1iYVAzIhaltYmvILc5Gf7YIicjr3luO5WmCxS\r\nL1kGFLW9PGjxkngQIyt3VdFVCeunBLu383i3oSN2Bj25LQ3CdlbtXNFf3rmk\r\n2OyrDdRwbDOxYr27eQQ6jYTw3r3LQmqxqKSHQSPTUWkySqzoKRoUhuLKaskH\r\nne09fDTTl4ULgSFFSe9eYg7TEjIAQSLRi2dp2xTn1HwkEekhmZQxXH2uHG0X\r\nV8gBXztNChV2gpeP1x0eTxCAFW9GkXVB6AZpDFMz7vWFsfFDd41UkqpbGqlk\r\nBX7PfN93GcDzCXFtr4/8DlAW1yfPp4V/3HPte5Bgx44vt3MMHnZ8McFKgTzc\r\n+9G3VBxAa/xEahYsLrfCShAe9SHs9PMcfsIbMokAnaa7ReBcWujrnJPyM0r9\r\n7x4XHJY01ImdKbZL/BA9eyYkwztvV40fyMd4A+jODMjINVmk+hyA/X5zc4JD\r\nOZXsF3yVj2e46rOBmoW0O+KnzuN1LWbp+BfsXKeN2bpJnFvKL3Vwms37ODfj\r\nXrbfrG+u2NqH1owLphgDBQt0N+fZpilATgo=\r\n=gAue\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.36_1652374335692_0.5242748077409536","host":"s3://npm-registry-packages"}},"2.0.0-dev.37":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.37","_id":"@keep-network/random-beacon@2.0.0-dev.37","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e5807b11d71e475cff3823c979a6b51c711e72b6","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.37.tgz","fileCount":131,"integrity":"sha512-B2dfpbg7hR8/1rZqDugfRXZFav20cLgJjN7FS0MR8ziU3BsMxgU5mKhharqzPk6SV/pi8wtPb7QpsUqbS6reTw==","signatures":[{"sig":"MEUCIQDzEbt0HwZziRdOWA8k2ZOeJfqRqr0su1uJdytpk9vDigIgJN1OLOogDyawNLOaAnczGck/UfmWbLMzeui4MEGQdwI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19432938,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJigiTmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqyTA/9FZgTvRAvKx+3sQclywA0rOcpCWKneweIhDTsgYKadCbb8FUI\r\n8RP1kzuyXFou4s939pMuRjgYZFXAl1bhJrDFWWbmpGEEdHyOtKrxaOs2kxRX\r\nlFrQcHL4NfS+uIHnTpF8kaWfbd7jqVlCEOK/IPD1o2OhBZ9gKRNuDe3aNMgW\r\nTM4SbfeGIgyzXtktIQLkJOEFYmem6tfPd9rVlXNSLYyM7+XfL2hK26qdRGJr\r\n/basOzp8QOH4Xt3UpL8lfkHXXtRMdGBMq+4dn9B1afQ9glS6l+CNDJI5ecmM\r\nEBTk9obTenJbkDr2TSF2LqiyjyCEjJd/xS7Exkl98gqkJIjC2wTJorOnm7H3\r\nv3/1Ofg8UJZ4c9dbMPEsLDVWt+Unjd9K+BGUWZURTU7/Pca2eUAmHSafDJIW\r\nBf4jDC4ENzLeHL42Xis76NWTq7BUnhueqdAkr1ASBR1Y1iKh99R5SmwQhCh6\r\nzaKY0bOlbJP5gI9NfF51zGjtNWGxfkukeQV2A+ZF7I8pgt/K+qtxZ9nJvWRM\r\nWzBBb3BlWhEl0vUg5lekp94lvjCYMNZq4dsQvqZHJw+vhJQHTsx291la6JTk\r\n6jZcRfTqYwMWSp78S4PNmuCN7FwGyFQySgyu1gl6Ol7gsWLUo8fItgRTIDw9\r\ni37ABzCUHS93sYgDSNJs3mUfDXITMLbWseQ=\r\n=XDsy\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.37_1652696294035_0.9886242419692006","host":"s3://npm-registry-packages"}},"2.0.0-dev.38":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.38","_id":"@keep-network/random-beacon@2.0.0-dev.38","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a46c1ba912eea119fe05a417096131967bf5330a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.38.tgz","fileCount":131,"integrity":"sha512-5YtTJs1lG+5nBu+SxDyQkp1sYzEe2UQxmYzAaVG9RV7A0dSV8rm6KsXu/3Aa5DqnVnH/e5cGw+UGPrVMXm5xKQ==","signatures":[{"sig":"MEYCIQDOa0uHKjJtBD0H4HALrG1sQwFGTrg9M9bz3VNxEEMKBgIhAIKC9D9Iw8G6L2FVSJiE/oxDuOB+6RclCL1lsoEOkbF4","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19122296,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJigm6XACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpLnA/+P/0NnWx0Ol4Pmuy8WGz72je2EOSgEPAHud+u+RELIX5bSdjU\r\nnJSbZrsngi2ajECIXBbMvAeW8t2miBMJxBABd7JEMzpGo4Zxmfogm90535Nu\r\ni44P9WQ1191ylbvnX8Zds7x60M9oZuKO50pGKcS35Z9YT3SUcke4iHn3q2wo\r\nXKSJNN7OwojIsf8gVsj08FRPnOZH9btN1StpnvKKaBErmReheUVKc4m5xR6B\r\nRFV8xOm6dE5zFfniExO4aH9SHvDBnyo7aATR/e1siPPMtYBJ21qRPH5/Ncsq\r\n9XDQS8zIu6UQfwFrB6WRh8bD4VqXNnq0VMMW7XT6LJHYW8oQwZBwtzutQVbn\r\nqRTKxU10aCEcqwnaXrP66Y581GXtfOZOWdUaNK4luboCKqcU49Gi4jRq54sy\r\npLrJfehF9k+JETlbxfuzlWa6WKVKIxcjMqemCcicsG9o4pbZNK3j8rZEvorB\r\nvJM6+renkwPsg+PbLhLGMubkpjLV8mUGSV8WWwPVRpaqlw0K4ojiW07MseSv\r\ncKKhVFAfvQ+cK4pR2puUoTq1RQEr6vsWeZQed6VL1jBvhmXn82pZ1J51g98i\r\n293ZO/A5aQKzr2UMA0Lmx6uPPmknkRFFyt7G7Hdu6fOu9z8DuVWBl7C4xq8W\r\npVK0miuTRe++lMi17rmm3hIgJ3c2p9SuLAw=\r\n=3lte\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.4.1-pre.1","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.38_1652715159145_0.33046489182937533","host":"s3://npm-registry-packages"}},"2.0.0-dev.39":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.39","_id":"@keep-network/random-beacon@2.0.0-dev.39","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"592e0df954931dd18896b6a6be479577d075cc59","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.39.tgz","fileCount":131,"integrity":"sha512-fm07pJE1E2bPhw1zmY695+DVPVikRUZ/Jm2MvnDJ0Ly2mhyHOVp96zW4Yd35sNXmMN/dL+eXRc1Jkwr58NoCXA==","signatures":[{"sig":"MEQCIA85MvsunbwYpmyOHiLv717K13FZw1k/VHDa4FHMt+h1AiBuCnbSZnYfZDCGCVQRlxjbw+0OyOrojnSCKNdK7HdiSw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19122379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJig1XfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpUIQ/9FJ7YA4LTjNBgAjiAxtOmJ2tQiIlA7z5ImOeRoAMxnWGFxpKu\r\nHCtxtv7+HIeMCv4vhwwqiPVDGemDQXNeNYza5/iC8ay6zHhEBF8W3fs6lH21\r\nZwyFYWoKFYq5g36nl4uB4aNvVLQ/4u9YJoW9bdmYsKFUONDjYdQfcE+vT5dJ\r\nj9uWHhm4Zax6b7JcFVA5ZmiLzAKtEbAJT/wAzBkhAjY4Tin0gqqYogPR1qtc\r\np8tyKhldNZDZoRtT64gvwrfL31A1+FyJKtGFuZ4w1DSWkjzc/mdXVMl9jnPb\r\noX4ayyiIJAjuh5PgmGGqeYKeJ+eVANmaGd7S2QTaN2W9OCXX49dNOWZH5bMS\r\ni6aO9zxhWJwXwwAUG46qafyKK98IbEKfAiH8FCR+lnf87rkSoeVVwQ60MEyf\r\n0D0YxFk1dZw0kX0wIZcx0t04gFGToNvcaeAONzIm8AVRwgvecqIU7BIbxMTc\r\nxkd0qD8Dmb5fo37N5TTpYciRCuqv5pbxFxbFVQHHiHJTz+l/s42+mHwIrR+z\r\n40HIUeBQwE0Sw6G1W4hpFkOnQI6sOm93M6OELlmvQpuzud3CveQt8+LBYxcc\r\nPAW9bQZ2CyvohWDuTgilc9V1o/t/Fz2/QezORKeR9yqAUgSo9Mh3cYnnu2RF\r\ns50u02oBzGtlFAaX7Bo9vs7JS9TbB/hOHtQ=\r\n=Ixa+\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.4","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.39_1652774367646_0.9077489279069175","host":"s3://npm-registry-packages"}},"2.0.0-dev.40":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.40","_id":"@keep-network/random-beacon@2.0.0-dev.40","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5e477ff43b14170a206094d695dd556c0638c48d","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.40.tgz","fileCount":145,"integrity":"sha512-U/CxqFlTCjGo9OdHAddcF/wg1vLzS1y71JlP/wZbzp+SnHAC0aJQLCtpgX0yL0VZRhLjIORcP91TiURdFYdBRg==","signatures":[{"sig":"MEYCIQDkSavvE0YH3xJ5kiTgwu4Y7GiHdceA9RQoB6Gc6jgsIAIhAOsrc2zorohv9IQhFVEbcHjK5bTPfWLvFz4jvcW4DgBV","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19159602,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqu5vACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoq1g/+O6L+9F3IhtFFdzb/k7c5cxG/YuLm85v5+o/Ewz05DhYJVz8r\r\nUa5Tf74FLo0zXILa020MOPgHQitGcqplU28SWaHs0zWfZ3T+qRIryjMRhA/P\r\nuaIbsdDNckC4I+loKiq+DZgbif0NHI/HCUYF2sVWNdTrx4ipHAviptntfCq4\r\nJ0DgbhVHkZnh8u0YujveKbYEThwN+qXMpLAXscKHHKPBEGGDVfOH/dd5DxwW\r\nTlcE+jjKYz5/FGLKnvi+TJbNKUC/tkvc69VY4LfH2yMfliNuYFep/88WrCIj\r\nmjNRGrDkhQ1xR1GPXWHLYHafgpGI0Nz50H7tghTw8iaMEeBJx7cJZ9vfcnma\r\nZU90d/9CgD65ACZGmBLzMCP0dBFf6c/tCf8ieCxNeYXs8xf07yzOEJNXZMvt\r\nOu1OzhXuIWPk3+EapoEE3SlJDwWveXnELN964nsYiojAzg59E0Fu8ZdVsr7x\r\nEzaTSbBQPJ34AL450oRTikvoOy22KV7vxugKGhgWhaagycsCZDNVEkzQkRCK\r\n8fCc5GNwVzJdcbSDYmMYmvRUdhwcDu5uEjuRaWhY5oqbmpje1mwmWhQ123Xq\r\ngl4a2eSglD5g6660edQ3yU2JGQKWdlywbltsqbr2j0O4/C3LAvlqSc+88Isz\r\nrOISw4zbJaLU/SzuYGob0FEouGwVTjgFGSk=\r\n=fqXd\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.7","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.40_1655369326812_0.9116548533702906","host":"s3://npm-registry-packages"}},"2.0.0-dev.41":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.41","_id":"@keep-network/random-beacon@2.0.0-dev.41","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"b42442b95b7410729fe066e224d5d47277087568","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.41.tgz","fileCount":145,"integrity":"sha512-3/FGR5Ft9vatlshTBOgvdwZlvRhrlCB78+7sQlJRjtv7Lc9z2TarBImlom6e3OcI8ufhgTMaCREdtLUe8erWhQ==","signatures":[{"sig":"MEUCICE1uRWfHJwbaBkR9LeZAQaFwPEjX+E1gaDxfCkZXzikAiEAkW5H27wfe93eqVZxD9WJLrd8AecuN4a4FW0KiOlA2xQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19159730,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJivtBoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqdIw/8DkGSd1q3SNnuW8zgus+q1RyCRu80LXUh26/fHmff9K3BJtil\r\nK6+c559OfnCiHI9HgXKCN/p7Fsf88LQX2RnW3R4TbiETACxDocMuEhzWayuX\r\nS2TDCxdEQu7jZ4QTo/65d2mocdwgUMxbH/vrmejhqa8hl3yTTZC9RQRFcRp7\r\nr0c07IoCyb+FxvKA1/TH2HP6Gfiei/l9WYO8m+/BlO362Gl+Kx7qA1yWhKDB\r\nh9XxuTWqHnNTHpwGEknIhndb2tp2W2ntsIPevEAjAeLDe0KJOn+B1s2b8wny\r\nrz6Tp00YcCJezY14MwAxTZ8MbhmhqbquCFYAzGvtlElkaiJ/ONsLRLvmM4kN\r\nIUBgtqXkJxFmLzEGaG5qAuEJIM1x+ZpweP8AfAso/r7IxrXrSaFwY6yd102K\r\nY1+xwLJO9fm78GTtgf2Mcy1kQL5UiphegDSninS1AZ9h9+KZDdb9644ikZoI\r\nslbe7IWQvfOUcRlZ5/q53pz6R5kOrqqM5Jd4LxkyLu3241wmO11cw6/vG356\r\nh+nIgz9JCo4/oeKOvj/hEIiAblXker99O1o21rGrkJrlNq+8Hz6DtvQAPJ9c\r\n20u43qsXf8NyWXPrCwurAo8lrJCu1FnTP5KIQLIByUJCUe0YhjjV6u7P46RI\r\npWiH1QWXduqUzOTGo0feFOnY69jpdnDd7ro=\r\n=ioGg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.2","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.7","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.41_1656672360305_0.5913644754569194","host":"s3://npm-registry-packages"}},"2.0.0-dev.42":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.42","_id":"@keep-network/random-beacon@2.0.0-dev.42","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5b69e675616bb36eef4c2cadde9a353f843415b4","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.42.tgz","fileCount":102,"integrity":"sha512-6afFkCbkoXmqrPDObIOd+eDQn/aXFCQwqcymDjipN/MLS6EKT0QUEuo84KQjZh6nJh19L2ggGldu/UhcSqnIMQ==","signatures":[{"sig":"MEUCIQDWcSaO6PdCWTinbnaNQvJOPFwfxealEiKfmw1tE4v6mQIgM+ceeImgJOmvNhQl/cFUo8ImVhBr3dWBDV6KTEWYuYA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":17783398,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJivvxzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoyhxAAkE+y44u0+EIQoCyYYRU8mTAwtcc3DWs9UQnJ/F7CU7NDMFFR\r\nVKUPDTUZnec/mM+lUH06jnSZhyMChRPOGhCvFfvxPvD/7Wc3jehKkVgmX7ns\r\nPMMkSQSUNULo3O4PhaDj3Bo4Rs5QbYAqfsvNOe2ro0P19HdGHPK7Ggt817QO\r\ngaAdny1eSRK3Lz1wiHSmrwJJknEYadX3HE6APJUEWhzTMtat6+kglDo09PKo\r\nZdnIM49dm/Qu7EIQKwtJgzlDhm52T7obcJOvfaczw9TNkyCUjfRiYPfZDcpa\r\n4WlNN0rjg2vAmF5DaKXNgtXG0aKAAxM8Zzyz74xXeR+imbpmZuuPGzhvuV04\r\nHpIfo/4YAH55bA3gKoqUaq6mTyzcGu3GVbE9xuudEvijRwMUw0Mqw37XM/ov\r\nrn1RROTIsDl6jaHXAy+oqBup9acUQYrkU/N4gx0IqqeLMMV/ekbMDz9oyfyX\r\nnTeJtLw9V7z5pS+fadfhuZkoKKxkIIz/e1mmtH9XWnBnKZKyqA4809WpJ/M5\r\ntomX+g6rHjE5hvgIHV2Mrs7BJHofDnFLBRiwQfm2lUGY1jETZQyLsmq/5Ma8\r\nxGbzlT1l6O+5q+6Gy52ZAnQCqVQxmATHwKhh+8fXb1lomXEJNSKWdqAhDVDI\r\n/JFRm23YtRt2JyyTh/92f4fd51KWRwLdhpE=\r\n=7fSr\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.10","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.7","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.42_1656683635101_0.8676721165087751","host":"s3://npm-registry-packages"}},"2.0.0-dev.43":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.43","_id":"@keep-network/random-beacon@2.0.0-dev.43","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3a586ee76c9b1aa449112eec1dc2f11829d9cb58","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.43.tgz","fileCount":125,"integrity":"sha512-dtQMZtBysAMZ135+0QO0RTdAvfaWa9/6KULJ376OjjKg+cTlitIqAHRfx2/Z/j2gHJWhOt3R4FrxrIdjgSBjLA==","signatures":[{"sig":"MEUCIHUkJwqZ2NGuyUt+vZoh57qrt5+E3HrfQytVc6a9KKiRAiEAwMd2hNx+orhtMXJpPSlaO0ztyQg6quq4vAOF5QsLhqo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":18763116,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiw+wcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoTxg/+KTWjeeaC43eu7pkc2CpphLc77yrmX3C6I38Xv3DH2oj/puYj\r\nxTLQFU/8ZvAplECu07e0JCUhaguE+6roQvfjtPm1LS6G9q3kAq3rEewb9mSZ\r\nXoKcwHK7a+Ppvka6h1l+K5vNqcoAsQEEtC7nyiNzaHHUIsbF3G2ruD6aUZ6d\r\nt7RPaW8g4+iAxErzlS0zW44UcpNfNe1+Tjz7uJfu0fpjeMJrE8fkINxhHnP+\r\n1E5WnURnfZD7KH1qCPHwsTqWcISZ0pLAzwb9wo0Q4rQxrRDCsriX2Qd2VRov\r\n318C+VqsenxTTvPRG9kR29Q0YuMdFjH6z1Lhmj3dTsq22/ddU4dojZj5akRd\r\ndYDO5qPODBSEabXjGNc6UB6x9JleD7YexWVlsDvZZ/FHbPMkUw2OKgKW2CW1\r\njUG9mQp58r+NBjDZQTejSMA1THsPUNvB5iA0f+O2ldTO907mZ1ImKYAZdbKk\r\nRq60a9RK7rUZiSoDQz1uXAqqsudOYtxatyYI4Jk8u4WfT0ZHTzapNafVdEAp\r\nhZrjKuNNK6shgK/VqIlJbFUx8aaBpLZGdndb9JTpM85SIUupcZ5pdjKVffHW\r\n0ADuhq24SUR39eJUDwhUIMbNqnydtnepEf6pTgOP0lZ6FaEpm1lOuEaCfgdo\r\neoQ8VY0f2mQDWLlOPcIRq0j3557Agfc7zQ0=\r\n=R4aX\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.10","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.7","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.43_1657007131907_0.5623956749094141","host":"s3://npm-registry-packages"}},"2.0.0-goerli.0":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.0","_id":"@keep-network/random-beacon@2.0.0-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"933dab809309a87734835e818fe8ef22f96830ae","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.0.tgz","fileCount":227,"integrity":"sha512-M1RSjgBP/V65clWL+Kn8UaTZBxhthWwoCbxGk0Nw9OU1Qc/59w05XpowHY2VJPe22MZVM0FgVGSDeYNnBthUEA==","signatures":[{"sig":"MEUCIQDVJp0LhemOBs0Bl3EhStdwGTsLG5zBubfGSVWfDJk1MQIgRCjjWk79wOcSLVTRZkZkU1vrAPO9guQTzWMgxBM0YeI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19116993,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJixW1JACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGrA//Sb52CLmVxTAUJQ22Prr7HsngJ8pOXdh1qqOsh1/iK64K2c5W\r\nRM7uer/ts7knqvzQ8vjJwkbJUaSRpIXR0QhgTIWkhg7fw9fuaLINIU3DbLYU\r\ngo1zpiz7fhls9U1VT1rszG/dGMMgNYnUTaHfNT8mAnzvC9N2hdwosM56OKb1\r\nUIWJmL6AdwvofJsJBfLjsWMxCxxKe/B0+W0l6oaKsgJ+lN8fgUFBhvvuqYAr\r\nxP0nIacax++wTZXSQnmeR//o8cpa/iwNC/m8EogZxhbaoQwDonrsEm7Uv6rv\r\n7FgmbkgmTInaWQ2S1YzOYY7+2W+PMbBSpl8hIpI0RSkFj6wQC/82DIWf+zRp\r\nfS4PaGeIXfj9bmgju3nJ1aOmLObkzebPTtvKmt8bNgOcOgMDfmW4zPnoY+og\r\nPjMXP+5SpPmHCQMszhIuQCWx5IE/UG9dsU7zOA8CKfatyPjzeVp8C/jM1K4f\r\nEd0JZ3MrOc+wZfsHqWPLsre01CcZlvR5w/94R8OJdvERro3LCoSPqHEmQ8Pm\r\nXJdHW6NHK65D5tJwdiZ/iG62DaAKv8jolOwput4JQ6AHxWO7UcpuAq6rvczu\r\nQsSU1Q1wtiw3yZmag9SnfOn1hGaCttM7gyJlbY5+xO9OgjP7vTcKxHDaIHUn\r\nXQKIkkJ3eKX2UIi/TsYnFFew6WAoJko4VK8=\r\n=g6pF\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"gitHead":"c6e6c6e07fbc699d767d931392a050e6aa961407","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ deployments/development/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.13.1","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"goerli"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.0_1657105737321_0.33740344926771204","host":"s3://npm-registry-packages"}},"2.0.0-dev.44":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.44","_id":"@keep-network/random-beacon@2.0.0-dev.44","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a3e02cacfdb2cbfb5015816c92eb4a9b271ffdfa","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.44.tgz","fileCount":149,"integrity":"sha512-xQk9rz1W3fAXeta+QJDhCetANCo94rAK6Kz7V0+9UR6vtmQ6W8pNyRsI/DM5LDGpy+8s7gKmVAFjdfDaqQhxUg==","signatures":[{"sig":"MEQCIFaGMqw9pI9DtTtiQuNvxrM3+JSvAxkYcLsXmdiMBo1+AiB2qOoPjKBLPxMJIiriGFFzoUzpJkteOmDNkJNqzVyMzA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24202223,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJixYPHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmopaQ/5AJqvIXaQepJRvGl8QhkjLhY0uqxdEpM7AcSFABBpn52yBsWO\r\n4OXvmuBdpYFIymYHVpwF3vFbl4oP+5W4qG3HYvSBrNPRm/6PU4KB9QL9f8Vw\r\nuekB01ajm/7LJ0z+B/y1gaPq2pikydGBuLtNbsbmMUhErJZeVrWa492QoCyN\r\n39XAjCGr05US28XgClZNn6cxGv8sSWSUY+TbnzHISAz3DneHQd0GSlXBq2hq\r\nbO/dXcKvYmc21UAisc4lz5HfavQvl+E2IazSfvrbHTkUsnDe36xxutdBGMOl\r\n69Q6GG378+evSM6a5W3FkeRdXxcvLPaKMO770bFyAa+zTwTI0T7WvKu+CBxE\r\nhU185gbu5DsV0inUXoZPBdLK7i1RTMGQ2Jz/bkbsMiqfmmFAMTxk6Kzc1tjI\r\nYnFPncaVogVZQjDs6rg2eUDGLhZmjf36HOf6nxXDnEKvEtqsXzbTpbvXgvpW\r\nsX3mASWKTklpXvw4CQp3dDTLIL4bxKFsuALXS3O92Z4D/qEsotEaJAxwl/CT\r\nRikYzbe2EjdAb3B37mw0Yg/JTAt98wykFw4HruU+714O9CKCnpiwM+xK6gCw\r\nTJJj6OZol0mnUy7U51Ior14gWOzO/pOQS4vUwbWyJ+UM/IpAjnIUriK1gKKn\r\n0CW7Pxr0Kj5jkVKEwEOOTUaFbfnQ9McC1AI=\r\n=VWgu\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.10","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.44_1657111494977_0.8831844628525589","host":"s3://npm-registry-packages"}},"2.0.0-dev.45":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.45","_id":"@keep-network/random-beacon@2.0.0-dev.45","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a1c586ff191e1f1ff2e9891b0519bb47af207006","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.45.tgz","fileCount":149,"integrity":"sha512-IKrSVWgb9ypI6gPuzv6v/H+DH7/sdJiH4ux/wZy+IbJKEa+JYQNMCbyeghhQTlERqy/hAt5+x8utsXGc3gK5Cw==","signatures":[{"sig":"MEUCIQCjpurwGLhpXhad981lgxeiXT3XzRImVXl4uRMwTPWKaAIgdv2aLz7LaAULRGNA0UtJ//xp/0kbtFOvYllO+XHSFOQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24189973,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJixryFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmquVw//Qm5Fn1yhcfhmZymzSGGqyHCzZsQlafaedUdbCw5E4aoCrCZl\r\nbnVWQvxGTPeSBox4HKLWYpWxXLgjY/i5cg/9WaJGYm3uAWSu+gPNWN8UN9Jr\r\nTsZBxuqdoTuGy20oNi8ojzPwLVvyvyNBJQn35y96Fj17D/okcUSUdvhGlcSw\r\niLOxkhikJteTDx/1FM3fxmRUkeoTbIaokweFOW7vweVozflArgaFE8mxZcKw\r\nEgUtIuRiXEjHKTw6tDrVxdgMANy0ptMKr9a0mPNgtRAOrtgWjleGxSDcnzvd\r\nOqkXl1b32mA+MTk6u4u4A0G3qgg5dI81c4XPAu8GGkAOmWgczocxa6ws17fN\r\nNI8g2+gxpwOhgj9tyz7UIxsOtrJ75HZZ+2OE0pzd/UjPXCV1FXBjsP0kBkXb\r\nu+JDDET+1odNy4/uAtz8FyM4/z3JNuE7lKISQZo6fMkDDJQnmTzuWEvMYOS/\r\nPzz2EvZ8ahTre4ojH/rw25l9nucBC7lHJdUqlgBmVjfS2mt7DPOZ1lFBgsHj\r\nGQ+PbjtNYP7pr/+BmNXk4hq0RzQwb61IPCJ706919uxeb7MmmOyTWd5bqVpv\r\nGdj81DhMC/S3KPkTvVkpDViFDvDZG55b+x8PsFCNONdS/kQX4e0DrqEvkG9O\r\n3ewGhsVCPhTUDB2/iSvMuGhW45Cw6CdOYPQ=\r\n=vfhd\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.10","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.45_1657191556941_0.38941030610495764","host":"s3://npm-registry-packages"}},"2.0.0-dev.46":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.46","_id":"@keep-network/random-beacon@2.0.0-dev.46","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"de7f5caf48c8c147f661eca3a91e0b646e37e82a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.46.tgz","fileCount":149,"integrity":"sha512-KsYBHD27rpe8RI2JuGMhBavHFkYqrJq+/AdvpI6kEQUuS7JOEuNMbtAmFMWa5d8UdyoX9hGAwz718RKxTwf9eg==","signatures":[{"sig":"MEQCIBRjrVyrh6jMhWwgmEn9lkse/1CTtb1aI87LS7BddVwZAiBnLdZ8d5fDX9iHtvKIwyh5zCGI/B3vPgGNKs9A3FFIcA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24189975,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJix/aKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZwBAAhjN7SW1E1GELEDnjxfl1SCRVOi4fQlWG9jXemUVNHJoJtbPS\r\nkDKol3V11/JL6FwtEaYUAXr9vLEt6iF+wrk/gLa/dJ7Q8ceAtHANLSHCBpxS\r\nV8OwYX4Xo9qkU1pzlwkdhCjdD/t+uXnWS8f+oWMco8f4F8fwZT/3vlLOAJF8\r\nDzDd781BcI6vuemy8BG+l/Dq8d85b/2xyLFAedMTEAT2K8HA3TSx19sf7sPW\r\nC5afNPBcdHdlNaKVmhsagD6KGOkYOICqngi1Ye6JfHYiqtGTyUVmc2l6/obd\r\nczA36CHl8ZK/dHS0/d7WX1qC1xagv3XwZ0xqetrdeX58gcLbvVt9rTGQIiKN\r\nAF3kNC/b21wztatUm756lADz3QsqM2YlFHa98ZSJjbR/TuozN5MWf0OEFNjk\r\n6/ZK14fjMcEAXMpdvY3lkm2TqLd0JRnla0Sc76WME9lH8aSBWNE0b7ueJHrc\r\nu6whwvUT2/Rcl+RzNrUhDya64E5mjme+jhFxEgpxeGpKOHIfkChthE37rmsB\r\nkiwRwmVqPm8+kO0WV9HY/1P5JQEnSWBVXXJcQT1pLy6f6ZyRQIYKlxrZNTpL\r\ne+B/gqtDkf1VVcWZgrKuqatQLL3q0OpXG4YXOfwo2iA00wBzYwSXB4oll5rp\r\ncsjl8IPJKMewzaHca9mIUPQww/mow1Fy9nU=\r\n=lkvd\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.6.4","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.10","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.46_1657271946089_0.6567298243743851","host":"s3://npm-registry-packages"}},"2.0.0-dev.47":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.47","_id":"@keep-network/random-beacon@2.0.0-dev.47","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c04ceb34344bdfab3411cdd9f1f4f1a72b33de4d","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.47.tgz","fileCount":149,"integrity":"sha512-PjsDQXzdQIUTX9mm48BYMI9I2imSH2JtmjO8JE/qfStsP3mldENMjLtQsvCgAaO010SguzKyMaHTC5L0T2kf3w==","signatures":[{"sig":"MEYCIQC570caCzMOlSqmOnV637/u/AFbCXyErhEx7HRWx+1TSQIhALGtnmy85qEuWjWfZhFi0PsvkxYYm9D3ZmlDHpPHffkG","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24243058,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiyCKgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpWSQ/9En3L3cxI9riD6kMXajDhCmVEE/evwYX5UW24Hcd5bXOjaMNj\r\nFKJVe5rXX3J6dIS51g2xTHbBnqpbF2i6/JNHBhz13yOYTEMOayYn9VUke1Im\r\npahL6jpdyxEMqhBBoKBYYVl5KDZCpS6KE57B0zKWwGI57IP1ZzqkQq9uHYpA\r\nNghVLpMajKTUO/u4I6au1Uoc+hMzGHr8Lnk6uby6x/ay7zHuLtld+UuANGcR\r\nXgTMrOGvr+F7T8zO2y0ILyuxc6JS/x70pbtRThQkDZ9xF8BwuN7pEy0DprVV\r\nxqNZPAU/9zvtBP/COKEWkQhofSdOFSSEjC0DMAkEP4WXxTt5Xm7smDeJti01\r\njEgL1zu/GbhDtD3wOCV5DlqYnll93XDQQoz16SopRXUITpCX+xWKdkSp3Ub2\r\nPkdle3WIb/JhvHCVT88UrwwccgqhzwV5yc4NceEfAThqoWfgCpVnlNL2Q6iI\r\n1SpfxK8nX0K2yi4NghBJW8ggDcmFHffRDNFikA1RiveeBFyJ/OOS3+weAD+L\r\nEVWUjS2SI7+yBB0mN6EcmouQtPsgYjDO/C4Vo9ilfQIJkbkRhRfI4NqPJ4Pb\r\ngO8b5oRXY6UXEvZ+KNNKaM47GoiDOSW2MMEgDybpF8W/JHUO+uAoC30UGfqq\r\n63BnFyxtDYJLSSvFmIwNGHZPw5wzpSeF8PU=\r\n=GIhz\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.47_1657283232135_0.31957877236405796","host":"s3://npm-registry-packages"}},"2.0.0-dev.48":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.48","_id":"@keep-network/random-beacon@2.0.0-dev.48","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"43cd8f7eb2181af47a52b6235f9d38b417bdd1c6","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.48.tgz","fileCount":150,"integrity":"sha512-kv1LM9VLkHWrMWwIZSVteI3Yr7jKqc51QtbRIwW4ilQacLdbFeSp0Lxtm4ZY8PA5rVtPsHANroaiUGpiPiHshg==","signatures":[{"sig":"MEUCIAzNIZfaOsXash9xYO/iVc9TDfWHuhI3P4BfTRRdmO7jAiEAo6IQhkk0KNTaL1eDPSjxzrHe189akpp5OjEJOJv+wlk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24247090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiy9PbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqvQw/9H8Hf0ecJAf8Qk3D80vHADCvuSCNCOfrzqfHcge7X5hoojPRp\r\nLIYJEIcU4+2KpUGgCDahi7ZnQ/KqwG9OmnGOCzWQeCL/fZyfvbvtLYK2/n+B\r\nVZr4J6phP72TfmJXeArDGP7GeiLG+MG0EYJJZNQAHj1tBhvj3Zxckj50wfq7\r\nAvmqn17H178BwzHG5i4p5A/eGc7QmW50ou0EyU7g2DBoqbae9vVga5abFRIk\r\nfHNl3WHA2DnZlAhk1HrXsWOsVV7D1sGAVefvXiqKru1Q7/QXgkgIL+O5sKUt\r\ncUfgM6Jfunmz+Z7PnNCNZ7OToczGt/hfCfzx6+IWtMMqxBg7sA4RIvdLOWMG\r\nSq3l27Szh0eSTbIs6djxPzkUHN1On6j4YnOGQOFfS++09iJ7k9VnfEIThkMT\r\noodDRPjiBPXRMhDepvcXBSikdUz35HfuwOmcWobyCUPocFppOBJgAuYZHhha\r\nDqPG0UldW4xPjYuXRr3VAOZwcyxdtWYpYWmPah4arX0+iaoLAzHtSYjN80ik\r\ni4EQbR6Vb9dowVBSb0SUfnUyiLtVNmLMIGhcZbdwkMkk3q+h/OVseqUYll6B\r\nN0LwSsvFfBqPJd/ulIFjW+Ppuoxaj7owbJUWZbGyJL8ITd7Y7Pn7W8mZWQ1t\r\n3qosxlpmP6XksBEc7VnJW0PK6PxoCLpprbI=\r\n=JlmK\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.48_1657525211560_0.30514183534420924","host":"s3://npm-registry-packages"}},"2.0.0-dev.49":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.49","_id":"@keep-network/random-beacon@2.0.0-dev.49","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ec8830772dfc4a441b3f98d2b2fe6289125c96b5","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.49.tgz","fileCount":150,"integrity":"sha512-B//Goc4GUOD8XMuMwl6jvXSwXlmUQ33p00ApUosz05N/YJapVXeXNHO4/hbgMwsnk9SSzNwu4YLiYKNRXdVjBQ==","signatures":[{"sig":"MEYCIQCTfThL/AZIrWx2MBre71PhiXeSTfTzQjEUW3y3+DCx0QIhAOAuNLRHcQB9yagyVE1qkXfPCHx5N5HaZ9AT72gvD6xq","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24247089,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiy+47ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpoJw/9EgT6SaDxY7cPRxj885LaTWMoKvXbU5qmwj0XJx8WcMK7p5K/\r\n7ODs8wloQie8pHLQkaM6xryc7J99omN/xmJaRx9GtUKBInX0E1gdGxGWgT9w\r\nbgn10k7j6hVmTY0kreVvwr1hfoP8dh+yYXdsi6GJleafGCU8v6dgSDnuyXT1\r\nFW4PBPA31NaRX98Cft8DDjfLDF1EWloA5qtX810WlXGrpLKAkJnQMZ89jIbc\r\neWO8IDH7p4Da84sw17CPpOk6XMg/mEsh/CMGSwN7LndPEhyMhOhGryr98UA3\r\nep0dlIy1b3gL4h7pSc1R3HxjuB8E6D4RxahLA4IgmclTY0D7NP2FBxgmVadI\r\ngh+ueI29iPERmYiaRuIH6xVwzjAGnSx14Mizu5EadLZzzkOleWIwXwvJODEZ\r\nik9NYESx/+efeY5Tow5Tv27UHqxTl88bnftxlbS9LqKq3T6aoZZhp8cp96RN\r\ndSvuM8Mz/ZtoA9AoBtKOBAh026vop0QgwTejGSoxCXN6w2K2U3Khq8JjVsrD\r\nm0NDwVI3lrHwzQFOTcFFclDhmvpg9QQ8zrWppOTTU3/D00aSkpHRsmw0pOb3\r\n/Y9WTgYW1qUaOSbOmS2TUxEOY96eYjX+E+ANlXyh8kewjmCgNY7u8/PFcwVq\r\nhXJdNcWlQ+E6HUtgYZcxepqPptX6xHkyCfs=\r\n=jJZx\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.49_1657531963049_0.802730665467225","host":"s3://npm-registry-packages"}},"2.0.0-dev.50":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.50","_id":"@keep-network/random-beacon@2.0.0-dev.50","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2cb6d1e42a54feeb184442fd047cab59b14713fd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.50.tgz","fileCount":150,"integrity":"sha512-/NtQtVksfS8EFJSbm6mxb2WMUqn/uh9JdZ7y61Fgqoo8ooHHeGnlgYhwfHo7ijg2xaD8Opk/y9mVdnb3m0MHoQ==","signatures":[{"sig":"MEQCIDsmmRfzPqHTepfmlFw7GZd2cAC7IVdILmWmVvysQ8jaAiBfOkGwputUJnkkJfJiQsntNOKsABKdsTi9DSkUJKoTyg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24221765,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi0DkqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrVsw/+J4mD83RpecsK6z0CnxGG1YdcWuscU6WAqhBRS/CQcssSiBCM\r\n61BEZkhcNOsVLW2t/YX0n64qphmirJEYQYs/Fm7D0SEuOfvQvuqut9hIS8sZ\r\nsA3zykY3++RHCdZeAWQmdHr3OZBYeyamtPUJgPjr4VjqjuMhfLjAzGklI4gI\r\nkEQioBO5F2UVNGJvFJ1wEA+nM5f3Y9/zE2+Bpy+ZGyhf0TzfwOjL1AlA3vW7\r\nUXBAVKZVWXir1Nl+DCn0WJyElYNVnuf0ytSJEJVEmlqxEHoLAtiDnfxbzKMn\r\nKO8SB+IYo4bKfSvidlASTaM+7akszTVzGhpU+2pVLlopz8c5QqhMxYo/byKb\r\ntU2abUZ+YAAQ6XuEF7wENXERxpwLx+S7iHfUcrVxOj7AsJ1Ah8r3OA4AX+1b\r\nFVNtu49Q/uQpy4jTABtAZA7TeZ7gIIuUGg+hHAAn7eY6f/K/Z7SG+KEsbpc8\r\nPEgu+wQRWu05qT8LyQY1JYVvFUttQalF9ZZO9YQytWlGeNcHUqQtEhJos35F\r\nA1filP64lVVb8VJuqq9gOcVvLJn8ODEMKLuFpPR0RN44iNGXrNB73Hru3bA2\r\nxBS1wgh6ttbaR+k6KyOsZYL8xCdC2Vr4xgd32PCHjnOD+ueQDSrKaHtF7CjV\r\n6AUk8679wmgZDp2T7fPCasIXL+ehDkf4Dmk=\r\n=cF7/\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.50_1657813290529_0.17137776315596076","host":"s3://npm-registry-packages"}},"2.0.0-dev.51":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.51","_id":"@keep-network/random-beacon@2.0.0-dev.51","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1ad1e7a4e330eb459cbda8aa9d9d4b3492c989fd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.51.tgz","fileCount":150,"integrity":"sha512-cZSsZVbkKn3ZM2mXPr9MifvOI3g/oZB5E/+8zTLC3i3PksABjZCOH8qHfFuionYK6V1ao/2QeZzC1B9ZCQ4oIg==","signatures":[{"sig":"MEUCIQD3x4opQp8nKm0Mswhc3shQIIt755SNFjlQxVcRG1kPPQIgA/DqckWMrFQJnTW3L1O3Y4sGBAG2mgUK+Pq0jsjSZVg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24422401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi3rcyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpeIw//SSCkXsDO9/qehc0kk/AWxHoOi5sHr7xUOVh7boZ+4OiepNnI\r\n1BGbfsHlp3pSC57B9iDwxxZ79E5nB2GUuWjs7TjVDtXk5eQVeMOIEma1GSEY\r\nIeijW+BTUw5vglIEfYfsN1GXbcP3vxBbrGEX+75m23s80WGeX/llI6ylizSK\r\nqrzJBgZWQRPkqmh6z96GPNKZPa42d58w+QGsUmrkUZpr68etuabgQqW9A7t7\r\ndGZ5sVfUC+X99lQGXuqW9Aavf64fhpYtvKA9kpSUKPJHZ5XywRNwUneHPqL5\r\n/sfDsy8EfDQDaRa6Np6Rdj1dGBEQrW3l4BJVh5SpPYtT/iG2x3z9OXw0RDBh\r\nsCPhEpLOXU3OwmFEd3GsTBSR06Y66VVYGmqd7E4WphAFAlVgQ3m4dQxsyVsl\r\nplfbCmSxHyhufNJxmqRDgDF6LDhlQRezChsFAMpV3aV0fhKjM8b4OnkmXXHq\r\n8H1OHNnt99njiaT3N25wL6r1p40c8aa5D4cWjVB5gDDwH7UyZJCiAc68cRta\r\nE1m8N+EnOfzAx5TBRNYVyOavXi9Hr5tuwTbGnXPSkE/PBGotYzX9ugOG59rg\r\n1KUdIdm89qKQyHCVWz9QZhTqbsQ9OW6qlsypgO/zSMcS/R8tTA1p9YC2gP3A\r\nTgm+9mB2nLWA3aS2Krhkx0PHOqmvkM/iw8c=\r\n=dTOS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.51_1658763057568_0.09407019302140163","host":"s3://npm-registry-packages"}},"2.0.0-dev.52":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.52","_id":"@keep-network/random-beacon@2.0.0-dev.52","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8d0fdc95e66e4848280a28d4d0c91f09b2dc51ae","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.52.tgz","fileCount":150,"integrity":"sha512-mTwKRNdsxs9k7COYDZptrgKcYjSa9sdD680T4ABwTKk9pP/uByo3X797j615eByTedfjdOyXksZdxSLT8UBw/g==","signatures":[{"sig":"MEUCIQCvXrgVBUpG5w6+SpNUQTZ0ZMaza2/JyJ6KD+DVsDIRogIgIZ2r2gTQvd0Z1bJ6rUuNxzegv+MCrLDWrzceJ0niTT4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24422401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi3rd3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqJjA/+M+65qkeephxwgAe1uYWpT7uNTzxQJFTY3y5SeTOE/LLzzu95\r\nb7BXQ6tzyqkltNll0FMEsSk5SOtV84NnyaUDLlHD3yBsWzD56izCZXJ00iNq\r\n2AYCGT6bwV6NytzUfHlUBE2i9GFeNfUcdau9sOeFrauxmfhIV90V86i8cbSl\r\nCL5wc0lph7q91U2U369dlTQy6NWFg+eAIoo8swJRfZlDSvdjFbaT+FP3iuKc\r\nns8WFfSqIamAIQRUP+QnHxBk/OXJsx3wWZeS57w74F1GVQ4ng7wOi2ORCesa\r\nvvV3A9/apsPVmg4x9fT0gA6W2x4gj2Dcg3RVwdtB2I4+cAu52LX1ByQU0gZj\r\nTwIkc/bFm6ILuJsunFPKKW3gsNhcaL5YMBM60N9IZ5kr5pqK1kHQ1+axUylt\r\nJxY4CktkpA2pzzrWiWPf5gU9xfdeMh/Ot6dwAcisVspIjZ14or9CyvIDrN0a\r\n9+Vgqh8J1HZ8HW0JhRiDGX7dkxa7HwYj5PJ2Q95WsBD6HShSvh1iHQ52io2l\r\n09WpsDABQdR4soIwbYzib4h+jKDWZCOR+JfuP1G6unduxfNpBeaauNpzpsjE\r\noZ1VWxcRG7pgmMWCSs/PVEfvWwzMG4IVONkVsN0P7kWfHP4u3/pFpZwOD5lT\r\nqnA1aBA36vQ7TuywglfoxP06VA7KB/YoR8U=\r\n=EdXb\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.52_1658763127018_0.6506912111609198","host":"s3://npm-registry-packages"}},"2.0.0-dev.53":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.53","_id":"@keep-network/random-beacon@2.0.0-dev.53","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c601bef0e8e18bee885731ed9d2c2c78374d1fcd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.53.tgz","fileCount":150,"integrity":"sha512-mKw2Q9RnxOpEhbU0zwsNSZIRubWJbdbq/XCpntZPWq4GdpNP4H5QOr0uYcyyq/B6+90F/uF+XTI+EI827vllNg==","signatures":[{"sig":"MEYCIQDVbKQaJ05A/WmVGNUz7NlNe5CLZviSB8KXHQ+sV+AxdQIhAIlErOGp33WQo7K76Onnmi7a8uaw9ScV1Rzzsb3s4lYw","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24444310,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi4kyHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqhnA/+LtaN82E9/VjykycH2S4MCXp/quMjOneg79SIAa5KR+yd1xyu\r\nd+DdjYb7D8Sm+zBtMa8Vb0/tFxbEzzU6cohTP2HsXGQjlh85JWw0bdC7nsIC\r\n1pskH9U9IgnfgDQIa1h0Na+cXjCXPiR/m+KsekWyHMntA7cin16sw6ksjyNz\r\nyCKhcfw2aNErdMsR9/WxeSX6TYodhG6s7j3wJ9FdDDB/ekCXiJ+HIlY0LMYN\r\n/OtAvVyxJlyA3g26kuCJ083MMpDsvfmIS0hs764gO3eAjlqZ0iHW0VyJ2OV+\r\nqLyK2rjiMhmT5QrWinXV5cblLb3OciHQV2FmfqQVXEtKA1gYDsPnzn57cAm6\r\na5wUF320NGKHjpiZQcQDAO0EoJer4Q89OQh+/+E1aKKjNYkB/vUiNCgIbsMQ\r\nVu28f4Dm+CfQ8O1djxjNxMINKb1AeMb4T4nITivLEpMRG702sx7qNn5WAKr3\r\nbndSO8YMTtkEHysqAF5p4/gvNUb8BT8CzO7j7J7f8SXMxE1qq0SkK7VahaAt\r\na9BHWzYodGLNaiTqg76MkOOuPwYAaJZwchSzZBrrFhytklv+ewbYYFyZulVG\r\n3ySbMr9uKq7WNGn4BcAWqbIiSmAtfgbZYTrfZdixQ6yORbEEtHy2zzql+/+d\r\nGES7hV32xlwQDNSkOxSD2xb+FVaMiPW+Tjo=\r\n=Nmwg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.53_1658997894756_0.5918937873363372","host":"s3://npm-registry-packages"}},"2.0.0-dev.54":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.54","_id":"@keep-network/random-beacon@2.0.0-dev.54","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f0ad8d148da0e3ccafe359081214830753a93e59","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.54.tgz","fileCount":150,"integrity":"sha512-bO+UJR796q3WWWmP/osf2UoMJzBDjdRnzNN0R9YuGd46MTXykdIh7RRcT6Mo4sWX21FmifTk8OS5QK/KrS6E0Q==","signatures":[{"sig":"MEQCIClucEen5D/ZYDy8WYykbjd0y2124lOLyGeAPfpANOKTAiA0sSQGz1irHAC1q/4v+nAss4NV2iaLD7Tykak+Fr8vnw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24444551,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi56cSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqnMg//T1BnOjXKyQIbeFYHBthoUh4LAb3eiC62wlWm7I6Gn8ZBsKC3\r\nLiQHT0QUMvM269f/w2tfoNJGCGbn3dpdztyVrvUGRyOlCskwT2d+4QHzwLmt\r\nOTfieDwvJCOLssg9zvR4iY188Pm/kBgQpvDezmxBNsjSTJ/tFPPAkh9gNDXG\r\n5MideRlF/1e5aHcX9h/iq2jIRpeSisjiWlM3713njGyjPlT3RTuJVmKHQoEd\r\n3d+c/C2G+SJztd/JJr3Lq4HmgFC68OlZtEzb8EpGeWP8xRIdPzDGBz+slrLd\r\nvUetyhFA6Dur8+9gvDB/NAruzKAdj+xB/c0UIYRv391qlzG2556xf2gM7HQH\r\nTVeTlR6P9LrqG+YSwO1QkpW2CNc/vD8q0NrW1p3dZe91YkUu48bVk/GvE95c\r\nu0JUONYtHePqoom1rtUKzK4XLwzzhxZ80YilN50ex6w+Z8DAf531RwrRSbHs\r\nly+UKRV3mh5JW49zwHmG84WIZzfESny9XaTYXT/+iLhJKM8u3FEDi5mQI+Ab\r\nPRXhnAhkalCL3+X+LZGSOQQD9pjSuCkzY81Qw5NVuPjTAPOLDRYRouShuF6y\r\nDQ+bXsqLcMKkwbb+kF41pqUB/gXCB47OYuWXI1jRygmsUr/CChQw1JLt/ORE\r\nJwQIpD9LJNr9sAr57UoyUVPdFdXM+46PoVA=\r\n=Fsps\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.54_1659348753733_0.37397821162049416","host":"s3://npm-registry-packages"}},"2.0.0-dev.55":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.55","_id":"@keep-network/random-beacon@2.0.0-dev.55","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7a7c9b07cd5df56cfb447203043d78e990be7054","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.55.tgz","fileCount":163,"integrity":"sha512-wOMKHVdfIH2hDwcizCBZnHTXG0znUiqpxjIppPRTG6aPpF3Vyi5paCDSUEa4XqOYUT+Z19p0ovuE6ycV55HOew==","signatures":[{"sig":"MEYCIQC8ib5hcvQMxEmSGZ7h0kiVPt4duRKWYjdZ+fcF/xmKtAIhAK7SP2qsxVh8fiWoTMCqC0mryC9IcFR7iK8xrRk/jlJE","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24148804,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6noAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpZ+A/+KvaTrXMld4ONlkOWbrapi0qlwas678Gs/U5KT6L0p7j5p0pI\r\nqwumDmPh07ONa+ecLHsTlMdHcUpaB8rwqyLJ489MmXRqicEuWai+58ynG3Iy\r\n3ELKMkPR8S/O/Bsg4ltaU0rQ6l7r1xGuuACsT4lqcAz9AfnRimghZV9qB74j\r\nbEkzURIhh4hqJ94rUrzDgpPqHooeLEsq0fC6ga8/PDiPdQpsgopaAhPGULB4\r\n2D8qfHcBukpAECG3zhoPfXLykMcgdbGbRIeaY0DpniQs6YNKuucI8gW2K5Hm\r\nrClIcriTc1c5oaKT21OYHeVLTHEZT1yt60x06NY1m4zOFzkWJM8gGWRuwAzQ\r\nZPWzKbvoiIWwKF3f/LSheoCxln8AB3knFrFLqX23UOOo89/iL6gsjvzoGhvF\r\nkDNBLtWnpJTQ4yUY3mmvjfKmMtBI0YPAIab4qmS5YrsY1s1MW+q/1F5Zy6WR\r\nOpkvTVVrCJQlOb6ljSqUs5bTd30EPxnd8F7zzIo0JccThuZbEwKPln1JrXr6\r\nLjqzRWcNazt2qK5Z41SPPGZ+SVYMUmuMc99D6xF50ecqk56+bsJs+RwsVJNS\r\nRniRr+H7Sr8hl84rtlKGwjIm/FscBsDYbSLhuNA1JciwlJTu1aDSXw9ecvmA\r\nDymITrGG3+QK66zCwl1I6yo3j7i7/dMJSgs=\r\n=FztZ\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.55_1659533824487_0.5746593466043122","host":"s3://npm-registry-packages"}},"2.0.0-dev.56":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.56","_id":"@keep-network/random-beacon@2.0.0-dev.56","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"70999781d6fa93e67b47e953472fa72f6f312455","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.56.tgz","fileCount":163,"integrity":"sha512-eoqwSFeFIoR2NI0/5TutqRHHom91nJtoCNtdPMGK1eqH+Jo6SAZh9jAbz9KIEoX0bnhOoMQhyJdbdOv3Nr4S7w==","signatures":[{"sig":"MEUCIQD/Me8LzKuVqq8ChdP069a5bFnUNOrUd7HzvpqMOEthawIgXhaM26xYoAsf20x6KyjflvhkCAV9xPTwPsr2Dl9g2To=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24150289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi677YACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo6qQ/9FzORnA57zV1kJs1kmQqwg9rMbdprTPDu8ApKltcH55zWuQUf\r\nEtidtJWA09kdNx2ISrOK3YhZZIO80SuIKNHFjcWX/qKuhBRdKpHXMRERUuqg\r\ndIrC4HGvuHs4U2oyM6n+GoMVWYiiCad/FQzxb7gcMgSxxsHQ86+zQqXT3yH2\r\nfIE8IUBc+QiqNoF0V5AipVXG+F/d5wI8NC3esg8zQ3W8USNXeA46lozlxUC4\r\nQemGm6CVNYvO9g/f0QfxWsHx30HkJ1PtIF1smIn+0NFA6ncbb+Ei5oW0SCls\r\n/m2uHgr7NXFbHRlXSFPCLowmoAUVYyGfckEkC3uyCfVnqhfSWbnuGWTRSQHc\r\nKn0SxsHKql+vS77ufMxrQadeyIYUwclCsDo6w9V5OzYwawaesXNSf9++7OLF\r\ntgtw0m6loR7XJ+9rtOfZppcjhUlAIjaGjXeRMpk2ME2mH18mCRwwEvNxkmPz\r\njHlgeK2ZIbsLUucGQkrUtrhO4HU0VlJyicFmcp7VglQ9PbB1gdIUFaiRXDBx\r\n+ZGrC9hXr+qji/oQNeV7xyhPxHX9cRDG1D8LP5lt+Vda7j+O4TsiImZ/wOz4\r\n/oX4TcYX1Tlj6FHY7i0OT/LBk4lexS14Lil9qV71CSWvP5IYF7t+2iZHH9R7\r\n/XMWS5s+LopvMTO0pC/hCCZC2odK6cv0h28=\r\n=qDC/\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.56_1659616984555_0.7621533905674802","host":"s3://npm-registry-packages"}},"2.0.0-goerli.1":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.1","_id":"@keep-network/random-beacon@2.0.0-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"419ce85cf3ba860693e7b048a1f1066e4317122c","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.1.tgz","fileCount":152,"integrity":"sha512-dlKP4hep9qAcozla8gKnpe/nuKVDWPhg6y+ykLdk1OjrNUkowZ34HeFHRs4VoZLbfTl7bikx1t9sbzMCVCxJFw==","signatures":[{"sig":"MEYCIQCGFXOJH/b1cQeS4qJxzAZjp167F04vfusnT9L2osZqHwIhAIDeq0BRrN532zGRp3Vus3uZ5vii9pO8bE4yTMrvbf1+","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22314383,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6+PTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoVVBAAoZ4KSewTeKpVm/Yl7vrfywkdpHGX5owytp+JBPv6TMjJbbjs\r\nNHhT3rRBRkwecyqYcwMqNcyFxpkz3iddIwA7QS1AgK6Vf168hqbJeRMF+p/6\r\n0bk1athKqt7EZ5FbIy4fw/alECQvHBboOyMnzp/17fwbZ57dro+1cRBpSRBJ\r\n2Hzx3EMY6YIi2QKJLQ7dkBkEg3txldRm/mDsbccV13sFqOCJmozlp04cPX4v\r\nIO2tBLcH6XyfDfw/OqnE07yo3vowtaSrB/k57CZSCCYLpmIS0bfGdNb2Za29\r\nlMuRc9E0lFEJCbve4EzQaeq2Rq9fTI46LUGqVsnACFaA+B5hX2pkFUxkq9vd\r\nULWeL/T1me5LtZYQcrnWkTkseO0a4h8Y6CiPQdaIi+kH8npbhV33eiMFZEOb\r\nK4yi94NT2plpK3qHGOtOmbNnuRGnBTpaH0MWlUsPYUmaBMNfy71P/Edcolfr\r\nkpUJXR29y8gOVXiuYJtdu4o1smY6mmOyaBxjLoIG5ePyrSQ2EQKAy4j/PqVw\r\nGpb7gT3XLZh9ztKFiCozGQZ/sHjlbu7cDXmp6OcloBnhokcZwOE2X3Y1e6uu\r\nG3SaoyAwvpCv4o8O/GJE6GRx9Cqy9AU+RWuycKRBiVTuSFjwd+CsqymF/kS4\r\npoiPk2YOn7Jk5GlIEm162/06NfG8SJYrWzs=\r\n=h4iD\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.1_1659626451068_0.7894613708549416","host":"s3://npm-registry-packages"}},"2.0.0-dev.57":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.57","_id":"@keep-network/random-beacon@2.0.0-dev.57","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9670fc5d595e5c9b3a9d00283234f52025716cbe","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.57.tgz","fileCount":163,"integrity":"sha512-jYX0wRd+TBN+t9rlly+WRW+bP4FyWfKTOC2PChpZ23EqFlEUocAspMDwqN2qTBgPQNj5u57hnwda2qSAa8eZKw==","signatures":[{"sig":"MEUCIQDYIlPYRLKS1/vBmaXLHvvDUlh8vsLgvSY2a1PU5TgnZQIgUI5pSF5QMCgcC5mXV4RQOydVf0nmOby3IAZm+JZdVl8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24150345,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7EJcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr08A/5AFxk6wsic8rn3mfxzxOBZ9xfAk5pstPmsnFq+eDzqusRBGld\r\nClOPGpFtQ81VJkvUZhhKYEJ4MqEr4QBEBqQ5IPhuO/v2IJq0uJPgslDdqyBN\r\nJ/4xiLdjzhocV0wWth7n7NqjoXUlC7GXaaoKcny+pbmPF/4xoMaowE3S5bfw\r\nvpuutseWH3KDB62gdyQXR6Eegun6CHk0OvzZ2DIvM9USHtk/7q5DLpfoVhaX\r\nx+hb1UgraK+e8Mj0UIJy6Zi48bbBZt417FLzqH/xLtbPE49GnUezqMcgGTeP\r\nt94dnxdLRazta78+C2gQ8uKIq9VwRTVbimv34n+ZfnAEC8DCnNz5qaldkCbK\r\n9BFLIfJPlxURsiToW7GdjvN/Ju0zoxpdSGXd2VpiKeGdQofD9u1KcTphRu8+\r\n2H/tWPkdZLDY/Y0CXpxPCiF67Ar9EsJYBBFh0FKIz6V0/QM1jYNmwPDBQIIm\r\nOiFhx/XDcluq2u9cS15T+6lDqkPyR8IYFe8XhXA9D2Vd6VML757/qKNfEmxb\r\nqfWAKp0fHg6Cl1qQOVzuq8Mh37+K5m9XOBNnQEUlcfczInb8CdkNwTF/ReWF\r\noz4R5fN3ol74sO6Rdgul3j059W1swHYKhA7jk6plI3WIGBg9ORmgVPLPcvXn\r\nNBKhUHnbbhHfWvT929tt/pXbQHUIsmp8qao=\r\n=3RmV\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.57_1659650651903_0.4701947429503275","host":"s3://npm-registry-packages"}},"2.0.0-dev.58":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.58","_id":"@keep-network/random-beacon@2.0.0-dev.58","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"cf1a7531886db853475432809b31dd8509cee45b","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.58.tgz","fileCount":163,"integrity":"sha512-baKmS5VNZM8XhhCv5jwWfDp5zkinrx85dapn/z2qa806x/qv+ySiWTJ3U7CYkZmTP5+6lZUubHCYgBSXPXvpsA==","signatures":[{"sig":"MEQCIBHyXYxDtFK0yehrmF1/t/oH+cCD6eluMfGSy6aa75QvAiAiaXSupL/u81dsD3mZxQTRwNGUmEtXCicuqtdUdbsKAQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24150436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8M5sACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqDhQ/+L/oDSbpaNBKbReowhI46hL7xkr9X/gpWJTFKJXkGNGOC4hLi\r\nH8cNx4+BxgnT2ct3Yq2Q+Q0S5KBcwYgQJobXCUh8Fi450khheCOPzuw/U8X1\r\nu/FMU8pjrO56V/CbNH/ZuvA3aBUZecQST4TMxDVKVuhOGBR7YTaoIeQxw8H2\r\nVQRlPKuRYBFAtBQNAUVlg4oFZ0qiCUyZxvgNGotiYemhIDJgKAY4qrkpyJP/\r\nwx4b6056gGLEBifhyO+BwkAU0yPP/69ySG2txR3JjU7aGWr3yAkK9ariTF0J\r\n4AnjBWAiNVuxGkblrMkU0u3qLxUulSbiq4D8SaxgrmieXKkK69ZS4piKcHHl\r\nxhQTg7T7xh3yMs+Id+kHPwkb3orMgL25WeWQZKPSjBmKignDt/y7nke41mBL\r\nVi4+j68DDVP/kjM0t08kY0yRYIZvjQPDEQtIxpaeEf3rvrTQnTnK/A40oSEW\r\n4Y9F81xYr9Pst+d0efEcWlkyTAMifKVXmVKGsKPnOUY7Q4DP3UKdWkztva/U\r\nlHMYcWI7gaI7BvjqCcLmFxP18FEOLlyJMGwi0uzVM/wQtRzQVszLvG5Th0Wl\r\nfJYOfFkJdVFeX5xfE0X/io4mlNH0rd6yRDAxk3vlOxfRwhTzSTzlhVb8ib1S\r\n/+0yK2qqWvv4xvuHyn/ocSfPa77jF25RKxk=\r\n=bwOK\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.58_1659948651729_0.5300044847435064","host":"s3://npm-registry-packages"}},"2.0.0-dev.59":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.59","_id":"@keep-network/random-beacon@2.0.0-dev.59","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"cf51041870e9738884b1db47c68e6f903daa34ac","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.59.tgz","fileCount":163,"integrity":"sha512-8NSqbYWXVMAH9nXW3sEsEVGWHvFzZtMwyfwT8BnebOvsIDRHNbdDDWSI/wiop8PisUG6JjPTQfGvAyts083RUQ==","signatures":[{"sig":"MEYCIQCsxMCcj/ljNjApVj1AtB039bPcUNO6dlVPzv5p1y4EGAIhAJDsy4x/K/FdASHKVFv1sY5nigSr4E0kJRBeNYMtjg6J","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24150436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8M7sACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmobkQ/+KKJNA9pYHPw6gjNetm++xbeUvmfAI0EtemeG/FQ8A85d0yx3\r\nglK0QAywwJc44klJtNHZa8wcvtn7/yRlI1dvV/u3/uO9IKru+QxIfTf2hzAT\r\nlErzYnqB4gmsB/6r456Osy0ZuYlV1tAe2K93DpDaFdjGggHaRM1auWbzWOf7\r\nGDQcWIF9XUw5vO4Ry8uKuhwNUfvhWoReYC8Oq4t55R3uJp2q19T2xQ5tLc5y\r\nKjthA66eYCil6rCz7+7s9z5yxYu7R8udJ2hPeqn7cSSNu792uuGfkNZmAb1u\r\nGdwLIzxZRd7YUPcVNqtQjPR1boq1eDkOOXGqM76m8qmk+vXYTzG3LhGCJ29e\r\nYYOtytH13LQt83Tehkr0kOUSjWZgkfzLBiKPGqy9eeJ5o3ZOHq5MPhXWtMGv\r\nmvf6U1TcHzDpYZQSSGvqHCn/T0uFqkNRUZQHU0X3DyaLUeWNaL0DTWl5elLd\r\n7QZcAE0+jLa5Xfo5KULgM1WKyWgN9+i3GI/O8F09DplaSCGzxg4A7wFUrZ6+\r\nEw/zyquh4sRrWR+zwzimYn+f4S0ea4ZB8RR/Ss6yYk6dtVeP329Wzq/IAL2t\r\nPhZDSsx1EX7F+wB+3yRxNhB4A4EAY4CK1kM/RKJjvqvYpF6tv+03DjkXikiV\r\nOm3z1cggeijaaLzIrCfbCrYYRGiu+4yMbSU=\r\n=jgjP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.59_1659948780275_0.5898400336974283","host":"s3://npm-registry-packages"}},"2.0.0-dev.60":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.60","_id":"@keep-network/random-beacon@2.0.0-dev.60","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e2b516143d4d16ac4284294e18b7a091e5e0b463","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.60.tgz","fileCount":163,"integrity":"sha512-8QhzK7mBLQejSG5v6JHMAERjXr4AMSNFWk2K4pxEMxCb93tYn5pEPqeZNg8YkFcusLepjll/x63qkxEM6wAxfQ==","signatures":[{"sig":"MEUCIQDyJKP1WmOf8ASEUaM9isF5GHiEiwOl1+oBiDXL7oljowIgcQzJmsX4IzZ3RRd8zB0QhJkaz5oEY2tnYeq9TdS0CNo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24259503,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8OnRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqrrw//QpD62O6GpuHZgDCYgIcwsVwqTPZHsRUP8RMKBKc5Zgxgv7P+\r\nFwuWmAxSTxVr4OqipM2++qEFQyhQ6XMzb9K3Om+XMfxQl0tncR8witGtWUfR\r\n72llBFZmH7eQQdRYH3srWjqtbmbc5bLlyq+mARq/JeTZjPB7u4nQ/wwjnrTY\r\nIQDD7AkCroYmA6vHgGbLmH2Udv6v9e9ROFp7IjgHGeRL2mUs6G6RSz0hci2m\r\nbKmzboexUufSpfSYTiF0EdqF7sPyiet1d56EQi8yAfpQmj1UWAUFouUpMuU0\r\nYBn06L8medvGKZNUdXZiKi7ntrmvQcSTzrze3sFoI4vkiaV78CMITFR/VfV/\r\nyt7BhWAr10oHD8YxE4qzND8ETlyN1Nhd6CTttXny2Zqo6Rz7onKSEj6ZOeqz\r\nFLCQUodNpN3JbuUx/cEgjX8TmJAryYEoytY5vfZy3nmRdWD502Lc5br7UALb\r\nIGd0978poLevhjybQxfA5FKkzoEBk+FTQqXaJr1kBjuuMpvptBoBApJ1VKtF\r\nro/nRLUxTyinIWgYw7tqqebaOwP4Ltn67Rs966Sl/VH1o36WI9w4sDK2tpC5\r\nzW95LuC0upTR3yepPp8VXzJLhlA5vohZyuJ+ieRDUVrQgLKYNuK8xTYhbzW3\r\noHKlwLeraiywVTQVEc0vc+cwqCh51G5M5mg=\r\n=J+Jg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.60_1659955665430_0.7741762987728493","host":"s3://npm-registry-packages"}},"2.0.0-dev.61":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.61","_id":"@keep-network/random-beacon@2.0.0-dev.61","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"95dde59d90b7b1be15de71947bf0ab34e5dae2e8","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.61.tgz","fileCount":163,"integrity":"sha512-v/uEHtKq/1NyP/9Nsvpb5k8oyxoKNFSYDBgEku9xe3gEWRDy8ucRN+abCLjUaoK8e9hTB4ywZMuXp5rzT8Ri4Q==","signatures":[{"sig":"MEUCIQCnpMSUaWTXndd7mAf8UpV5WusvH8XdeGfojogynPP1BwIgI0I1rswIkrNo0zhoDNSXea9Wg5jqZfgpkRg/QG9EW/c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24259661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8T+hACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoz+A/+IP4wmd8DOCPlWMKyZLKQqt+PrE2e5bfJe3z7kO0mLovB7CJF\r\nawVdRsoAv/8Vmff7GfTBWHOmssMi41deFkuGK+5/KGAGBGN3OI2RNrIRkbwA\r\noz3fLXhXQAZMsddcmarmLuaC9SHHRWhZm+2wbkhADbBK9CGDxBjYsv3WaiqN\r\ndWPI1CUE8PsbzNqHrc4vyFFzb8WRZPd6+7PsEi8pp5l0eJMXI88oZCRm/PfF\r\n8Z8nGyPTfnn+5S2QF1eZHsdeDqDZAgbYL64WOFFHK3YVB6bpFlsG5hazUd+L\r\nEpeqg21xeMhV2PgDLQ0tYr/pSmjqkI9LvIN6QhBkZl54C7lTL76+f0CE3t8Q\r\nx44UegnaMSOYKjHFz1PYtMh3CII8PtAqabeV5bvNkjkhawzoxRmsq+0pA4l/\r\naGAoi+46cl/ho0bg4p0J+yb1qrZs7yvVmNdZE1jNf+DH+X3yAmDaLzPSlw+7\r\nSBWGpocPJy0hy5TCNqwVA5MyXHo2a5S3D8NxFuttN/JnNiYenEEMScLTpnpt\r\n4th4PuYHW68SVEgvU/vwkHQhoFxVwS1i86gJA7avZcUuj9qQ+0y3Fow2sM+U\r\n1RdjVHZRPuIsVFps5RqSEchOF3ergSmpRKy2XB5YpT72zrGBLT5BlQai+W/l\r\n2XG7vMfJ/TAXBPg8lxC/rZBuBEtjWtF5kfE=\r\n=e0tE\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.61_1659977633044_0.35796310465964454","host":"s3://npm-registry-packages"}},"2.0.0-goerli.2":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.2","_id":"@keep-network/random-beacon@2.0.0-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"20e98b581518debfe0b3575d9db7ec3aab3d8c34","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.2.tgz","fileCount":152,"integrity":"sha512-RsRTr4LgaANUDa8EyiyzdQz/UHwq6s8rkgW9BgzxMX47xl0hHI+pbY8xC78EZNefU9MkPeBhrM8bw7x6esWq6Q==","signatures":[{"sig":"MEQCIBCxcBX+T1Z35C0KLpOAI9ebqVP0TEf/iwUrpCXNxCJZAiBXHe9TF1laQclgncKrZw188qQS8AbAcokDYwSXvhPQyA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22426232,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8V4gACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqwMw//WavjIZrOZ+00VLVkmPfac2vEYgiDX283wAN8CLa8x/giT2Lm\r\nY3UgyqG+C1IyUXWpLOCooeNg5H/kUHlsfTPEVm489zq6VhpT1EdY3lqR+/EY\r\nuMuokBNs3tSEvrjtYkLXEqXCKv8dbacaQSepJmeD1b/CPDLTN6CUrwPm1jDS\r\nbxMY2l9v5esbCpVhbrsXfi7vDML7FnF8HV48fCkngfsbj+ce+yW9D/fMboqe\r\nff9rNYcBMeoRZmqD754ExdEpvJ8n2NLmpNa6AnOx27eMgXzpHv2HMUQ3bb6O\r\nS5nzQi/gjpg4ti9hiQ5rDvf+HcOJZvOYarYqIBbDhzKoUXbH7cyjDZjbKEp/\r\nWOTTnvPZsg245vBkXc5hGOjP9FMCqzQITrMjvMvWAfEKmHWx1UhCLzpRDPHp\r\n0ealbt9WSBcy9i5gjJ/ipP3xUP8nGw7kcc9FIqItu2d7FSBFXy5RuCxuqpV1\r\nWbrNAo6obcMYkutvK5F+wAQhA2yp4Ahv1kU1feBpj6pyBQXgh8u1TeXEFa6G\r\n5KvosNeGbyx2zBYHvFJTbd/1ijrPWaUheVWXq/KkbflCemhBrrqi2lA86pv5\r\nvacWb7WfNichLAWMSeGQdpz6Ea/38xuaOkwg9KJGZ2Oo1O4ZoZz9cLxuIfSf\r\n4vgDaTY0cfKDm/JnfSajDvNM4P4Ca2IoBOA=\r\n=I4h0\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.2_1659985439764_0.9728719200021914","host":"s3://npm-registry-packages"}},"2.0.0-goerli.3":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.3","_id":"@keep-network/random-beacon@2.0.0-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2f0b3e29fe64b02a07d5b20ecb4749149608ff80","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.3.tgz","fileCount":152,"integrity":"sha512-bcMSyzTTjClbK3HlG1sqNoTt39eAuZKKGW3VHV6sjdjWRDLNsm8RUGpe0piwH+EsFdH+r8ebe91vXHbGTaa6Cw==","signatures":[{"sig":"MEQCIERhwM5IrMCNydfajJBWbSHiAR+0w/I9n8DToqhqkQ9DAiBUcECStLGxT/RPuW0qrYCMcNH9jlifn1bJM+KTv/rDQA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22426219,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8gfBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpE+Q//Vr3wI99bO6GjbiUKNEPh3JqD2sZxJb4dhbL6ARlSnofX1UxL\r\n5CaJU8NBK5u23r8epyFeDoAye+Oy5q0EKUUtfFSKEeOBqgIbeaxgaiGZkic5\r\nF/BSkchTg9ypQJI5eOl/2nO+qXx9f4inAgynaVO8fwejI8FTo4GpRtBpy0pT\r\n6T63cjkLotGLYZiIcf0jqVrO5iT5p7buNdDxylHOiTM7gSLtDxAMwJxngpDo\r\nfQY0Jg9lbnjqqTOUCEi1wcFy9mFQF8+5tzPrH5nsSsXTVJE3DecyZnC1lnSN\r\nETOOgxv9KbDKojZ3RzntqLaGP6vLVnqWwdzKsHLrunBQm/hc0tKml+ydo6sC\r\np9i4CeQ3JrRlJJKBCyD55RMtE48VfT4DRsuf3VwQnEaZ9Mv2I2H2rbWYunHa\r\ni1zK/L/KRdupba2bQntzSlURi+l0ByxRtBUpwLn1GbngzP8ISaHcgeYMPoWO\r\nvxAaxe/whF3q5G/+mq46YV0piHbphyykZSbzKG5Tv4LT1TUBq+/99aPl8ATI\r\nKY2jvMBWLuh+kmr36JJiai8i2L+tnrgiqNnfwvzjMTSWJfzp443eko8mnSc5\r\nCqv3H8AS2cQ42TD4IVOtdN++zFvoRRmTM6IKJPb/fMRHorJJbwIKkO8XTtM2\r\nsa8ajw8OKmlgGnen5H1ofYqJ/BeL5vXzy7Y=\r\n=cgKv\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.3_1660028864721_0.8688866880054171","host":"s3://npm-registry-packages"}},"2.0.0-goerli.4":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.4","_id":"@keep-network/random-beacon@2.0.0-goerli.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"76d9843532c18dece383ae2a88a810ed15e58d9c","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.4.tgz","fileCount":152,"integrity":"sha512-VhB0qjRbGTw1i5TIamBI4kkZcM3mZu9ilnOW2cV9pm6NwdI9Ro26thZqT15tqWeC1xG+pU2jzn+LJFQ0VbMgqg==","signatures":[{"sig":"MEYCIQDCe9YApKDycNgNctaEABqmFrFKkbiuCluaWAcithuxiQIhAN+tnhtNgMUnktHInXKELQ6UTa9JIVojB5nWh6s8ZjgM","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22426225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8iDsACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpH9Q//a+T1JIKvzlSNRHTbOue2cyLIWwwueBjWrBKFpuJb9oOGwOKZ\r\nzU0lg7cUGWbo0z7aXaC9fSw3Ih0uqB3BYj/XwembgYLtEnTg7+5MCjgvdVQq\r\nxSPBQ02doLE9GPrChAr+z6kcFbZ/3nA2eac9K4ONbhYdOHjg2GmsRFPd/1vo\r\nn5hcE7iec+LSdJncneAnXtr5hNYO19vO5nQx1hKt/dXmzb/KpiTsp1wIY+f4\r\nLEG7MY2GP7JrQu4d4/3xTqN9V9XkU+xbHcYjVlOaJvRooYvLygmMzcDB2fK5\r\nvx2fzZylL0NysTsoSo3pBFn1BIPoglaGZfhIL7BhLDMo//SYNy+gtomLT6Da\r\nxtjFcKJNnFUzGBTWyIyroOTZHvTsD+lsfuQrEruya5J2xr/wpEX9Wq1ECxXZ\r\nL+JWFjr8/W083CVgBiVK5veARvzKUOtGsj+b5NclC23TJx8MreK+Esxva8Us\r\ndy1KMrK6kWJqYXI9OO7UAf8RJxdEN4decCUwJBvRyobxBQoYwE9WTrWNv5xS\r\nIWpa1BJZpF5hpuVnUnycfqEfKpclVDFvbKFazjddrwdzIfxEv0osb9QxmqZj\r\nYpShByr/7HKfISwHy3P+G+VpzzS8gXGfAul1akITWpgCUCkZ3Q9cCzu2cX2R\r\nKkRWb55ZNS5hmYDnTfT9Hs/z9BU9QBQk8oA=\r\n=JaVi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.4_1660035308357_0.5325757935835347","host":"s3://npm-registry-packages"}},"2.0.0-dev.62":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.62","_id":"@keep-network/random-beacon@2.0.0-dev.62","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a63efd87326e0b42bd54265a79b5feac2d6ff190","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.62.tgz","fileCount":163,"integrity":"sha512-fcJ/xB8uMdBX+/l/D28pD9ikN3SFegHyBIRlD5Jz9y+3zddnuxp3D/ZrAt/80cD72XSEf4DRPPZA6NcyMG5qeQ==","signatures":[{"sig":"MEQCIDfwB88k6WkUbXCp4Tj7A+G4RlVzn43x8e1CljTRjH03AiATPlCxYhY1PLLMqNabHJS6gyE61mJVpFNyS23A0olpAA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24259611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8mJCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpO7w/+OekAkpA9faOGe06YVvvC3vS3NnFI+k/mKhJt+3o5Utn2V0QH\r\nFZJvyenhnluwQiOb5eYSxO3S/RUja9QQxxDYILdXcLATLvbS11bROvRS5N5M\r\nNbN+50f6gxtSzv3HeUX3kA3d6RqKk/yqPWMmayE75lrhsFmd3BRA8AnCJBVN\r\nWzEQblhicxib+NbRLkE3psu/WL+xyDO+QZzhISKq5QO1hU+Bn6RotB9vAQIs\r\nDX2xJkO7T42wABwDEFoXfD0GWNL2udn7YmpIunFyBvQHs/hrCcARx2+DwoDF\r\nv6MwFcKOG4Kr4mghKYEzc62xHgICw9QqzlxQzaQFhP+nVrJjxv1qRDfNWa+H\r\nA/MKemo05Tos5bAYyLERWHakjzp42ktNOVWJOnMQcd2ZXC6E7/ozO47UF2mW\r\nMJKovy5mdsUabbwXKWJuo61An0g2u6x+ty4bfbywVMsqTmT/72YOIBwbekTi\r\n6It4TkmsC33IOH6FUgcriVsChFAN+jVeJFU4eBAmPTvywst0lQkisF1bVyta\r\nGU6qKkIXMyKw+7LyOBxUUIfxZBrWaaLzZZoI0bqTQ8zznDfv5Z6OsTEK0LEq\r\n3cEpr5ZcrheSR6q/mLYldw/RNzSURFyiG3o1c5K5NAL40bcTyFciP+br7WKx\r\n6wKbmPhyZHzPx/5HLPRjhVclv73/JJ7LF9c=\r\n=qOUs\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.62_1660052033723_0.5755787142742452","host":"s3://npm-registry-packages"}},"2.0.0-goerli.5":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.5","_id":"@keep-network/random-beacon@2.0.0-goerli.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"892542bf39313995dd2bdc917b6776fe47cc59e2","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.5.tgz","fileCount":152,"integrity":"sha512-mutkF3gFcmlCJORloXSe7p0UuX4gu3cJJdfmPil79EXCitrAs0wBgFzt+LjNPMsXw+21EEncZ1XY7oaLnEMWfg==","signatures":[{"sig":"MEQCID1GW9oUoF1k+YJaNchHUGOwWUImHoewlCX9QrSC/+GCAiBdDml+FAAMFSbYdM4+2+JnHhcidcDS4hcio2tzfH1LyQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22425927,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi830bACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoUdA//e8kqjp7YeF8fMaULBAY4jdLYFaudhYHvlzH5/GSinmwe5L3Y\r\na8BLsMrzLtUr5vHPG+aBfX7z6ZFg2CM0LS3YKGpmkVCM3bu80GFuRSFlo2S/\r\nXKhXcakohCjzSnAfKIeBpFK6WkeCbX2n50P1Mud/afSeeFR2kahYpuSYJj//\r\n82l7QA4GoMqpCLHGwwWcqJ8YQUP0onTX3RBpfSyIXHspCEweanHGd7eXX3lV\r\n7dpsUkbnRB3Z7FLW5z9rQkoKnD9ZyH7i9WG4Hl8e01ng/6mJDhguYcGywJxz\r\nuZrRzEOrTNIQsNh+XDAuuGe4oLqoIUJaLwrDgjQdniTl6s/Ys11/tE6yebc8\r\nLNASJM2QHA70FPMZ0zqufQ00telvHArifyu9d5t7xfqHYd4j9NoT6uj9kLsD\r\nKZX+nvmJNrXxmI+4vnY16OfRJGQ55uhCSy6xaRBPPwwfhNNS/5WIsHEZpMQ8\r\nECloMTjQQmYa5aBoNptw6QHV3zdba40HGVp2JvCia7Gygdo2c2QJTKV/MRzx\r\nKhnl/Co4C1xoUjQHGIHnbTEuMJ1JznARQw9YDx7Cjh10ErXNh4EEO3Q2XXuG\r\nN3/LlBGR9moAE4BdbQ/MTsLqP/wvDUD5zW06k2ZYxFvZ2fonq6sy7Cue5pmz\r\ngdEQgqyYA5aoeli31eddMqqCUK1el/VmjUs=\r\n=Tzs7\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.5_1660124442741_0.5261286247346457","host":"s3://npm-registry-packages"}},"2.0.0-dev.63":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.63","_id":"@keep-network/random-beacon@2.0.0-dev.63","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"631990c9b51b0ae80427fb192e4b37801fa83c48","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.63.tgz","fileCount":163,"integrity":"sha512-LeUZp4HOnbC38xBF9bLE8CuAFncD/+0PlIW6XGHL9vaH9HE5Gt8cBqF5AjmDaWSFlKMyxydhQd3iprpF2XoC7A==","signatures":[{"sig":"MEQCIEDnK98+I2Stwo7S//OhTJoHOAONmAMI0xzjBi/ZxERlAiB/XfHOiSzu5T9CJlnf8+rM1bNOfzcVTnSdGscakODNew==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24272774,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi86KcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpL6Q//bsWCShlQpC9ezd/a0MQypuCNhoZIORN07Qw8UPgmzdX2yftM\r\nakOJb1Q4W9DPEMleob5nV0qCjbuQGZHIX3NPpp2lK0sTWWEtffIqkf8zK3Sw\r\ns0flsXadTrUCM79Xk2vQ0dewiVS3HJlTR0yxJyNsjSAXneKQK22LXaBEUpNw\r\nyEqcG0AXMt+ZpNFLVzxMxchEJmdIcr8dyT19wrrnvYKlhXe3yRhBy6xA8wSV\r\nlNQt3cd12Bsx3c6OQ0tdjOYN1aMXULFq6UD0Z/uDPOZSr5tfmtSDUxs8AnDz\r\ngl2CD27mJT9350IHLlFrqL8rg9MPrXmJHVq+yH6ldYideMk/QJtqAfZrztuh\r\npb2NxC0R6jM6/CgOLpsMxfREswXHwDfTh5E+ol/rat3AUugEUzLSxJcvgAke\r\ncoXQ3NoB3CV2tnQCl78V5l3C+ilxqdxCPA6F7aFl5lfuZlr3FeMNQH1VWG2l\r\n7ziIatbREvniRXs1sXRX3KACS7AEIXHURDEzTeCthYn+3vkil5eWpulHA8tS\r\n5Z4kzKmcG1jToxtDKgksjV/OxMaC6kf3MnYkdFxH3DCz67m6SPDpor3ZWx5U\r\ntRgy3SgHxz+vjPoyMiyeIhfcFflUvQeRenNXu41AIyEqrKvYUi6Zr4yyyln8\r\nlJZqD/9uQeNBIMXWUON+QmK1kJAiunuZaNo=\r\n=0Kab\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.63_1660134044346_0.29560969913250434","host":"s3://npm-registry-packages"}},"2.0.0-dev.64":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.64","_id":"@keep-network/random-beacon@2.0.0-dev.64","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"de9a70f6b4872fd0f54fc4f713f46a5956116a30","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.64.tgz","fileCount":163,"integrity":"sha512-SeCme8IQFaAYZF5gEw7GmyshUmgXwUPbcJg8Kcxza92hukGu04EpvkMRYcmV5hIBCrbPitlOMbshLhWq4t2QFw==","signatures":[{"sig":"MEUCIQCi9odAiMvtIT4GFjvenOwPEw60WJHsKTwFDFskIxwTqAIgVw7yLjTtJx8EkkGDkhVQmcUcMVbOqNzLkwlXr27yuVY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24272774,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi+dMyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmocyQ//T7g19+J6rcCToEHCaG6Qtn+LgVpQdWRNdQDkxEz5HKSKoNRP\r\nQ+TXaLm6WsqAMN+/6nJadZjN5QzGXRxO9qnwaOGvEZqTA4P8qSD4E9Eah7nD\r\noFRLQXlejrspNnbgqhw4bbvdx+ds2oeRzAC5kuME0msQZgzaHS6Zd3h9RKQt\r\n7BQf6/8fU9BHk/jNUE6Fn3N2wk6NktrbZYZvWJNnW68wTYyLmTqDyYJrbwy/\r\nxgRuEI2EKhl27mS/v7b1ZWj/UnLhYooamPiW6Jv/1lCd1M7Ybm2O+J/uE9fw\r\nUly+2BxEJfSQT8M1gEqUC3k/iCNrpc0XMdL+utBddcRL96OYqqMcD6IAk+m9\r\nqCxjv2dzkneoKXaaBUVTZtqUUJqUGO3KVy8HE7WftVdTyK+SJvTvBwYj4GA/\r\nS9vpShojm5I0QyecLSdaT3x/ivTDBFqHD1X3tH0tLHd/TEYvOvyFQCOK5GTg\r\nHSzYCPQQGRMVL/ig9b+a8EnrII6z1Ov/y7lr0FnfKDYgDQtJI3f9cjuToMZM\r\nghjhzV5DJVtGLYfG6ZeKmEj50VIwA3WOKQSVImnXzw1Ljmw2ghifzhqOGw5L\r\ncJ+vapEKuzwd/DxiGmYSXIiOMscpNIa19sd0WcGSR16/Zq90pRPAYponHXgT\r\n76HVjd5XevlOZJpXinPU74o3Zc/1oDQ+/pw=\r\n=rjOL\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.64_1660539698426_0.05195547046720117","host":"s3://npm-registry-packages"}},"2.0.0-goerli.6":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.6","_id":"@keep-network/random-beacon@2.0.0-goerli.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a92ec14869f86e9510946c8a0df9b8f02635d79b","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.6.tgz","fileCount":152,"integrity":"sha512-iu5RxWOhDnQFKtq937IvdMlo3s+AUerBzRUgpAgtQNMyV27ukbUGrwCy2Aqmb+1rTbabi3XxfjiM0li9Rgc2xw==","signatures":[{"sig":"MEQCIDbqgEnhhktMCViF6sd1vdDBQ0ym3TNoYVtPYNuFs5uPAiATgQUWbyIiKaVtKMzOfO6EVNvlLtLDjtjxVjlNBPGvsQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22439334,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi/SXEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqGIQ/+IfNGjUZ+x98K/oDIq8K+MaumRM3okoZlqLXMuksOYQKDDPRD\r\nOrIdLnDIecxXzU5895OdLnne0Yvsx8ZR8DIhp8vba8R1YY6KKciirtU8LE38\r\nsAaX4TNBq2GJnvR07F1YDwtWC1vvY7t7bZqf2zFVhp4q5T1u1a3rYAeM6wk/\r\n59WgGKnf7egwyed8IiClFFcfxLWHKkT4L6JkP6V/MaMTMQ6cVGMCNGXjnpDr\r\n7gZaxmAU+hxOvR3N9LEqBas69ryAsUPZ+HXWwL2Nw7DjVKPGrr3pVIUuBBTX\r\n6JQN9xomrXKr1U3QBYeJqIa7y/60B/Gr/xOpnpO9H4iGph2clE9O+C3XU623\r\n5kms2dKwMFws4f5y4n5hNQMrBw9E+a8z2BoyOx552Br/8nMBrIxpB5IvN9Xi\r\nWZOO6COIMJeY1ll73JbexDcJLtXMk58Y1CgxQbTYK1YyGMy0ijMIutvEevIh\r\nJb2aczurSXhgEXOS9AUl7cN2eZRhMHnhcrZyz67eHTtuFBZaUagYEqogMo1a\r\njDfUR1DwkiZry4+tjQP/5r4SamyqZMNXP8IZRy7yBKs7LLeFuGAHRmSok9QZ\r\n5N9UgxSNAjZkh+1GrvOqynEmxZ4dBoKkNTv2eTLMSgNA/U+LDHVzZG/1Q8w/\r\njFXrd8playlyyY5sLL4yYLpmKdbf7tMzbHQ=\r\n=tttJ\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.6_1660757444522_0.6427367934795949","host":"s3://npm-registry-packages"}},"2.0.0-dev.65":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.65","_id":"@keep-network/random-beacon@2.0.0-dev.65","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3b39d75b3ec64830303d929b8b2c136d4fe87274","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.65.tgz","fileCount":169,"integrity":"sha512-Rm+k4RBVcaF9rKm6orbGJQLEfNzWQGiibuq4rGX/ONRLoa7wZo/f4Xu1MItLFPQkzF1sFoufxm0xBL/svKLs/Q==","signatures":[{"sig":"MEUCIQDeYVUsidSP8r202YIuv3f+hbRlRADqV6D8lt/pHNyLfwIgJsHIDEyjp0yDR/21c3of9KypG7H7RoSlDwvj6G7YPcA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24287892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi/f+oACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqpkxAAn4JckHUlDraOymz0H4htmBI+LfmcCLU5Y21B4kwsUSAKQkY1\r\nPb1m7Bvo9K7jM3fPmi3nENK+rWbuI/GebwkS/VzTZiKN5TAe+reFuFmnC2IN\r\n7M1h45RNd3ysspGb3+2IOGVMGd1xcmuobo6BvcwMF+/Vp61hTDgrEgaNJSgV\r\npq/ZUln/IsiaWcnPXKPyfv6HS4rsqoZkA59RsuhdHnuOOvA09wFnjY8vhFT4\r\nEiyufeOGFA6+ZsukSdLNdPZeixDTCKGbzdClfrw/cyws8cfq/x903iOLSZxU\r\n2PsQAIHm0TUg7L69PoJnyDGkkokfzCwn14LTcrBFMjzFJQnzTKWpLTzQkSph\r\nFO3Pp5LuHgizJLqz2MVVwJ8ggrbXz6IUw7vvFO9qgnq5RE3AKx3w1V6nggfj\r\nHLw4f/u7XWdM3dJxRfbU/WpkVeDBo+ypZ3Z+PmhdTBp1YkrnY9OjHBtBV3Cj\r\nAoGHxak1N7eegAdYkdyN7Ogimq4Hm6Jqo62Qj2fQPttD/NBITjMnQXR3STPK\r\neeDCNUtmxSF+Q7ObH95wniQQgZzA19u37xkKgCu9WbszMaksr2nvE1nfufbz\r\nKrNmEBYtlUHNl7vUIkKq/xHBgr5EIURqZWfMNpJLa+GJ8xd0ePqerKSM+KZN\r\nZrzbWch1jwDwBVvOXEMwojHrejmBS0/zMJw=\r\n=kVfg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.65_1660813223843_0.44347653979757906","host":"s3://npm-registry-packages"}},"2.0.0-dapp-dev-goerli.0":{"name":"@keep-network/random-beacon","version":"2.0.0-dapp-dev-goerli.0","_id":"@keep-network/random-beacon@2.0.0-dapp-dev-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ff7d71bf41dbe097ada2a5bbd322d0a57c569301","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dapp-dev-goerli.0.tgz","fileCount":152,"integrity":"sha512-XpfASiBBJBp+EhxgWiyzYRmFDkryt/D+cJDqVO9/O01DZ10OW1Hqat2ORyNQKQeKrQ1TWauTqUMSw/NSxqaDFQ==","signatures":[{"sig":"MEUCIQCY12PIrZrnz0/GC+uFERdvfj3fnFSeA6wvM53QcyEyLwIgUx5Zo12sEtJQ1/L9GkerXjTQSEcieIwLlOrvQvYsZH8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22425877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi/kC4ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmow4BAAlLSrMkn9kygg4mTTKCan4Krb7AEpLpSS2dTC4QPrvtKZIPyM\r\njxk2nvKgJYA4+UPNrtH3koRojMEZN7XIAYxjwc+ictH3wpZUJchNQUJfccFp\r\nK4Av6MVHxGYvVStrZnvphJR2KHYp4OTGDXuqqeulb505YoCbLs5x10vpReTO\r\nbzCFB6uy21a+aCiBcQFA5BkEYFdvSOUWGNJ/1aX+TA6Ttv1fAedda6PZG8D/\r\nxwlImW2i4vVMcyVjeOlUNXt1oBAkhoOPTWHvk3HX2ic95u/bZUY8sk1dV71t\r\nEogyI5nQdoZvz1d/dzcZgNninl/BU0VLMeMBhE2c+pmrK5Tii6LqlqlSlmVO\r\n7HtdQWid1vFeLTUDc0ikbmQygx6Xdl5ct/hqxpIXZX8VswvBaapId6kHCZKW\r\nadow+Gt2b7tHUmmv3jyb521RzhcfT+vrXC/84pIHiW9OxuEUAsLLqF2GshXd\r\ns4ugHYmTWHG0UZM8ggZ/MKNBz3lOM6wuX8JRctICh04Bon2Y20CfDW9rBQGO\r\nZftrFAJNyI7E5nCVrKIDr27Bz91insIAlPWmcOohNxofAjcCLTMrBbSLdZqx\r\nxxxbo2wnMOo1WwUSCPc468bGCZXOn++Me7WoBRxEj9bmfEWqUvujghxLVnU6\r\n6ti6IZTaz8DoVSxfUW7N2iPoTdnL7UmHn74=\r\n=uQkm\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dapp-dev-goerli.0_1660829880414_0.34157742260581214","host":"s3://npm-registry-packages"}},"2.0.0-goerli.7":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.7","_id":"@keep-network/random-beacon@2.0.0-goerli.7","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"994e6cfe0537ec73a2613fa83bc019697efcc2a7","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.7.tgz","fileCount":158,"integrity":"sha512-ij4Vwfj2n1nUunWSCr4Y06VQ6x1kB4sa7c79uWQO2T96p6bCu3hoCZtAzjdvKpHjPPIMkSWXknpaZe+jYvdUXA==","signatures":[{"sig":"MEYCIQDG2VAvLn7c7LvOkVobKe07e8eGUzfaSGFGP62/VWQaXgIhAINd+RgHab7VdPe5YSEyeNOdOP5JUply09setQ78iLSB","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22454461,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjA5NmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoM0g/+MApSpjkXglOmHHgsPqL6O0HF2z4ZR+FE8pWyYEJ6gRU7pKZ/\r\n4xZcwHXqOAbnaZhsM8+DsD5KtQ5E44H33jMe3s/z+mDTrAvD620WODwSUX+6\r\nIOeBUW8noepVz8QCYmdMDsUnrlUjHzJ5iDHNN6cu0iLg8bwBNrYIGqRdhtgM\r\n//zAN8mm6T01kZmz87fExiglxV8Fwor5PDl1J87332B2T/3MUWxiRSa0DtBp\r\ntICdC4tt45+qvffIPFsUMatBtKWwxRX8L1aXjeLw2VtTCn5kW0tlWEvn3LEI\r\nkdeKL/jWEFeG/deAhqRFWam2F/YGDWLFPtXfIiJ+1dDAE9wLslqE4k+XVIA5\r\nt+oWysZCI7/bH/mO+XuZUYF7r5yFcl6ztX+crMuQkTYU98qZnBsu9LkLG6lm\r\noyikDVF/3Cjv6IOp/R6tQzhiQokZUEbSC5DlINcfCVGhqIfY7z8/ppbBs/b1\r\nnHoxJBjCVj5q/b1ITBCEnPNOGi82QTzzHEJ9akedMM8lRvEblZl7i3LkK3UK\r\nkO5qUgpOBPVzIgyEOQ2e63OBhM1r8qgFvQ7py2peJm6Mr3m+Nj8XOhbsFj2c\r\naL6B4OlTyllEtDk+C2/Lj2X8uqaVVUvI+8dB9+GeBVEwQQI4V/Ac+udLhRiK\r\nyvkqDgyD39MSl1OcGZxANjcttDoXDE9mLvM=\r\n=8AVG\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.7_1661178726649_0.21355502220235678","host":"s3://npm-registry-packages"}},"2.0.0-goerli.8":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.8","_id":"@keep-network/random-beacon@2.0.0-goerli.8","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"fd7d0d9399af400f2e31e6c54dd758efc4bfaab9","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.8.tgz","fileCount":158,"integrity":"sha512-i24bWiHA/nwyYwlmr37dy3Yg88516jnUqbYalj+bVvf+GeKMO0vcmXavC5iw8mDRUdgLGTcgZwBk/QFq16En1w==","signatures":[{"sig":"MEUCIQD2AjOg5rQckecdYWN1Y6rk4JTzZ4SZYQzW3XbHyHU0dwIgVOEy2Scqbg0aqB9QWCfKw4qAIDNr+XC8wj5YVszGV+8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22454458,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBQQxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq1Ow//YCg0rsTDXOSr9XK/eo44TJ12HpdnNdsrxXecGM0eVIjsc143\r\nEP8O5H5TVcIDF62sjv3DW89sicBS6IA6Nu4REjUj8Cgm8Rh5o7ZuFGp/9b67\r\nNurm21mrmwDMYGHdKGmvD303pzOTn0wxHjbV4bGufsYS5SI32ca3/QikanmM\r\nIztuc4LuWHjM4uDb28TBleEtpbl7X+A8leUDnWQKjZCKrcbT5lQSdjzs1vFn\r\nbCWk58Nhfx0eCL29FxR5ZqINW3I742yBsyB2vnUjAbXOTjn6yVnKYFSNuKYC\r\nTCefLkKNmoWKu9X+yd+BRg0NAyzC+fgjxW9yXI8nqnMS/hVE+SY1ku50F9e7\r\nSUhcSEyXVo9jH5gxyVFnQFaHQZyWrxXNzcp0JRFHSQLYQCMhG+/2SPRmIUIC\r\nYtRso9E3tky401UZ2zNwneLms5FXN6BCFsnCOkfadw7xsP0F79Bw4wKcOpFc\r\n0peg8gbwDzJd90LsadPPXf0hdnfzPQzmRlSsQ5v949AGvrWfXvWXHiiYu25Y\r\nH+MP8JxK8hEO5ZlqzbRxbEn/xLFhFs5hJSS1yV4GHeaNDy2zRYZ1JyYJq1el\r\nYeQBDi03vak/eL8JQYb7qgsh2Cv2Mz0WEkgbT5V2Rx70vqZYifOm5iY2VrLv\r\nnEQYO2HlmfJd8ZBQ1TVzAoxcGcv3PwCz9AA=\r\n=KJiR\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.8_1661273137477_0.7112628435524171","host":"s3://npm-registry-packages"}},"2.0.0-goerli.9":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.9","_id":"@keep-network/random-beacon@2.0.0-goerli.9","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0ac68e730ddd396dd3cc01644960a53966fc52fc","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.9.tgz","fileCount":158,"integrity":"sha512-5x+a7+30uhbYnlOPFQKvVXgG1iNew/hnRw4/w3QhmB9uy7i70bgehkcVHDPFvF8z6vXeAD+yCz2d4xSH6mAH7A==","signatures":[{"sig":"MEUCIQCAwXYDAu02pAeaDNVlmJ4NVmH4Eh8W7JBksD76XU9kCAIgP9muGKhZItNQvnAX5cbTRFmAlciZeLlUeotRWqMHITU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22454467,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjB2/DACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrB6BAAk6SgPsBbBHzwhiv3Akby3uQVmumHleQh75SdQPJ8EZFqLIkh\r\nOlJCwoBU9sNO9AyUBQwrjND5DJ6wIsQtMmQYgzSodmsH71O0lNM6NHgTJEal\r\nMsJ0CLBnwt8NgDS0SHyiwZEO0zHyrvTkXHiumOi5P2qYzW26zCN0FkZkSw/K\r\ndYeq8/yR2W5lGsDQZQlqbIOKAHBp8j+Ioe2X9/Nbyhf8sC493bHa/G6LmG3Y\r\n8a+uJ8nq4Io6d65cjZvZdR1ZZTqQAlaRz3IrleLefJ+5j96MQzY0U/Bq15ao\r\nnSKX6GaY4eghbi7YVs0Gk/jb1z2U06HpJWRceps9eqs4uehTpbnYL91dlot6\r\nssalxNh7iU6HPUl9QiTKjq9eEt7dxZycmrpXVgzqlMRNyt9VDrvbBrUeu8lk\r\nltr8NaNjBO/Oar6RAl2lQmTrdAIxBTEBLMAY4in97NSxPzVSxh505NxIGsBD\r\n/K1GiH4dU+JHjUyi8q//oUxZ7k8GtJO7wVBzFqpZeZyiwzXNoNzAyQ1MyRaA\r\nSDl8wzZ7PrB1O6aVNjxPtekcNBoaDm/j9sZ97xAYXp+m8C5oGU+sY9t25+Fi\r\niSwU8IknhRjnrNknR5LVnVkuQpkosw88s6M7bFatVAmWzVq3QqnpvNhyBAIm\r\n0mv5ohqqQusAv3HY8XCJWVjtEbgcoCSh5Dc=\r\n=W872\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.9_1661431746776_0.20151330466965756","host":"s3://npm-registry-packages"}},"2.0.0-dapp-dev-goerli.1":{"name":"@keep-network/random-beacon","version":"2.0.0-dapp-dev-goerli.1","_id":"@keep-network/random-beacon@2.0.0-dapp-dev-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"46fb184c80148ea81b9e4096b9e42567b07352c1","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dapp-dev-goerli.1.tgz","fileCount":158,"integrity":"sha512-1jxf1qYPii7qKTmWlEYB/k9oHcjfDcWc5uLK7526DB7La39bF14OvgDdLq+Ng4tnRe8VePxR5p4Hpt7J+GZt+A==","signatures":[{"sig":"MEQCIAwkVbmnCKt0Pzy65VZ/j0b4iyJurmIZYCaSMtfzf7CxAiBrTOgid9NoKMoQWahZ+z1PpDPZaDogfaZUzp4WSTxebw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22454245,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjENVOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpJ4w/+PwQKLtvTZQLy8OpEWDwNwQsqOpwQ8qUPj35NLYnCUbAj+u66\r\n85XCDhqYKPmDtA8Pp8/RguOPmjLzgN4s1q0+Hr4BpmOG+NbyGkkpH8uEEVFC\r\nIz3zyTqH6CoCUZEJNmsAxh+oInyxqB/ybh5BU1ytyNA3gax4A+LRTZNdlQY+\r\nGaPoIGAFN5fqzjt3jRz0di1e/iaAgGaMwngWwuRL50TlBvY2QxL0ZoUcpyB/\r\n3LCGHJBSmItOA0V/2bwkrDzA5u8qKIu91jNLtq0qg/PBbX95h1BEj5f7/C8N\r\nB0utXNuC0XuzPlUsNcHMHAX+L+Ck6Vezc+tdPWhsI5nvOv1JmSyPVqRm058M\r\n7RoLPEYxrnlzjPMl9j9RJBwYHQPv//g77vCT4WVKNpBc0RJ9KG28U7poYf5o\r\nHy/PWjsWevwJ+Ik0g6/bzBOTZPOTgZTl5xJG6PxhlWzCt3aifyl+zyVm+kmw\r\nagedu05Cw6S660pWNihTK+qZx5/HsS6L6lisbKD3LZfQFF7gWRQbtg7H+Qne\r\n1zxJagEZ5nUq55XQ4hyf14IXCG5vzzAJzG6a/Olx2kwfFMB6QGotousYugv8\r\nZ7oVb3pzemoAVFeEH+EWMrh4wzUEhDXWAkk0f1vk4f92OYNfWcJ++IyfiUjd\r\n0ViF8GU4g6S9lOqqeo+nXQ7a49OrRxRed+M=\r\n=hkiW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dapp-dev-goerli.1_1662047565967_0.2500286750919649","host":"s3://npm-registry-packages"}},"2.0.0-dev.66":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.66","_id":"@keep-network/random-beacon@2.0.0-dev.66","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"10b0213c016fa71e12bff4a16d54ec3bcd00a959","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.66.tgz","fileCount":169,"integrity":"sha512-hQIVEPqIlGuf02vUCCyai9NwNtm6Nc+Nfu8i0mMwdEjOO4DyuZ5CMyWIgjUdJaPjR4l8ahyxs826dDlUAc+2ZA==","signatures":[{"sig":"MEUCIQDqB7qG0/HjcW3XCFT/nKzZFXXUiQ1Pn/XNYCvIsYEJIgIgJiwNui9XF/XyItBxUtSZzkSJqaizeZcH60woC7Fztv8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24278585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjEcU0ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr89A/+Mm1v/KIpujQT8dcupK4LP+G2oghANb058Pj1/YJFqvIN2C6w\r\n8n/+qgoPFP3slLI0JY95zMcyapHcnQk4Q+Icy5jN16p/IqYSHz5x5vjzcX7L\r\nDdEuMYPkx0lDhROkEwoS3BFBhSkk9osDerB0zc45AOQETfhSJTXRpzLCHPHU\r\nDjD1wPhFUIDdICFCStaV+1AcS46NXu35CloyzSy1/jgy95WybC4AiPhUM6U8\r\nl2GBHXWrbx9w6slToTrDVVXDSvYvLBEdvv6Rxu2cTvdNi5zpmNoBQO9LrE5N\r\ncC9yEfVCOyoRB1FaOgDpb2M04gzPAVO0Q5TVqFXsjzzxevUoiKxrAwC5XI8t\r\niSeU2Fl9T9Tg1nkpL13+fQnzc11ppli42p/KTzBrssKteI7l4xrfeeNqeUYL\r\nJUBiC8KhujkWJrxTOck8MxyikRvdXCLRUmDQ1tmujtx7sarH5i4yySb3t3Lw\r\nyLV8rYilucxxuf7gEjHTKQb3DMDOInqrxKs7VZKRgdVEUOXKiUBLgFfAg2T2\r\nc75YDhKz6ALh2YgAUxnoPxMVpin957MD46Tv20IYV4nAiK+/p7w3VY/yazke\r\nStZyKN7Dab5IGqKq7ruu6S6eNqT02glCV2YsQNcenFryepCqOwzx4jgAGBC0\r\n9k40MSGQguy6CpJkmUnx7lJhgAUGQAvlSno=\r\n=noWx\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.66_1662108979788_0.7297481100531744","host":"s3://npm-registry-packages"}},"2.0.0-dev.67":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.67","_id":"@keep-network/random-beacon@2.0.0-dev.67","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0455235bc521d97e5df722975d03f10090493d9f","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.67.tgz","fileCount":169,"integrity":"sha512-l4tOZPCF7StKZE2tWWCBB58+TcllrGKfP2AvtmC51eAhjBCJQZ44dFwloDlMe67Tp/zlW9enOj0bZmhSmsqZzw==","signatures":[{"sig":"MEQCIHYU0KWUerdrFeeqB9fTDH0g+Jy5Z82qr35SNitEmxaWAiAD0ILONa00jHMdBRD5y2JN0o7WmfNJsZVbcugKc0WnHg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24278585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjEcWUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpRHg/8C9zkUAjuhkahK6qiU2NyItrD12gO3NaVoMv8I+vsoF0+3yqj\r\nrCJDEN7O5CkOH3p9d+SNLPEjcbjZD7sFV41i20Ry5mhHi4Hc7C+ldLUzYymp\r\nCWnCZ7LFg4WQpX5bTWXvzd9jf8s4oGobxb0BYTIffvyXJibsId8tOSr+fpAE\r\nRmjewUK26iAi9SVYcyy03lIB+S13fEJMK0lIdo4STbm/uEnrBmx4SMwYSytG\r\n8I8eYPsWC6Px+eyms7ra9L6trVhKjP6ETc7vZyafbVcupE+u9CubYnktRW2n\r\nMKVfqOgNqQQH3zVTD1t1DIm5c1rEVc1zqL32521fPmLJb9SinhRjknVrw2Qo\r\nzwYP/gxresSw0VDU/+ZuyAIhI+rodEsnV9N6PBa/tUQrVxfMsFSzoKhKDceO\r\nkg6hUkH0j/Ikdf3uJqsL3auSpROOlQyCHwfDLpLdkGJURxxiDbwWP2+6YluO\r\nUA+plbpBCAR7HUT2RZdHDNtwCHE1a3tN/ouV3lXxJpS9yY7zH0Wiu+mLPO4l\r\nlTsHLkAP9SO5kU6pjHrOtzlikKylLm7J4JYY1TYUwOgCJI383pnSAm0hmWSF\r\n79sdbMVUvh22mDHnSYCRxqcNitEFd264jBACkJQYlXyNfi5irPJ6UkibQtJZ\r\nG7aa1nR0TNAckPcUKOj75L5Kl1MwC+hy38E=\r\n=Z1d+\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.67_1662109076382_0.7903664301664872","host":"s3://npm-registry-packages"}},"2.0.0-goerli.10":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.10","_id":"@keep-network/random-beacon@2.0.0-goerli.10","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8afe15ac036800b9070f4f1d52fa5013908f22b6","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.10.tgz","fileCount":158,"integrity":"sha512-qI/N6k+KuObszD55rwU6myx/5HYN8dSD1td70ih46n/Cp8oTDLfi3TVEHXtdUs+RWQri3feows7x/GHZXbBmnA==","signatures":[{"sig":"MEUCIQD3bt1G8VjhzDF0nLyx3SqdoIcvfgNb0r5AX2btJ1smewIga8pG2zO6/fM68b+p82HaIHR94UYfZA8WvdXbriOea6k=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22440175,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFxmwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqXJA/+JpZ0dUkTayP5+23puCvnSaskooZTtD3wCVG3b7+fk2XJoOAE\r\nHDYJWa8yBaveOR4/FQ9Kp6sjKC/8MA10ShgpvYFgBMTvcMWZAK3XuLKoLsBM\r\n8YK0xVE6uz3MwKwh8k27HBsZfk+VpOSRaSjSPJD7TM71v4aCuovUc6Qu84N9\r\nRuhxGcKKlWr+hy0iXhGM0inujrYW3xmXZkKJaVz5yaLtMKg7zouyAyeXk5Lr\r\n3T4+JkRQ8H48Mad91qSCIbB6R3IBIjLFir6W9Rfx/ecxcQEIF97L6+FtOOAN\r\nyycDQU3iqLNiNhdE+ye63Xw9vBrT7SbsDJ7GQz768knLyacBHaRun72P5fjb\r\nt8hxBDyxNCDtiCIdjFVlZ8MUXjMAThHMquB34hJ/QKXVfYI570aNWBNWo1sl\r\nPKXfEIVobP+nIoj4r5dqfwGVte+tBH/Wswr+6Vhcwke72EIKaLNDJvJC9zNC\r\nS2eTV4yBa0HLfGUP/BVxsM1oThm39gLX9lg3JmLOb6haoFJC4GjVi8/SqDXe\r\nT3fHR/SgTtj5MHcUuRSHkRGrwtwnVZOAVkuDLfzJo0GYMCBxXe/1fwmKnBF1\r\nWlA++8z4lECdoL8R2tjR/olK+2j+wGG/eiGNntONQGHLv4sGU6nzPQGOYCfa\r\nRjnYrgVy2Ke8Gd9cRo9zmh/19KkNjl/lv8w=\r\n=ahfa\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.10_1662458288428_0.10531537549272696","host":"s3://npm-registry-packages"}},"2.0.0-dev.68":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.68","_id":"@keep-network/random-beacon@2.0.0-dev.68","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"51039fd8e519fe159110da52f7e04feeb9e3ce22","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.68.tgz","fileCount":169,"integrity":"sha512-03f2RMeU/9nnPMMIEZo5k9DIRARJkM1UGq7boSlHbqXc/4AqVvoO+g1sd5pPRqu7cuc9EbFOZOjmOFV2zxnWPw==","signatures":[{"sig":"MEUCIQDatbrJ3eHB6mBJuacJDMrHmmzed7KIZWkC0QOZB/HmZgIgTRcQNBHyREVBVd0sZst+GSRZ9S1XBNTf6xg3NMUZynU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24281306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFx1CACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqjsRAAnNmE2Kqc3D7f3pduvTOgjiR7WXkr8p5exs2pLBh4Rz/zL5HW\r\nix3RI1+M9o+7cM1K69eK9QAfB9bZ1dBzhOw3mZrY2rZVI8ahJvoRw+32CBeC\r\ni1/x2L9wq+Uw3XUboNqIiU8X4VxSvs8wZGQqgR6veJa75oR85dnOkM2UZfJf\r\nlSFC+CoLeileWNh1/gac3AcKrYrWYqgFrY9eKBp3yk/O0OUYDUvOmouZZcFi\r\n1fP0XSa/MO81FkhlfngGPQQVkswQZLX5p5VvOHvKhZC0A3y1djwz0fMGfhjH\r\nsESPHFrPRbqk2364FFeEullZ4CSg/5t1tDEAS3elfbE02VOAoKBa9BQNdces\r\noBo0LWNsfAl+IOsxWQpHELh9DM2YtTjVN2C6NsCod7DR09wGT2hFaLFfiavN\r\n56BiJCOUWoXOIOe+c+At6oZvlcC0FVdNcScj9qovHIRQNi9LzIKDl7cVhWK6\r\ntPKdXwd2BuygiHo6ZKo+vPzGx9hCjqOCgI4MINCdHVWe2Ms1XshH26i3Zmpi\r\nno56IoAOqBiTNLnkP4tI5+rBsTgz8GdE19dM6FnLyNAeldiha9jXPoizcXng\r\nXWV1fEqrcy0C9IHmItPSFpPjwDsCp21xbpx73LFXyYC+Cybd/kebi3Kd17d1\r\nBkHtZIeXk7k4ozXzKbKx0i2J5qJGs1YQfsE=\r\n=oHNv\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.14","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.68_1662459202189_0.23743655365858074","host":"s3://npm-registry-packages"}},"2.0.0-goerli.11":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.11","_id":"@keep-network/random-beacon@2.0.0-goerli.11","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1921328d7acd0d65d33a426e5aa719f44bce6732","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.11.tgz","fileCount":158,"integrity":"sha512-6pEnzg6tYyrsaIZAs82RntIBBz5XKbPzxbXURc3CpEieEBMJWNWhkp+MVwPgZ8ahR702GLLUIwZZ2ML1W9GogA==","signatures":[{"sig":"MEUCIC8t+KQEXlcr+9B9TrEkSh806sHTxI7EGltOYBKRSlZXAiEA7NURgqnTKcvW4NrX6Qn0ge2RqlEOjuZDCjyZZX5iNyw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22442893,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFyFbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqflA/+IthWmOt4UupGhrSQLxM1+jXaS1ZaBNglNBUfXzc5xwjEqEFP\r\nD+yvRKUQS1RWv5mXyecc9uRPDhN6j6O4yENLjtVNECobIL7HUFYij6AbmXUT\r\n7/t7GUCiH+DYhapOLFInhaGDXajoAm8tgPwDeSQLU6hwpxBplO4nfE+bu9lx\r\nAOn6IUamIF8vQKvDKzHsZoNhJ3442XWpdLZGsejYARK1plI//NKDZLyg0yPz\r\ngTzQQUeeZf2h5O1A6lEf5u2eFr1Uu5OjFKshc71UnHEWM5U82jTnrO4PMuYb\r\nfXr26TSaDSzW02JQzh1pnflcm2v8WOAPiiPEn7daTbJwElZfUDCXdiLr3EmH\r\n9iT67gNsmafhuFxaoMkcZpN0nc5GXhEwNavkuVSrq6BjS2JFiEBS8r4KjV70\r\nQIo0AMW9KRujwe7e8phWfCT+/ri0F0PfasqsGZQi/54idcED8lFMsaLMIhPi\r\nvZhxWTQYAL4kCa3KevEd5XeAGFrWtThNB7B4NuNfg5lE7kxDfiZE7upZrVfp\r\nMKWIpRs0rkmxQITSsijR88mrChJiSyI65z6+Z/+2IURIu9KaiYrhtPvWRIO3\r\nxoyjjfsn9w66QyFA7Xj6tKw/QFsSnf+7plF5U8a1rE+ctTSMQZMfekG48QZb\r\njpK0QmDbzLCSL2T8Sl2qRZCZ82Nj1AxfsSs=\r\n=QqBo\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.14","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.11_1662460250651_0.5908050019402291","host":"s3://npm-registry-packages"}},"2.0.0-goerli.12":{"name":"@keep-network/random-beacon","version":"2.0.0-goerli.12","_id":"@keep-network/random-beacon@2.0.0-goerli.12","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"304ce78cd0391836341e49721a8c69c9d6b9c313","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-goerli.12.tgz","fileCount":158,"integrity":"sha512-fY3f8iQrQNCpGYmeyF1eTMFeTzIuV/nA8lpMrgurtDmYDvFYJM46dauzyxl+93QBw3lbj6PkoVD07vViSCqkgw==","signatures":[{"sig":"MEUCIHEr/4kOHi3XxkCtXoAseM3P0naN6XQcwctz8x6tb3/cAiEAvCm0gWM437xwDQSibWb1O38gxdZDbE04w6hLl+UNtyQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":22444667,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjHyLuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr5sw/+MOR1daHrTbCDhXFI/J+rRCk//0gyj3URGB2kVU9UVnI89s2Q\r\ny8+nRy//L/YOANgko7lp8zWGX2Lcr32aSF3LLl1SkUTuWGi+u4HCXhr4nodA\r\n4xA5+ucIMZ3EFp7JlwD7QZrvVuDke9A0KKV0V0VbaxQF//jWkRZvpZFtqTSH\r\npL0H5vtnSR24uBJNvtxmuqdlU8RfG/0uDUkCheOnoF+frW3PFnMXDitEeK+x\r\nhA6pSSdI/CHnqy4qcEFrnuao3Kqn5grheqJCKrihRkM20Ran/pgLHuy3agCD\r\n1vAbfo2BGqyHiBMAsdRGeeFyswwGylivolpXLyOKt+sLQLtul4lNh6F5YxIf\r\nKklJLnWnC6a/Capn3TTI8seHYvLrPk6URG0KpMmrJJ61epRJO4mMv0zzW95T\r\nIcBYUH4vEt2EiVT9uJWCm/KqY/VkTY3UuTNmmS5EZY24NqHL3/dJ1nLSdiia\r\naibADERYpTnk7/cLArzz/Jgmcb0PcyDTK4stz7CxY2rLNWGuLHqqOxLjE6jW\r\nzkaDaVFkqfbyE1drsRvs7lV6qhxNoeMUoACakC27GXOQK41w3BbXPvxqjeNc\r\nYLGuPGdNb4xKvVbYNZyzSiSp1f6G8e5bLAp5Xqmgvgx+kyJMQRtuO9woYHv5\r\ncxsxaTOMcVRJuzRKbTpDx/yanqkoo+TBLKo=\r\n=yOTq\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.14","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-goerli.12_1662984942030_0.1765467370933409","host":"s3://npm-registry-packages"}},"2.0.0-dev.69":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.69","_id":"@keep-network/random-beacon@2.0.0-dev.69","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0c209a2d9bbf0e307b2c10385634647a724216fa","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.69.tgz","fileCount":169,"integrity":"sha512-EBKvsmZSLmIsbyMVfmuXw76Ds5hR8Dfc7Gqfrxv0GlBIt8ApyezZkmZ9lglGJ2dw4RPQYn+0VH0x/upP4/C5og==","signatures":[{"sig":"MEUCIQCY3tplom/VW2B21MpZ+T3UxbGXB6qm9KfIzTD+hgQfCwIgLki9Vs5+lrEjevyu3kFOFkh1lRZnQCsSWaMGAKAUA3c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24282866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIB/KACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqeqQ//cmxkjLu6j647hDG+WoNdABEXtcTLNLShFRQHYNaxZkUrmRHH\r\n3IXDld2AxeZK3sm+5Go2mTskhmqdeFx4FMrqcXjRvrkch1NTCvGoYgUi7Vd2\r\nX0zVq2cOBsbELeG3GqQ/BUi/T4ldVc14VZk/HCmPC08CCKE3p7Gek6R5+S4L\r\npNJyQSYL9V/M3dq/xNH8mSD7oUDfMceiZk8pzdix62TWHPROqE16JwGV8Kh8\r\n7lpkmgKZppsMsEHDodfFhc24hwaMu3pEdn+ojh1kYlOSXW7Cmn3ibZbCyHi0\r\nHHr5nxOG29rb6oL94IlDw7DlWvwmnrnHLff+u8iXm37kbZCle3Moaynuoe7e\r\njmJ5hu6TSlj9hqR9yeqbYRBmX02U6YBtaMeq3nR2XkSSLzQ0S9GDyjr1vYKL\r\nzgEaBKSpdbkZ5v84jhRsYwTFVGud2nu83Oa2zabitLsdXvDRpVM6oFw4THjA\r\n1WorxjJpbThqavhA08HIN/dHLjlndIft9KIv9NGSQbEUqm9iQ6cL8t0+pIA5\r\n25BK8zNZs1jtEU9X5Mcic5Rz3MuENWZKC/60ROHXdN1Tbd0HkkwqG+cZ9q7S\r\n+tyXrK8WsAgwCGh4JBRCLHfe3OTvK068GYBC3lnzqGnESdf0YLu7wIVXgJsp\r\nNmqFy6JLRXWsfd7JBV3/wGoN0MTjedZ3tes=\r\n=2PHH\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.69_1663049673821_0.5584558443614904","host":"s3://npm-registry-packages"}},"2.0.0-dev.70":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.70","_id":"@keep-network/random-beacon@2.0.0-dev.70","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9353e3e7ad0318842f6323b0ac620099bb35e255","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.70.tgz","fileCount":169,"integrity":"sha512-KDBLsQaC12QanyZJE+Tn1pQtJCaQk9qD3iMWGfl6WOfHwFTnKXk2upQVtlqXae26dcHj+H+NjaBZ2DF5QHK9OA==","signatures":[{"sig":"MEQCIAT3eWAzktzyCyVwuRExIPks4kWc1xfTG0IRcRMuIfvXAiBziOBge14i+7ZahJXK6cxquD2v27iZgQcBZsXwaiKV9g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24282866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIKhRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr54Q/9F0X+CcWFvuUP4tQWviHIpkFDhy/LBmXuP5XYkFo+GFXmybtk\r\nMdBlK7SFZdCK21iRZQdBxw4+cRtMbq4hv0g+NIuH5nRaB/QM9CE8bI3yKoSB\r\ntbipyUvzkWlZJYavjH5cN7icZ3yBKCnswUZkdkW98CX40CX5xYIJlbOFsMMh\r\npfWbOo5QMyJi7RXAUr4Bh6uoz6IRfGQc7Ok6ote1UkPwORWSVcJyrKZLSpqU\r\nZeo/u56bPaOZYPF3jBN5yJmNowciduCfYNZsf7VQEfhGOQkHu8LFs5Khnudm\r\nGe1q4kaXd+YXVfvibhooTGn3gLN/El9bQrZLVOt+d+Xa9fSHoy7nmiuh7XiR\r\n/6/zMuFI8Q9FFX/xHpyGWukoYSURVfI2dAyW1vBVoZbwwF5pnNUz1mTPb4Hk\r\nUhTruNRk3nb+5C26cMfpTeWreKp+7Ce49xmt7cMwsGfsq4h4eUhawRtblmd8\r\nYSFvNIP2nEtbhEs5cZpt+AX2ZsFL5PNfxz3yqAcXCZtnMGICjpMhL2tKeGj3\r\ndqk3Mqm5gu7SPOeoey9z1yIGsNMNGNV0sBsKko0aZVlZpwclXXpAc0UQI9I9\r\nhzAXXdtlX+njrQz9Xml/Rrncjh87+UR44hxVOvGahKgJIgCl58+Iu6Bt0oEF\r\n3nLO/4CX6dA7dEa8hXAez8YzT+MtQBEsYCA=\r\n=Ny4V\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.70_1663084624970_0.1784469117150882","host":"s3://npm-registry-packages"}},"2.0.0-dev.71":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.71","_id":"@keep-network/random-beacon@2.0.0-dev.71","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"56b273df2f9682fc6a46b3a69c96fc511fba6a57","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.71.tgz","fileCount":169,"integrity":"sha512-ikjgTnA9fKlJbbqwGdixG5JSNhZ9AyPEQFh44tzILkiWqq16A3Xf6sHJbC+5HMGsgYEaFPEEd0kfjmgD4lmXxg==","signatures":[{"sig":"MEUCIQDA2ad3eSZ46STM9LmKCg4Z46dWNNvxpwV4qfSVPpAOfgIgYCJMreHmo1L6FfFDl1nx7dQUDnKSJIJAJuVpjTIzM3E=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24314738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJFXAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjUg/+K+4cQg5CsyQwDdkUiRQsA3k0SZro6GaXZuc1ZDl8Ci/wkAmx\r\nniXhfXAzCSQzcJHb3v7L8pzjyZvcF5RgGgFDqlOn7fhwkInFySC4VFPLevdp\r\nstAfZN7bIKAMixgxrn49JuNAaWmOfjyuDWpzmNaw3hw40QxDsOqWx57pGhgK\r\nCXL1DVH1LuHrymvOLAkBqVOLb/GXokC1ZiB1X6e2OSLdaQMLAaUnDIszuRcS\r\nkHGfxIAd6VCmW43DR9q3QEB/UZh75P0HbxaldkHoYqMlvqANcANaJn5EF3U2\r\n30s/ubbJR0AVt9KOaVtuzYFjlo/DmPqrbv2elDjIZCT/aam67C+kjXaFfxYG\r\ncODHMjMBwVME8CLEc0Be3D98BqwPvgb2I58xeRcCO0wE8ai41EWc3XLMTiEM\r\ntP+a/5gPvbtJaxz+DF/S7n75fzxHw1pZo6fzp7n/HoB9+e/L58hc6MfghKIL\r\nraOLtHiivQBojlu7HzXysIUqdEhN0z61F0Eo0+XbU//dPiOJoTDH0665xrYU\r\nwtJBdmCtjmzjGsXAzor4SlpG7UWdNIwG9+p+5YIdJN0LcGBP6992p/LZpdgF\r\nvyF1COatAWhI4I+BLmXmATf2IhC4Cc5KfqK5AekeU12Narqa+mffM21XSmqv\r\nuksjXdgS0lxtZPTdATY5YgXl87kyXyK0eNc=\r\n=lAha\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.71_1663325632285_0.020900716658138085","host":"s3://npm-registry-packages"}},"2.0.0-dev.72":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.72","_id":"@keep-network/random-beacon@2.0.0-dev.72","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0e96714628ac4fb5a3cdf67d5a7f56c6b67b4f50","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.72.tgz","fileCount":170,"integrity":"sha512-mx7lUf9sdtK/9/dYV0TBXPM2zA3xeKqkspII2T9LZ1BxX8IhELvmjrgErzg5Za5IXUDQYFUco4BHU/fpjR+Q+w==","signatures":[{"sig":"MEUCIQCxK/zmmf5m5hHnD3LyEQ25vsE89+oq4J13kj/T+s1naQIgRlHmk21dAVGkgK1nD+NIN0cklu1CZhuxUvBIqd2AUgk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24666057,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjLGoDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqTDg//dO8nQOe4015AGlMuYY6oQTXn2WHrFIq/AN0x6y8ixaCOMNzk\r\n16MoM1uYAmK0N/Xmq8C6cC886TInhAjPetMJM4sWhIilWrr7ogLGnRUKFPQD\r\nZdW9hrmoK8hjR2xJuCscRfZ/LnxQGbGI1G13UXBVwLi2iSZueeLxdiPJCxMQ\r\nBDyNWnYNWzFpaLNiwD4T536idpT7lPT7FSJCvZoLJZwSjMbo4iAgnv3RdIR/\r\n6NenNrgt77jrKg7k/eGpQdBDNwSATQiZ4ufhILoWF/UZQRcUMViUpu98cJJY\r\n9g5kxlJnrH36oQOy/S/kCvbFHFocY7ecr+cke08vW6mbCxHt0Cx2mPKAP2pr\r\n6Z+gr2FmgRGTgaUEfEe5d0lRdep5StZebL2RGWuIvt2FieVuBnDZ/giUcRn0\r\nI2bF8V3Oug1VhZ1BMX7fwtIzL8nmEoxqmfi2QEHTtbcNR1s4ZcoOeaGEI4Qb\r\nGpVAYrA4DQxcH9EGoTR0aqBZmWAv7VW3cXsSdqsk6/nwDdm7jNXXOeXyXBFm\r\n100EOIopEVaVcFkM1j/8KINYed8PQed9APRmhBjtVmnobhMubxQ4JK1Tv3QZ\r\nZ7YpA81Orv0C/qYGKtG7lZYqHuAYPGodgRCHm6QLwfS8/3F97BJbQtjoM6Lj\r\nq33HgCFVtRKTC+GjZpphAIKnhbdBOVnrJHE=\r\n=TsvO\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.72_1663855107518_0.4785016009925207","host":"s3://npm-registry-packages"}},"2.0.0-dev.73":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.73","_id":"@keep-network/random-beacon@2.0.0-dev.73","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"847f487b0fe8387f88f25387f18216ac443eb8c4","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.73.tgz","fileCount":172,"integrity":"sha512-/mSASMi1loq95GxfxRo70hPze2e7ovTrQ7aDAqdE+5Ylao0C1XALmdtm2T0XxPIjDu+ZwBxRDF+gB7c7uOPTSA==","signatures":[{"sig":"MEUCIQCo/AKx308pNbmUqkqbJftnfaHXgc3GqC82ubvTbbscpQIgOszgvKSZUwcTcXCB8nlxQuC+UtqLUmeaHNqCg4bfve8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24672381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjMGImACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrAOxAAoT7PqFtPS1kK2ah6/8fVu8pBIhNBM83f/NvNsWLwoPl5Nt9B\r\nPlfLd97F9osoEZnm09Fo909Yj/Ra/8pYxV1vtKbnwrBXfBLnbNt3Tdx0J/H8\r\nj9gY2VOzKgefU0nvWVpdBxQqrn27utx+uXukG+uOh2QSMR4BZGNhluA0LNCz\r\nYV4gfnj1nu/cVv+8ciF/FeOzcM9ARyG3sxqxRyNoxDhY3zqJOEApuLn+ilt8\r\nhGfslJLkTczSq0bi2WtznS6zv1Xfh+ak/HRcBZiqUZmnMudYu8wimi5zxQnd\r\n5WyskS7n8v0Ox/mo+5FwjCNdcE6V6V6N9KzyH9W1D+4R5G5AwCblL9vGNYX5\r\nVuObBd8G7MlW2sjvfiOu42KaOPObACIZ7MpfsG9DRKx+JoEkBVKAV64US1Y8\r\nkrD6JmBYsTzvho0h3b/aErmu72Tr6DH75XJH95QmTNnCzAk+ufQwot379O9r\r\nD0h7IBlnO8OY0I9Tcgdhr7OhG0FEhWQ/G3ScAigx9VZcO5BUijrkHbEDINze\r\ndClaQJd/h9jg2y5hz/0l9TeRNb7+gwdvjeCwhJK1yEj7qKHSqpMIGJQYHOCv\r\n3cRQKWTvYuf9MYvpFjSZOVJuqmqKNxRJrVz0iEpu8HH/88vXQi/OUDjSTgiS\r\nhLO7Jl/llwU38nl1brqoc57rFauVQCwvmo0=\r\n=SLtu\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.73_1664115238210_0.773907182335847","host":"s3://npm-registry-packages"}},"2.0.0-dev.74":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.74","_id":"@keep-network/random-beacon@2.0.0-dev.74","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"73b510ace23d348c0f18c52663f6f7160f297afa","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.74.tgz","fileCount":172,"integrity":"sha512-uvF/clZ+jG6PDQyZwbPKX/HjEOb6ew00OTA6eiUWYxKyknDT85qKGV1rxJ/GG+4N37eUAtejOqcD0nmwB6FfEg==","signatures":[{"sig":"MEUCIAP/MF5A3rMpatnKJM59R5KhEbimNgVUOwTuGpVjFxKkAiEAgKOFsIJAWFoVHyMwd861PYxMNHXLE+poXsV96pL1lmM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24672381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjMhcdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrO/RAAgJQPs+4japp4PzNG39VcPGrGKyx4Ep5FP2GGieAvp+fUwaZU\r\nOUuoGCtcilVK/d1OP9QtuMhnQQJgRhb5Tgb+z8lprciuAhPB5FGwsf37MjOf\r\nB456p34Uz1wmbj8JpAnsiM2lYHry/W5Rhv6dqYl68rUoJ/JBNn3xhHzMgOgP\r\nmg2k82Kyhmet4zWv2/TuSghWd67fJ7Gl98IQNsAvDCZoPWrTVr/nAZr2cxeQ\r\nEZtXPDo0CXBfS33ow4rYUk6oRSU26Na0168qSV1tL79ewBQ9dr28XIOObg8C\r\nOhdSVlHrGG/GY/Iro+wnKBg1J/tGQwFL+I1qcINhjcTl/Z5njsDclNXSiUwF\r\njxEnw5Oa2pY2v3tKNVz1d527ncWuAHe3OjapjtDC6VVwFteyvZaStnc8wAec\r\nflm6CoA7MFpY/tV1o/b7ThEbVnCsezL00LT5C7K++r5V0weKRSrTIXpMycKb\r\nw4TUAuwTcxow98xwrp4MFcfBoeyPxfnObW+4kuf98D+e70WR0BFLcJP5oSQF\r\ns+PsMWB8zwZLwLLXlyvJ48BlXUtAqhjPyPPmVm/12Q1N/2bvLeipoWnxgMXN\r\neLqzjXR1O+i23t3Ah5agHbbC2m8KF8D6bffHLs1XvDfEjL/zFlIt1e19UfPn\r\nqfdPGPc8mSETsa5cWpCt1DlIWwlerHQYfr4=\r\n=bZN6\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.74_1664227101284_0.8844674514268753","host":"s3://npm-registry-packages"}},"2.0.0-dev.75":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.75","_id":"@keep-network/random-beacon@2.0.0-dev.75","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6c8e71237b3d4aa6151975a7d1ea4e2a0a14f250","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.75.tgz","fileCount":172,"integrity":"sha512-sMA/ihwHrIslIXG/ymgl6g5WQXIEdlSW/lBZUfH5+i2sC+8Xhq+wxKh7G8DOqtX0R4QYnvf4hZWwBHT7pVUD3w==","signatures":[{"sig":"MEYCIQDaSJ5PUlABh8Bt3koi7cCN80rYNC5fxCKgIPb2CMkiQQIhAMMksQrKnzNe4nnyMjXEKAw4dgNwefyjQS/PSP2NIDBy","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24742808,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNCZmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGYQ/8DvIERi88Uk/SrAQ05P8dnLAzwhWOn7o3EICwNzYrhSymb4M9\r\nmv9Gy7v0Oc19wEOsizOTK8uLFYwXhlgP5ONkqnycUANEdRONAAJeKbzCviTx\r\nzwG0qocWl49y/JuuwVV4gk7kWXZP4g7oxbPbuBOjYvCAvs/NDLbmd9xvMAJi\r\ncycemI9OijQme3V5tnAGQWwo4dc1Rl7zzHTcvKQRZ60F/3vWHVZSimn7NZ6Y\r\nPY8IuYVEqkHrYy8ajER+QCAvf5yt+P+KbpIQfVrcdYRAHjIJZW/O7d1MQCrQ\r\nINsVn0njE4GI1RNlJZQQI4pgyQf/+JiLJvvHfhXLbNnc9z/NrOMwHXmv6YJa\r\nidGwDe5GQ7e01zfW+OaVuUMiUnersv2Zcdj4iWrsj7TySuQTyf2CHFzXVnSj\r\nvybKJ+u2jjp5MquNJud33HovVdilK63FvOJctORFpuwfaCNDFXYQJ9r/nSK8\r\nYi/44L319wqvF2X9Mu5CC+1Lub+KZQu4P7oOqmxZlAVhEyO3X9yoAKlq3cQZ\r\n2wgflUAgyU2L3J4X4kCrE3NpHfxh4SCWx2L3t3ffeEqzJ3KAacmO5X1U+l3x\r\nU1pCjJEjLRxdKm4nvKRvILlRPDwhJzPQsLGA0YmxR42sU13b3zXmp4RnbzhH\r\nefGt56M/IuWNcb1R2vRy1DPFcAJ8u1YXYlI=\r\n=YObL\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.75_1664362086164_0.8987015208083478","host":"s3://npm-registry-packages"}},"2.0.0-dev.76":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.76","_id":"@keep-network/random-beacon@2.0.0-dev.76","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"951b3d08e137eb6bc1fe5bf9b347afb65aff3ccd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.76.tgz","fileCount":172,"integrity":"sha512-IzUbGdGQ4rbqr0BIzlCxMazXQ0s/V4FzDFpIqqQp5phgRdQx7xNUHEk2OEITu+rFcBywMzEDtSBwD30EjcjzuQ==","signatures":[{"sig":"MEYCIQDmo5GPAIRaE3EvYKYVmAwOOv/HWmDKh8S9pSq1voC6LgIhAM6qj8krCynXx9SY32+osOLe7exbEVeoTe8H8chYtUWT","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24780152,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNELEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmodag/+LaejLDAtf8hgwCThlqoZSNfGmuuVF/kobLzP/K4Rup0EQ5PG\r\nihRKB/3zpsq7NJuczPMfKCPHxbbnsCJToyZtKevHlNqVvlgPLzBPhgchVA9f\r\nwTFMmwyNperj1+4abl64t/xMDqcLUoyf64zuE0I7yXptST9AjBpSk4fM9XfP\r\nqNHn1wv7bdDw3hWy5cVLZKYUwzFT/Hb4dG2avKNQb1u8+2jYAotyBl2RR6Ym\r\nZvx2jQNDtp9DoYMVyFpvMJo22/Wsx716793r3eBOlPVBchw7vwimBwEUx8x0\r\nUzZKr9TZwkhkp2/mkAvDHf60v64+b3l63VHctGu418WyHj1gEp1kOYl7YL+g\r\ngLXUbqyEYxepSwtARu4hHwJm9oOrAJWT52ICxWPbnzncTMTTamVLtjgbyGP3\r\nrKdsgKWgUXqaeDWlY2M3WypeNOFgAb4J1osZb1DeqmKql0HFFVvC8dRdK1P2\r\n8nK9Bj02TttLKAxT9GJXTq7yZyQA/zJwlu0NCx/PbA0JnSK89+Jxw8HXg+xV\r\nCW4M1zd34iAEStqXh6uL6zvnaoYvIhvUTm+ro4L3W/f++pFRNeVRx9Trk6Im\r\ndjuRX4DTpmSRq78fgU0oEdp7oSizrsLjjrDd3eRFT0z/ZDiAe/fOxQxvPbdC\r\nLSnkAvs8jFnmHvHGQscJPkP6R6KUKTD3mdw=\r\n=tqlO\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.76_1664369348432_0.7860643920401724","host":"s3://npm-registry-packages"}},"2.0.0-dev.77":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.77","_id":"@keep-network/random-beacon@2.0.0-dev.77","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8aa045e38f436d5dcc727b281e33a4d527cf046d","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.77.tgz","fileCount":172,"integrity":"sha512-QwQSlJtlSx+HqmQ/BwpWeucde1kYSfo/5gt97yXPiimDSaMH2JeYZKMQP/lveTv9qhZ8WqWkGIdzvpocuHnRPA==","signatures":[{"sig":"MEUCIQCKqM671tj4WBBL1/YIdvKUljqBTXjS/Glz5h0O3SibOAIgBTSJ4NsvdokrBIPaFUPDSKDNo+iDI+GQ8UEs2U7nRnM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24781978,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNGhnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqCOBAAnSaIOgp4zInG+738M1RplvJOBDFrNEZu6y3MV5wtchnrvaW3\r\nempbIPBTZ7zNSpARryKpUnOnaCSC9sLXdY8JNkX7VEt4QnLp7jAeKy/bGad/\r\ns2GSY3lphuvqHhS8OwNGREiRuUXSxTvegkaVHaWi1x2DVlyT/u2syBlX5fM/\r\nlH9mUA/0RNL9q7SUsWdN0iB4MMRrLeNLGzD5VyifODSPK2m9sud1PSw0A6lm\r\nXdTbMRw6lGSn3A7MvRCRmgOii2pYJXcwMziH8oENr9q235pU6S6umzns/4a1\r\nDt8joSf7Ga9SYGCtjMtgFRUyMkYYO7/VbKGJSJ0G756CYppyWpR/8sfUxqOO\r\n3a+efC0Dr9DS40tLDHmVxUpkvlpKWlmzvQRAlPocN7vPrjb13/5B3Z+/X4qx\r\nQuFjbhQHBV9iCZJfbUg2H8WPiSKd1qv2BrrtCa21TThlb2/vEGyrpQwlcEpd\r\n75O6UiuL/RxaI55KX4+9QoWqkEigzjkel9iqM9XY7x/2KdLORllFGWkUxQGr\r\ntL2eMFf8bhSq3teatBn87vk962LWB4icZkclOgTRojbHSi6roPq3pTghPEEe\r\nkuHjs/WmlX4SLcLWYe+A0P2lyu5fAjYCvr9BqnUR3A/XtFnUnquuxAYXYKYl\r\n8q4oedHimAhrY6EGB938YAZXBiLhL7WyKI8=\r\n=o4Ka\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.77_1664378983585_0.2390882137787136","host":"s3://npm-registry-packages"}},"2.0.0-dev.78":{"name":"@keep-network/random-beacon","version":"2.0.0-dev.78","_id":"@keep-network/random-beacon@2.0.0-dev.78","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d38f77d8a14e4d9cb0d59cf6532527160406f22a","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.78.tgz","fileCount":172,"integrity":"sha512-t1Jxaps0bcSQxS50T4IGPAPmTN68AsZ+WfumVPW90PhP1yISBWU1VRuM/kUIUCilC0ng0FYB939FexnFcyy0pg==","signatures":[{"sig":"MEQCICltrdfEwWnlbwkUmRYosoIC5dTyBK0tKufHtguzbOhZAiALNCCc1Mg4rU/SviORw0fXgdE44QvGLoARKpH2BqOPQg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24780753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNW3rACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpwLg/7Bk2gfyKHtETuoJIlo9HeA3p/Tgl/IeTrdvUNF+wMRNuILYPe\r\nmPs91HSWNbsFh/0ns9+nri5DxfNcpTp9saUKcIJcLNrAsy3P7wGVO4d41Mdd\r\n5VyvjpGi0xHNkCoBfEFIcrWU+ShhN5PH8wMWXYHJvv+dqjgRDTl6mvQ6bfcS\r\nNpBd1w4Ufwz8AAlHrBQOe+XwhM5MhJX6JtBFGjprdSy8zmqAuFHeb1VVnZNT\r\nxoUCMm1rcXUV68HSj1XMl486mEE4HmcOGGCVXGJUtKVlHLyHLiydlVWTTkgN\r\nYWkpp/S5IT96lZ787JgExl9ZMHqXF1by6UF1u8FUC5huBE+AHFvDhdoHghaQ\r\nazMCEBzoJHJWRewe7T9X4Ey3xRvhhkRodyHCzVv61556KbaXs6C+0qfD9BeZ\r\nd5oZlLek4KsKyLJMI7kjSm/QXNQWdK1qXFXtaDaQOrDc5vK3WZ7l8fgIwQbQ\r\nEdkh/vJ4hS0UvWl1Pm9Ys7jQtCykbUeSRiugLhnH9ulMA5n7Wo1j6anThG8F\r\n1Vdfk9LMKDhwE3ZFJhviMGuzYKaIRpFQ2u9EpXzBX9FW8DbmSEcJ0CD3ACck\r\nycJLK7vT/57aw0EPEP4r/pcKQoxE5ZGpe/5CcQ+rbBC5ilI+GcWItWC4Bhv4\r\n2JZhQa/9T6/D6gSlBOMV26amA7wPeUjodns=\r\n=AvUM\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0-dev.78_1664445931126_0.0937030479734049","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"@keep-network/random-beacon","version":"2.0.0","_id":"@keep-network/random-beacon@2.0.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ac52dd03da49ec5b0d39ba85c51de6c4c8f1b1dc","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.0.0.tgz","fileCount":153,"integrity":"sha512-c5AodlBkMRTEID7bDE7BA9lqDZcH3AFqOPz5b9f5syy62DnuiMkHTHIEIvdWWCL26m21A4PPyPjw7XirauE/OA==","signatures":[{"sig":"MEUCIHYRAr2sul8pP7OKKa5KdXQ6prVgaoPap8/eWhANk+QDAiEAgbNqPQo9c7wayDbyWfCHqAEiYukqSflA/SJwAsYX/qA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":18078410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNZHFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNPg/7BT2HPHBXt8MgUdyr6xydNdbeFie/znW2a8VDpEyHjWZDYDve\r\nd0Fkx5OK86q1LiKcoY6Pfp4l1ntYU0gfY3lMPPWZrX47x7HVkm4beLsvizo0\r\nME0s4lsO+z11pdeWB5D8OBGChilafiQYZ5MRHRAazI6TQpWMl0W1W7+ysyTx\r\npQ6DYaVIxMIWYzSyC307Knu7znHdZ1oQP7UotM8UIv1NOHE9i03TR9Y7DzCm\r\nf+bTT8T41/5ZxBPZrM5sBqVf+kp2gIRt/0mypqlZ99O3PjRSZUpdwY99jAV5\r\n+vbjsbEWlB2MMnUlv+he29B759hUOF0xBeUNPuAJjgMWcLckGO0l+wYN1Mjq\r\nibwik68ULFSZLYGwKdZF1bErOrf6jhDz1f78dCRi0LrY204QTxo/1CUMX3/z\r\nZgQDOpVeTpvssCfOVOYc5qWSYFEKIbIDE54YDab9fOvdZ5HIQT7yDGmn9SUs\r\n64apYHFWAHTLoPnzaZoLOVPq07TFlm0BlGcEyhiHs46sl+tBPIDcPkbcf/cn\r\nK1mcnJQ2fyp2rE4xM0l11of6JzRnIgRTkXNXeb2PImMpXO2Ou+EvF/PkaU8N\r\nFGp2ysp0rYxg+1Fh7gcXtQT01ldbKX1uVhIksUQuGsSUM9/lUlL8Gu5Of+hC\r\nlGukG6poIrLLnSHwn676s+Pqi0ldrq2a/S0=\r\n=fqNa\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"gitHead":"b95439a007e6ecea9224ae167394b8ef59602c13","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"_npmVersion":"8.15.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"16.17.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"2.0.0","@threshold-network/solidity-contracts":"1.2.1"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.0.0_1664455109347_0.5110672558346216","host":"s3://npm-registry-packages"}},"2.1.0-dev.0":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.0","_id":"@keep-network/random-beacon@2.1.0-dev.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"b74dd3297ec89def2370c73d59410ed9fb4e9292","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.0.tgz","fileCount":172,"integrity":"sha512-B+uAzt62yKBSzeEe+4l4zwQzLTwWCp3HRUinWyuDyHmfJlRhYMKo9UBB3+l/Oxotr6JUgMAQpLIOwvAtcSS+2Q==","signatures":[{"sig":"MEUCIBFhZ7Cn3ke+GTvxGeY8jldKxcXtguxIlbYazx79csQjAiEA2EQviZK4iGw+rV8gfE3P7v84+3pTDdSIVYyKG+/F9No=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24780709,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNbq0ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpPww/+O9FGiu92i1wBg97f06TYzdQE5xP1VRQ/1kwf5lRBALhnjOMp\r\nYgBh++GqF6sHz5bbXeuqb+CGSyy35rsL8o1s3lsQNt/irglSBEXpHnRL2Z4q\r\nb3Y+xpeOn9E5rzePmDrqqHrmJE9ctgoosfjSieDnv9NgiByA5oZiG5mp+ebk\r\nC+C7bxQrRm2r4lYPsju2zV4S4NTavnqt9o+hFZkcYklNtapCZoySQfGQ/I2o\r\nPLHDWwohbKBKAHvq1Ix7B3psZZlimZp4+GuwJmELzSMm52t8w4vE/1BqD3Hj\r\nQZKefjBgxuLAfqc7JSICeneCImhZYrymMisn58oTEm9njHMb+DAQcrKaXp7D\r\n4K68VwyZD3S+cTte+HKb+y5Tsth2xjnVEf6C93KCtCODT821HrFnwlGchZEh\r\ni3fAvY906L5KLYXfnNO4EPmV9odXFpZnJmEpSzdncnPWnOIQkfZpL/hTW8ws\r\n19tw0u0ipYCtqTgbR9lmXQWGWZ/cxNPZQlSQL44K//spLV1tnMpwdbi/DCdi\r\nnasZcg9yUjc7ve1PYDXs78iFGYKHVALgrxu39EDw4fWeC6nIytQ33c5FKbpg\r\n9B5CQBXiOp7tI0x/LnUXD3rv4zRPhHlZJHp8xIx3Z0JcqRiRuVIvDuUtfE8B\r\nxpUbJNYO6K0us8+r7ndpEV3Hu3JLuvWRMF4=\r\n=R0Bo\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.0_1664465587852_0.27737936622153625","host":"s3://npm-registry-packages"}},"2.1.0-goerli.0":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.0","_id":"@keep-network/random-beacon@2.1.0-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"369177c209bf6b59e9f73435415b25ba9107dc02","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.0.tgz","fileCount":162,"integrity":"sha512-UYIUQc3OuvTqxmxLFjPYPvqtjdWbuOiDT6ly4L7mHoJfWLGl1FrfiBIpkX9JVCPaMCZyiXjx6itEL+mMqL90Bg==","signatures":[{"sig":"MEUCIQDFHYAXOHMkcJbkH1GmgF3E0A5pboJaBm18jXvKlZYLLwIgKQUNaBDtYUNXAlBvUXURh11X9SDH6ljshkIxpHj4d1I=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23509600,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNc89ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZnQ//dUg8yu2J2p3/un2kvmUgLCmgGO7JH1fHQgrU3l7DyMqUa2hj\r\nooio1QpiarC03AQM81d+I6LWcmxIXbZxU6iyWuMEcRQU9DJk44AwIol+CInJ\r\nEXIWPTE3hSMPEl2Z00dG9x50GYo7n2b5r40Sz2muFMwFudQBfHZVlFWNVmFa\r\nKHqLNJtEX4jOjcFygwNGO+a5NBohylVXchwt/XepCsfc8JtoJk2m72cyR819\r\ntrAPd+aVMiuxHfz+hQk5yLBEjvtuwd9vYZt3F7uw5ZMP5FYQHEG7ADoM+LfR\r\nhNbpFjicB09mT9avq/PsTb8MKETC5axDHyy6o2+J0m+eMBImavrCwskXKE5r\r\nJuYEDcvfKYUazbYp3zaREc3Zyh+uDDwNRzKw/LdQ58khYKB1PYEFvTNRYt0R\r\ncta1o7IKm9ITzoeIDZxPzNl08J0gGClGnHfJiPCn9683eg3r3hl7Lti0u3+L\r\novWQrQt5dcpfdmbXAWbL8+/dsj2ZRvS41DWGtpNMGSGoHPJ6xCQc0NeyJaQK\r\nS/rctZPyKvzknRRW6uhz/g1XoCTHijAs3rdritUCgKEyuEfceRVKRtX94fGx\r\nxEXHxz8VOe2FbheyiCmEOKehzX60NI7LLn45XDEzcL5S63M0ox8l0TPiCQ5v\r\n0qwS4TJ2dTC3PDAu2Nf9h8ljrwyiKRshbt4=\r\n=jwx0\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.0_1664470845586_0.41565231757865795","host":"s3://npm-registry-packages"}},"2.1.0-goerli.1":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.1","_id":"@keep-network/random-beacon@2.1.0-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2ace7ae85d4ecf2ff9ee59cf6a38e65e3e81eef1","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.1.tgz","fileCount":162,"integrity":"sha512-Ym6q5PcA7UQAiZWeZe8TQAiH7cPfTMCVa9EFQSz8nzZkcphvsVp3seWWjMAwDI3ZxxBKVCvC4DzfZnUi0mgavw==","signatures":[{"sig":"MEYCIQDR9bn4+zLkyi00CX1V3CvRx4r4GsSCy4GS3zAuXxbSwgIhAIihsitIEbrls7e3v63HhNi9XZIaw2s5Xwo+OKLKP1BK","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23510077,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNo4EACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGFw//ZWUmSgpg8UV5dxfiGStAf0/lGvb6/BDJyDaKPVofWbwNxtOv\r\nisU7v3O5ljeYAHveg5lLxFhy7VOwoXixRWO20QPZGNXEnHw/7gs/6FgXOvcY\r\nJHdm0jLqK2wxD+JGCQ8RxUesCX6BGzqC8ElcXa4YRgnqXngs0KqmWMUV721w\r\nZ+G0edrA2VR4flc8eHq5y4o5tEj/9a61XurCWWon6ZvXSim2thW8d7zfLTsj\r\nur3fFDRkA3jws4b4p1nHtXhtKIGT6IJAp9i+/QLx7eEYoeSLSnanDyZkQd9A\r\n+H6infCib4P8NLBtPFBPo1Tacexyd4kKtJqpHFv7QGnfamJ+AV28K/Oi3DAS\r\n4LHY//GwqazGmX9b/nxvdOwQnhYE+tj9MTc4DAqZ8W8/ml0hwk72qgobOOOd\r\nFBFQZO+FaTlSJr5bu0C79fMOWqpHH+jIDkG6nb+boNNzg8YIVYKGwY1pjLPY\r\nTUeALHchZw03JotajwRXENx1DT1Zsb32cc9O2WFIr40igjmmnR+TJQI7ZO89\r\nTHjj8IK3Gs1nkv316HcVb8WwniL2U6NQoIoXWIV7aSVQBhjw7cs75ppuwSM3\r\nVjRu9WkYdYiXvRh0npoXJn+PPnptcE0hDlabJ/DEgiyjK18JEghC+pQMfknD\r\nW6jKDruW/UZObES8G9E/a+mYnscFK5FL/qo=\r\n=WNV3\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep Random Beacon","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.1_1664519684402_0.24743077544535397","host":"s3://npm-registry-packages"}},"2.1.0-dev.1":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.1","_id":"@keep-network/random-beacon@2.1.0-dev.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"197422cef030cb61b0b88fc08a59292a9efb3b28","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.1.tgz","fileCount":164,"integrity":"sha512-ppCPriGEhyc2Aw30wu0ujLphs6wRUdPYR345Knts8tx/z+D49Xg+3JA5tcUiPgXBnJnJJ00sk6uHXdhUS3LLDg==","signatures":[{"sig":"MEUCIQDmZaZYjT+GGdpr34onaN71XleO66zGo3C8eNqhYTnjcQIgEUKFX04yNDasmvWjePdaku3Q58frYpr9xf9T3xwMRa8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20005970,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjodvaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq/LBAAgHjsh7A2qUKLEyCpXPfir5Q7c9eHzQL6LZAwvVVrQ+4YwD0C\r\ndXYThoZ38CYuaaBUMq65nehwjBHdpI1jjCCkyxmF1FKAP6xQEM7Pjc1ikuPA\r\nLLniGeQLPLVLxOP40eYMjh/dlRufq3RpgBvIFqvN0K/GNeXXa9Fq+UvVnZF8\r\n50vlFTdmDy24yVOqVwi4//BlSrLOnjke3wbFEXpObYs7372NykOoH6ewDlSe\r\n+ZfEDvRORWFve4liEkTopEEER163Bpvnh7FRlIWmeJkLSWPlKYX0UJngvoaL\r\nyc2HT2+fG9kOHB8RBa2gq+/LHD7V2HtRu/FaHmm/IG6/VP683UaDi6PYfLMN\r\nzLyZfg4uiEbMtmk0NUB6zfRLvuHzcsmeq8UJ40mNFSjx/cgx2YLlnSwP8cG6\r\n4+Ml9IA1NI6iiLWzKqBdXnNzxlWRfzCNspadhBQKmGo3UMmiv/D8pNO8biCJ\r\nswm1pUm9EZnxmgrJmtiJv0MZzTF6rCQFezlJXYgd5UA7jD6GgyNNQHsjz53M\r\nG1pFGnA/wVQzaIvkBM50pczgnjUOAa6ewI/ZHrMKcvS7UhTPdhIcgkwFV8P0\r\nZDwSeYi+SBNrV+kGL37VkntREbEgOn5RhFJ7ddLsRaPiSGZy90oM5HpYI9os\r\n493nuqtj0/bySrQnNDScQwJAjHchIJ4ITWk=\r\n=484E\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/workflow/status/keep-network/keep-core/Solidity%20Random%20Beacon/main?event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"3503419a53c3fa7f81cf927a0829057bdad7d798","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.2"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"^1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.1_1671551962151_0.028273585817693236","host":"s3://npm-registry-packages"}},"2.1.0-dev.2":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.2","_id":"@keep-network/random-beacon@2.1.0-dev.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"aaf5bb4780b6aac6e71ba6ed8ecb29b4ccf6ae81","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.2.tgz","fileCount":164,"integrity":"sha512-Xc4MIQ65etB11GDLBPUvO8XRs6f2DVY0FmPF2uU00x8/fOIg12jjyyBQwNP1jJ+OLf7W36+CAZcUtj6kI87EAQ==","signatures":[{"sig":"MEUCIEvxJ3a6cylozcjuV7kaejq9siOLtaqcGdldqxfrXAcSAiEA6aH6JNbzLOs/buKBdWzN+nN8wP8EglhDbEH9r4DiaUg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20005985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjpZXNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpqmA/+MUeL/4iEDu/q67ZMpDQ221KBejZ4P3IIAelofhJH4sZaq4BP\r\nG2YO2liuRm+QamZJDhtLjPy97kvqVzuS/MtUhqSQ4aS5kSnZiTTKqliyRbPd\r\nfkAKT3zsB/f9wqHnKgASumtdVPxKoP7MPGNYJd8+RiEwQUDipaqdT5CVlxGM\r\n71trikiGtoQOxyJrof4n8ZrIRfJx6rX2jw0oobdvKyFOcniH5p2qRyLQ5gNY\r\nJSUGYMBgwXV2lm1OihGSQPOFZCJbxQzhz7dks+Is70D/W5PkHEdtzBiKlnVF\r\nwXnUrhJR2IJgaM3mnpOKW0TR6GrWFxLk4EY8GeHzmSkZWF4jiqn6K9cyG9Yt\r\n8gIkdYJK6+Ai7KLvTFh4jHa9pNItOUFj5dVj4NkUZfMd6ZfnENXCINEh+01B\r\nq9WcF5EfTLvoLfY3q0CX5kjy7ZYOMnfZZvEi3vadWqArhFOMjDoAyf08eKnl\r\ndVaWldNLaC7HhxJ1ZlfNgvu66OAzHha/NAOUEt6hl1XtaEFVWD5oKXteq6a+\r\n+yy4GL1bp085pFJwNrIMebl+8PJlFxvS4kkEfgdnDCxQ7Wy4t0WDDRRbtxyr\r\nSFGXyOIGGRtoOP12dFXXkpM/zRmRtrj1cSiJvtOlrfkaKDrf0RMkHHcC2n7l\r\nWkRASoSj9JCMWogrsF3uBiSNM0xRlPbQYHU=\r\n=p/4U\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"3d7f75d51cf35560bf78d330ae71e7e1bcc9d358","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.2"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.2_1671796172752_0.7911450302556409","host":"s3://npm-registry-packages"}},"2.1.0-dev.3":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.3","_id":"@keep-network/random-beacon@2.1.0-dev.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"39b1ba1c3992ce98ef1203114c6fc303df940463","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.3.tgz","fileCount":171,"integrity":"sha512-j8GcOxsZXABqJ5smiKxGbbHYhehrzbXtufG0hsk9vMnDSYSV9kLk8zz8rEJ3XKMFhMfy64z9t9Rv0PlAfmXVIw==","signatures":[{"sig":"MEUCIGe5dKn6vuqEjJprSIY10rqIvvwcrp5ChOT3poZwhtk/AiEAlKW/q4O69MuTPQk56pPV4cmz33P5b0uBriVYltO+AbU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20220709,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrtZnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr+rw/+KTVGGDYgSuiwYcdOs+J/O7xkos4HE0QXoRV8XtcfShEkXRPg\r\nSbnqn5kD9u2FX6e9AIEgk3BjJG08Zbi1vgqzkTi4aRF2n8l+AlltMq0zNyV1\r\nYfrMNlCci5CgH0Vmngp81cf1FKMh+U+FkBac+9oadZ+I+bqtKJx9ejlkCSN/\r\nsMSiEqmyo4TMmzycJz7fyx5nN0Mbe7jISF/VXUcoTLDJJCi3EwYsCk7amXcN\r\nRm/Us9Co2QhcdgbTDJUSpDO9qi6iLiDe94i73ezk23wOSqdEv1WQvKBSfHwQ\r\nAmf4eIF8yfUlhMT3xl/RL/Q3TeCqkXn4+17pNt12GIJGkWJzJOtD8eS7XzGz\r\nFjUIXz9FAr4omj5Lp4VqZwaSGZM6tYV8XfJYWeV6NRHM/aQHMKGTmTAkBsl5\r\n2MaHh6Asog2F4yPQ6QEpePzZdK9lE/QXVVNLGNwFbFwzDyPlAavGYooCaprS\r\nUw19MC1EzFBIkSdmYMKtlEpVSZqXjoe81Enyzklpp36Z/DnPEdMb+GIy6+JI\r\nQaFPmsJ8crNZJ+ds2vzphWdR1nfaY8Are1wMppLKu07OxB52OtlriWTF/DXx\r\n7PiF+FaBFQMzdxJJvbm2hljHoS603nGqYc4urvT6cwPUY9n8u4kddfYQ3IVA\r\nHBoEByasEiuODiNS70V2T6dku0z1Sr7teXQ=\r\n=ssTP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"7ad4a7f2663b6bd0b785d776e9d828db74a4280d","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.2"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.3_1672402535218_0.8943695300470025","host":"s3://npm-registry-packages"}},"2.1.0-dev.4":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.4","_id":"@keep-network/random-beacon@2.1.0-dev.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"464115a0dd262d217c605763491c8668b2a3fa82","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.4.tgz","fileCount":171,"integrity":"sha512-8mWbODZKFyV96RurPUwxhdf0HBGwlv9QGqCUo7SGtIxpqRTJPCHj86NjtD/ktqFPcPamc8WqlGUi2JGVntoqSg==","signatures":[{"sig":"MEUCIAyUlb/O5DbdQd2/2yvhkD//lNeApx/NKaD5TobSi5jPAiEA52g6zMQbCJCpR12Sl+eUZ1rDgdXwzDL/9oIiUyYp2SA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20194828,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjtFQwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqaWA/9Hgl7Wh8KR9Q7x7ql5Lsh5DZszPt9zHQ+yGsOuPNnI+jlMmce\r\nI5P00DphwlT2a2CMpJ3+VzPBddPsfNeh/ugZs3zbZ6SUVnZJPs2RHG7WVl/c\r\nFllIBnx3tNvARLmsWt6SlloWnWlnssc0EnGnAvxakX/y3J5EvW92vlMSGMlG\r\n0Ch1D0Vnpr6jteQhfgwLz4N1sIrxTHxDDU95ammbGGiVQgRNhEqQuMzeAz0P\r\nns6X3m/dyrTgXJUW0Ztrm58msZUG8z+aU5S6/58GUhK2VIzmtRk8/mkfedw1\r\niFFMvtBoVfvdegbLliuASsHT9TAAjscyNg9+ehxtUu9KPR+KD+Mo/KxSXVbG\r\njwIgTgnZNXdgWB/uyaFbPdlH1Cc/IeSzXnsMqHvuJv6CZVKPhMqtCf5Egv5+\r\nylP16HaTzOkcbHdNbVxhHVkOjEJCeoJhhIRV8+jJKJ7Bu2yRnvnxm8tqDGiE\r\nWJjYxwnrQv873DXy7C2kj9A+6m8+pzgPMu8vZ5yKayTdow4SqTXL98aij3Z2\r\nwHwy1R3/YbWAD9852F6ZtvvCYReYm4dTQ8C76eB/BIP2O0lu+j7PCLxERseF\r\nJKNCT9yv93pfe2qgiCuityBfbGBRDjmsMR8tACVlMJoNtMW2Jil3dc6i7VL7\r\noLPlW/J+aP3tGJ5UjQZ054ZmSN4rRyVAdto=\r\n=KaFa\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"075056f157c6c80f69b30d14436e6b66e7b43fb2","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.4_1672762415780_0.7521035773122526","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.0":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-goerli.0","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"04c20f4eab3933619f7dd19ec3d87d6a35ecd24c","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-goerli.0.tgz","fileCount":162,"integrity":"sha512-BPRBCrZUKLbnclsmmXvpl4+LT0VngPCRnhQoRW4euVjemGcnf8DC95K0I5AndjSPdx8QIl+kShYsUscAOIMGzA==","signatures":[{"sig":"MEQCICqySRNUQo6FHM8fxF2AlH3iLlxFlI+ySXk65UAWwzTVAiBb2d8XR581OMeXRP7nF9tbF4KKDRbVqAzS44a9HebBzg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19496654,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjtqGfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqHgA/+KD53q3lmeiPAHUNvswj8Je70BIaRjw1zJ1oMY5HAcyB567zZ\r\ntxhmbR6MVZyZ5J8uCLoOKV0vyfdb9rDTFV/Qzj8c3S71lW86wFSHF2hpB2Qm\r\nlE4EmrcxxZfqEL4h2GFeJilz3ERiFfiZ/yZ8CBPhMgFrJpA+Cp8bJLxaQuu4\r\nL5Itd7wlxh9uGOeE3BDFL7ml/Ayre2xj1t7e6rPWq0P7kEGHAUysl6ZpSg6k\r\n4G1iMYT6qmXIOaO7Gh17xXU0Cpifkn+ub7zbR4QJ4yGIltwgvJrEE4OvUb0b\r\nPrB5J99phvk4xL09lz2Lax8HT7sUNMM1GzdNC9HWkm0CxstjBYbpkBrbmmkX\r\nePVjvUc70Mxpz9bpnbk7mGGFJRJBq+lFm0u2JNmGV4j+ycapKErSeGivYSrn\r\n53CZ8+iPWI4NDb4kLcsr7SUgqx1N33/2KnWvTE3p2c8mSdskhIWSXakhYIbE\r\nsWA6fUjtrOfV3VON3laWVDsnJGA0UaxAVeQSKTBi7dmJx8LRI9xtpNL1LpXO\r\nB1VnUHWpENe+vvoCWCrCk/0LDXDiZhF7QvvTatjEyZdsvhTOjaKcJxLpbidh\r\nyknudd3ze8heEOQviJ0PEZiF64W6sXGjJTma+46E6jgUCDo8jF5uM+Sm5FO6\r\nuxVz1pR3uWcYUG2ehBGn69/xag4hoczoldU=\r\n=E3+V\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"13091b462069e27781e59bd081329d42dda7f3df","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-goerli.0_1672913310791_0.9646466173925536","host":"s3://npm-registry-packages"}},"2.1.0-dev.5":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.5","_id":"@keep-network/random-beacon@2.1.0-dev.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5ea1a76f57c8171fe3b12ecf4cfcefee38f954ac","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.5.tgz","fileCount":171,"integrity":"sha512-v3Mqzwx69WqG5bi8qEO4b72PpDMbwl69f5PYHZ0vO3g2pzU1PpVq2nq/vzgdqW2xgztvnHFwOq+lOyN8hx0K3A==","signatures":[{"sig":"MEUCIENzW1Tfa5MQxOZia8v4JWNmYi4psJ/2t2qkjyQeyNolAiEAle/jPhm0HxFG+tNwEAKEUOQa6+1nd/PqMM8wRLm8y10=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20194828,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjv/1OACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoOMA//XAPmveOVj+Mp7vM4AOjKtjjydyR80xBY0EEmqfWgKwjqCmK7\r\nv6c5WD+vqUiMhU2y76C5xgFlfXob3y1rxmdY9n4zij1DJmhGYHME045y0+OG\r\n5WqHS06IevPUMAA00uGO1A/ViN9pPLHUw/SGTVyyK9YUyZ6OezpY4Ip8WTYg\r\nF2Kp76hDlxHs7wd668+fi41XgkuU6zEz5jKK7U+IEvqVZemMRr7pm3O/nFZX\r\nFHUc4q9/BQrwPUfIekZeClqBBc12IrFN1Pldm7EvBImlYfiCfOZaq0uKji0B\r\nLjB6sT5U4ptXe+VlrQhROrVFHS7UwxXi/xG6/s7zqPfnDibwvNrT5SZgp1Eb\r\nwqnWggdr9BYmSV80zf8OL8HyF/jjCN25PqKAUJaPa7fr1bj2Vv+twwuMklg5\r\nmHTC39qhBiDcso79y1ge38SXEZzWRYvxUTAypJ2PwyfjFgJ7+Ho/I7DB1tov\r\nW5zqBxNp497vYE31ETKQ+UGgb6OjEx3ZinemN+2D/li5VdjOSlgKfCKafIb2\r\nQD/zl1n6V708s4acsrL+lwybQxglLWmfnIPB3XeLdJiR3k0PtF+B1FA6G7A2\r\nInQRBeKGne9ig6gvnq47QsKoi+2zLwr1SjmWjxv2SV7duBwCMoh9LmRjMlX+\r\njiatA75p27ky/nVb543zEZsyf3gRSn40fXI=\r\n=hmtO\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"9a6902961cdc5e40a58b0d1f305849543b3974e9","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.5_1673526605949_0.39061079431921764","host":"s3://npm-registry-packages"}},"2.1.0-goerli.2":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.2","_id":"@keep-network/random-beacon@2.1.0-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9f6533931c69be8b91af5f5947e703f41826cc68","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.2.tgz","fileCount":162,"integrity":"sha512-+chKVYajkyH6sTQX0Zq3MUroKJ1fEayQwSwtvyUMSXdq1arzNH9cj9WFTbgsdy/bsOEtgFBGN0VJZDrfVneDQw==","signatures":[{"sig":"MEYCIQDG4FSQd5G3zbuH6S0wqwKUt7u5QdKuwHFJEB7km1yUSgIhAOfHJTNo5pAfXcN2FxGnaiku/8qoRTE7JnMo6AAb/Brz","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19497234,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwTYeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqWYxAAoLQV719E8E82ZnXpQj6A8NOwisu1t8Iq4jTKxYjTD/oA41B8\r\n150GKPJ8+TGeGL4f37gyrKmuA1h2LKFtLq84XVvSzLIm7DBmF5ykvC0KBGRy\r\nzwOrLJz7RaVjikHqcwDXTPjw3V/aGTF2URplPJCaPh+eKvAK/lYQiWW7O07D\r\nxvYf4CLaN2cDT/ycfXiCeEA1+k4Znny0owAx8w1Xw33drH5LmZh3u0cvwrfg\r\n2tIwic0Pn5x7cnhdZ27iOcGAYUvXtw4kHJZL3KjbX/wY/Kn4CtKMiCxM0iei\r\ncPKGBtQkKL5x3qAIsLbedbczulIOYoRvYHw7piz8RSHRiIuWj0H7Z2bAgfYf\r\nauyEfPN2kq24msjVAXeeh/ZZ0aTYBZQyXW3bydAjaEbqC3BxlGRZVmJ4yamU\r\nN4Si9A/lTfYB/Qy+Op+lAwo7uMoAp4/Kbgkg229No8a3vhmYTqW5OSzqRLzc\r\nfrXzKqDHYMBdefZG2mfmfWZasi5jlXWq3H7lRrlBI9AnimuN6on6TwhhZka+\r\n8XmZ4GE7jgIkItpTtrYEphI8Qo8mPhK87oWABeTgpOIyRKuBGMtu7V+VRHlI\r\npujKZUv4ajmD1yJO4AadVYR2egN6XczoT+tmIgIATZXB4xOoBbu5UmsS3uhL\r\nDo72LevFiuWHlhr6ouyU+Q7hwm9UbXrW1uo=\r\n=UCE0\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"d6d51e9c2580e5fdae90636615cd116ae567e82f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.2_1673606685901_0.15341024056255081","host":"s3://npm-registry-packages"}},"2.1.0-goerli.3":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.3","_id":"@keep-network/random-beacon@2.1.0-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bb1df96ea0e2c2a8f18643673a3f101c09697cc0","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.3.tgz","fileCount":162,"integrity":"sha512-OFLQZ8vCq53gIZPrUZ4mOBDNgjjsmx5Yn+oZ5fuKWbLwQtZzIbWHPLpP6UjNEI+gKAeOpmjqXymBXy66CRHMzg==","signatures":[{"sig":"MEQCHxOrrLjzhgNBQxYEp3VHPMky6dib05V/tfe3zUPDWGgCIQDqw6nFgmDUbIA9jkFIbTJwM91m8EsHK0TH0O8xiiDzPw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19497229,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwVsmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp+wg/+JS8W6CEwGPJPf4W9J9Xk7vhz6IHhXmQ2sKdcKGtsd6OH5UqD\r\njb4vFcqh0jKuKsaolyf9tT7Tl8h6uFoe2qqk25+rCqsP8RgpSwHlW7JxZVRt\r\nLLvHz4lAMPs25D7aMNM0lu+EUEalR3Vqn0j3YnfBylQQxElZNVyiSwjJ/TYD\r\noEs/8mr1pQFo+yRHglT/PMfldjcbMxFjVuy4GP4zP13K+FBganMckAK3eEeD\r\nmBZkAbKzzWWmVNlZsi7m1S5/yJWWYDaCY9ZZbnYsDab2lWOEBJhv87RdBZNA\r\n5KjffY5pJzQjVOAHugs3nlD05ADYpNotCEW5LJ81sBCUyZOusO3aYQjwsgFs\r\nrEvGQ3LK8m1gNQfJKVwhKhjC4tHYUXFzkmOd+fD1uAx9BQoImJ39lBUY+DVa\r\nRnqFSOOow1+X4xtQKr9rwW1C1e9/EGoJ8WMYUvQzVrrX+AH+nlVVWK8mHXIO\r\nQNmQR3n27IDmMMZKWVa+qIcaoVCoAcsP96nIVayKpiPvki/IcmCdm2PT7KLY\r\nKxgtT7cTp0Fst+8R4E9MAdj2Z3AvzEU4JkGmLcVIq1ASxu2td5MZzq21iW6u\r\nCq1WqOe8D9BmK049CjPYLegOKKFzpzGHaGVPst5Wt2T9TrLVRHRbLAMMl4vA\r\n+wZQkPzuu7n7HvKkUd9Jcb9XKwkS9naQCJI=\r\n=zvm/\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"d6d51e9c2580e5fdae90636615cd116ae567e82f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.3_1673616166582_0.798339298512982","host":"s3://npm-registry-packages"}},"2.1.0-goerli.4":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.4","_id":"@keep-network/random-beacon@2.1.0-goerli.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"eb055c55b39f4e19f641c17f6205e51b6e4ecca9","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.4.tgz","fileCount":162,"integrity":"sha512-nfd/stfnbBgJiBiD0BEfALfhKOFtVj9ObyCZRa3BFFKIVs9UakCdfndgtCI4OVc5HCZmq8wD3noLICH0GWJ42g==","signatures":[{"sig":"MEQCIAoyUxRVyXBPCHQx+TiocVsL6wamt7rrr5p4R8S93c2mAiB/ueL8DaVB39tVSTSdjGMwvfX2DMmHvu4xiSM28k9plA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19497232,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwYwoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrQfRAAiwBQbTAljlQagvK7ueIb8WuF5we2I1w9HRFojNs7OpE+Ykth\r\ngbIxoymbw4B6vElpBYB79HmS1RfiP/B4gnan8vaOWEYM+qGGM3XJuAck7MGw\r\n1kpAK4BbBPJIHWb+SHo9/6KTGyEMrgE7kTTOmgE6eKi/SGQgfZcqILvFP2Dl\r\nEOq43Ou9s5rq0UbEKu6cZi7MhSrdq5lXqPl20ErRinHwRo9wAE8KmwCDeSt2\r\nGjwHmNRlrOtcUkbhL3cn5LcPL8I/j09XAvxGXkJmuKGvKrBBTB5m6w3RC8ov\r\n+xyfO9U6JYxCImPvhBfCgaUti9Z6Fa01w4AWuee5cxsFkP1MVPQy6UjWGksI\r\nYPKqu2eHldTUciUFmmnQniJnmunuO2oVNv2gXGjDCzUCyQZO1KrezgaYqvLj\r\nvsvupHLcj98erLGJZ1H6uHwHpuwJUJbpiseT3oPe6+EkbgaI8e6nnAsK17du\r\nlsQYuVHQsof5pCws5ac788WC2ML+XTfyXBSOEOqh9kCf4D6vXoEwdn8KR5RM\r\n+u3NpN/vTsZN/yX9RoCZl+aLYwin4osvv5VAq9Frop//G8Huoc+HbfoqbHPC\r\naeCKcf0xfBxmXbilDBDlirYhM8SIRZa8L6dT/TnyOGjkFmuhyTMh1lGptOx+\r\nsSjxHujthYNMqdlQQENQaaSpGMQPdPzlrgM=\r\n=rTid\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"257e80e07632e6160183d50346c7ea4610dc5811","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.4_1673628712190_0.4692615963464728","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.1":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-goerli.1","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9aa4329b94be11aef360fac4430e5666983afa6e","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-goerli.1.tgz","fileCount":162,"integrity":"sha512-GTDFUJRgCh4fpb2MhL81hxhwLoEIV1aY7N9FCKOmYmUZe9aaUgc7xi4cyB2MA7fyjitF72sO1vvjhojdHR7JSQ==","signatures":[{"sig":"MEUCIDvaIaBf1qTRhZPHyuWwDqKPEzCUjSLLVAqTrxYtOYxWAiEA5bJniCr4xDo3UtjCbEpuVsfjsiM0F+0vz/v+jAuz8Ww=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19496659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjySNJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoeuA/9GtxL9SP25CA1Sq8S29IOryUC8Cgpw9M/OpyUEgfa3SPs1nGD\r\nIJMtMgRYXzHoVOICUrRsAc4jY/KbYwNTjI0xEChDozG6c0zSACcX6+GpW6+F\r\nQlmLpHmy5UWt9YUclRKw6bQrtyHMww+XpzpAQk8W4vGUOzMsqZVkovDugVWg\r\naPsYmXuCVgfSBmGFbB+FdWOj5xxh37VkVSTR+a8M1/GovcBqw/gaqt3mN08Y\r\ngYmwl7CxCVyHNj6ARfPGM7Gx8xOMnzZ6c0bprHbZ3T/oSh0yghqScTKzL/hQ\r\nuicoPPgLgqiaqYfvUs97psteCYDzj3c81RKcaessE/JUCwL0g602hRKlEzGp\r\nn2rQoHqNxQgZeRPjezDUDoAc6vRx/YQtd23OrcGq8wdsHAOSBanl4nwgkKrD\r\nvcgTy6XlXaa72xWsXmF+32ToVchgAf0CHTQRXSPSiLPUKd7tmiLtToK920KF\r\nibfHrzg3BczMm9Nm3XuPwGjN6v/h3vs42Gzk8rvFvh4P8jgafuAs6DaBtWpR\r\ndfh6pSLOTO/9UG7D5/cVTn+fpVSJDvSAB1Segdj6iB77vsw1PIX/PGgzFWgL\r\nI7T0ap1MBgcc67mS+6jhy6eXph4/cGXhefElC1xyBajsHu3fZyYGJVqOwZOp\r\naz6UfPmEAMjIiHb566WUnZT88+r38lyFKh4=\r\n=w95N\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-goerli.1_1674126153394_0.6205967796514684","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.2":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-goerli.2","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7ff86be9d195cf29eae44a561df78bb48b7eb92e","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-goerli.2.tgz","fileCount":162,"integrity":"sha512-Q6mB7u8OjOWqJkvHygwUwo4frJ7UlmLe7NDfwrSxeCLVqR0L9hmFA+g7AfjBjh+GthBdbk/tX6ee+4+Hb3iWPA==","signatures":[{"sig":"MEYCIQDn6dG/odJeQ5cHAMluHI0s6gfFXxSCkDDdjFDUSPdjowIhAOTDUzUX9JeDohLdOktFrl24U0BnJ6RDTpKDksg9DoPG","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19496654,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjynOAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqrMw/+LLozfZDliCrO/03U3DPKt0A9DmcOeldcu7prgRmb6ehbBjsy\r\n6jC55KUdnks59EktKmzRIwWtJHyAuMh5ElWdHCbCOjIffpfXYeXxul2W7yY/\r\nqdOMJ3MWRxJleFal9Kt2h2d1uAcvr/eBFAhybLI3lLq3yih2utw4pc50EcsJ\r\nnxyIY17otKUyT7LBdQou065I/isYt4zXfAwo088/SoZ+Oj3GllQGy3t5NKUV\r\nmPS6Rbbl7HS1Lyz272XdQNDmHN6irPHxMliuF7TI1UV7+ki6gQmz/miNEmAw\r\nLcPdo8pKZ00/4VdrHfsGgt59UhSdJG+pAUOoxkWT98voZYsLwH3Tx3ZGdA6Y\r\nZfZuwygcarVNzsMWYeEip424yHFkeO4Pf+/jIstZMr7JOw/bni90moGhXw12\r\nMo/fAGlONk1yjoP/ofWeCC1v4OB251Fo7FqmtD8HxZxTblzhb1BY0TDFrhxn\r\nz877epAzo92UdHBWX+piFb5KBMwTrPH2Jbve9mUBCxkNikP0ObNd0u/kotWW\r\nLpXYPlTbNfUNWoQ2aze5cmIftrvAT3uRri08e7uWLEt1pdmF745XOAcbNBNc\r\nIvQqbsxoUM+jiJOcuExv4MsJblH0Oen3HqWjKsQzYw6cDP/OwFHaq4bEQjhi\r\nhCW+ibI0DILb80nK3/hPEUgplFjlJWDJueY=\r\n=2BwM\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-goerli.2_1674212224434_0.27211777043987606","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.3":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-goerli.3","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7f29a5a46a1ff0cb29e5fd9304867c47990cd460","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-goerli.3.tgz","fileCount":162,"integrity":"sha512-vTF/WbmGCuBCxIaVtj5nx26bJchSXwhjFhKTmaQL07+RIPYjkHSifOh+Z+6RqyQJxQ7SvJSTWczt8ERm42Mawg==","signatures":[{"sig":"MEUCIAf3688SfvqveCooqLRQzeVHugIB4c3rGbwS5IlfI+uHAiEAzK59jB+A3XyRFZm8S+DjJeByJ/zCG5q6J8VDCqSFFGM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19496656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjynbAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzVg//YH5/nYR1LkP2FsleYiFkxl6tjuI/sT289aHCujwssjl/Kv4u\r\nap5A4Fp+1jCllA0liN5OKP90iCWsuGhMGsgAWB0xCmERxqV8s0KQpVEv+sZa\r\nII85CbuJ8OW4gGrVvXwSgIsz/MYEjuBH6Bjeou5hnyAwOrGYCzwPKVi3Jc2e\r\ngmaui34W52gAsVghNkgy5pOSehNVaDq7dIq3ogjWix/WNXtPkGafXlUxfOMm\r\n1e/V5Slacl2R+Urm0I52UVMnW6nijY5VIHDUIBcG+pwhLkRpoxJOYQKV/q2r\r\nhqCBzlUDGywkKTYi+5dWKMVzAlSBNhcdkBWSBzShJQF1MpPwIBl79i1Z2YRg\r\n5LeYS/xrz/YPGM6vrqzIvWXiP8WURzpjos6B/6vB5pdQNEtZVTKcO+wwlVD9\r\nO1x3U4Ey9wHQIhXYIWHR/p0WL0XgWQOODnIPi1o30h5J6ALmL11XJVUOjKOF\r\nR3JXcrC+U3NYnF8RL9va6IVg1VYAWov/wMP1CU7wlmqzIgEgaYyO6ozyGIoL\r\npCjd8VOFLhcAUinoKUqlGZcJRIasxA72tKVXoqpBUStH5cWLFPiI4i46LPTV\r\nxumX2BNyyMoQQjFAMmSukPN44NLpZ7ArDaduw/dxSScjiLqgZyZIX4jQtxnm\r\nWDsgLLK1LwABrprUT/xlnSqNsdyWUAbqfH4=\r\n=xg2C\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-goerli.3_1674213056142_0.5498581383552807","host":"s3://npm-registry-packages"}},"2.1.0-goerli.5":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.5","_id":"@keep-network/random-beacon@2.1.0-goerli.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c1b5880e4fcd7ad185f65cf7a476084ceebc21cb","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.5.tgz","fileCount":162,"integrity":"sha512-Gr+0Qo8o7XwD5ZmiwDXR5l1WrczwPZxBIALgm6ngNFO2qGfw5oOQHa9cM/x0RgbAE6BZr15APfBX7bkDI2D6hg==","signatures":[{"sig":"MEYCIQDgZHszVIs6p2f+/tYo+na5TS/u/rPf2/jTX1tg+ytuvAIhAJ70gFMlcnk2W+mpVb7oL7gaVYmwgoqiSuooee6tHKTF","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19497237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjyoiNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrUBBAAlAve3x6AOb8wlIzaDS7w/btNyTS1vKYv/5o+fmJ+a0sHjJKc\r\ntmFaVsJAGHfNcy+XYFYGFBL91OuAksqDuZgJsL9oeGzdsTaPQX+/YBtbCxxl\r\nrYQe4YE8RpfSQjo13P30PvlF8X2lwQ2c3cnKSIFrME9E74II+T8F8+4yCJVS\r\nv/XpDhjiVshDn6rKhNQFUbXTJI7WU5NZd3EJupoN7oUx/o83ks6F+lceyk20\r\nJIcK6kejAGUkJZayv2hmIzOKzCOhd1jWTAdqXX8mGq3K1ELDxMnaVSWxCNJ8\r\nKuMJZmYVQUA4cuNUq8GtKH40cqZyNigxN+NWB73Mxu6XRa8/ektR20n9XrM6\r\n6aDAH1MEYLkoW3/j1ZEF7xFLVAHgba7Ymiz9rrBwUvLZVcYie8nuxPNgKRVI\r\nPfACe919Pdm9iWAe3G5du+iCZUYkZGHqkeYeFTWNavVnOJ7DZPwrahGJ4qsJ\r\nJD1Itnz23/PChP/BcEFCrL7NAkVogy1tNUZw6JJADD2XxpvL71mxHvXK4Drm\r\nzidElV4JpSx3zisSFrYHghIuSbroHtSwBQ3poi7SSjaO5RUNrWnK7ouvME0j\r\nOWPShNL6im9c3gCDXf+sMiBUMrlRz6SVisZm2LdcWfY0uptwnfQ9mdnOM4mf\r\n2km5EL/X1HsUBwVX8M05z/cbJTsPB/Xcbj4=\r\n=Ykwf\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ba1879c9fa21f094b0cf890ccc3eaffd012879ee","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.5_1674217612772_0.6461584132624103","host":"s3://npm-registry-packages"}},"2.1.0-goerli.6":{"name":"@keep-network/random-beacon","version":"2.1.0-goerli.6","_id":"@keep-network/random-beacon@2.1.0-goerli.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"dd6dcf4f5101b35a603a819f30fc884ecfeb0b85","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-goerli.6.tgz","fileCount":162,"integrity":"sha512-A+rnK0NkP4Q+EHzbbW9iuSUEjUgCAJdhdh14WY9zMpIreXn4KoT44WmyqcjYbwWe+1DcbsX5SwDK7yjmoeqcFA==","signatures":[{"sig":"MEYCIQCQx6E0N1m/sZrF8sv1+C+oA0hJYtipuVCv5hTPKg76+AIhANze05MXrPtHoXDeZHQpqBGhUl31BN4BrbVNT5fk9wlv","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19497233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzxmQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrynw/+NYRnBR9nBoi/96R1/PdCVg52CSWgQ5vhqxnEKjX6ed2qnZEg\r\nW1xWD4jkOUxOHU8ifgmJrvTsD1BbmY5FLv+axRS7bfVp6T5kcCenV4gLqijc\r\nhJPkOEYiRZLKSImi0Nl2cbkG3QRuLyhu/BOv5aVwJNQdeJe8CiePbAf8uk5+\r\n/y5wJGiUZR0VVFP0a5Xy9kHFlIPB3TAlRvbo8BoQI/6BvHJ9f7rfxgzbShNP\r\nnzMCKUfG1YNZSkMGCVl6EXnu71ktRxFKNUnt0tqwzm9KjjrXHRQNe1FCdMvz\r\naeO5QVYD6+mNjztX5kleNcDennois1ZVR5TzDxoa0LI1hbhjSA3g4uQgJDjX\r\nj1gtkNotkUURF0pRq4gQ6/p23gQpjyn4oqdF00rtw5A20gQEwg0fS7IcWl/d\r\ntXTlo5GarKixnccTSL/IEsd5goyeI1y3c/QloHxq9hYeAGxtCcdMM3L1U5Nc\r\nsnfOEtLqgVEXxZSFyPpExtOFmNnBjHdbTJXhn5pxTWSPv6BR9OYfw2S1NJjP\r\nkLd5KRY2znQ4e+EgLSTleP0TmrM/UgY4dQ/afJaavcnhWWee2a0rBi/TQ1hN\r\nqN3fb2VK5+MCFSsiBlxhU82iCH5M9V1gUBqwgBVhwxEda8hlKEdEuDTD219u\r\nh/O1Sr0i/yrGcCQZQrwr/CymJT97s9PZWuI=\r\n=UAmI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ba1879c9fa21f094b0cf890ccc3eaffd012879ee","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-goerli.6_1674516880399_0.15047576618886693","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.4":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-goerli.4","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-goerli.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7ffa9dd89c8f2d85120f7fc49adf55ca88fc8013","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-goerli.4.tgz","fileCount":162,"integrity":"sha512-GRp+MHe0We6hcjNkbwgntpXyQ3kgdEi3URy5UrMdZus40q9AddgMnW9oQ6n3i96JmiwpPgVM1qZVAyukkrynoQ==","signatures":[{"sig":"MEUCIQD1p3+RfihdEuiuAGdkkIqbf2deqZIuO7QyqfSu4ybiMQIgds4c2avZT1jxjsy7R2xTuV9LATVHm+oEW6uefijX9As=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19496648,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjz8gDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzGw/+Oq9A9r7K/hnn0hikVxaWf/9krM5cwEwwtxKyTlzlM/mwWYF/\r\nOgrI4pTY4B6jGketRhvbz7oqNiqFjvM10E07PImoNlisM0m70Vo4Up5iqqLK\r\n5QYLyJri2DWGp70cFiDm2GRmON0ASZHPE4Dw/h3lSaQM8Y8ZJB5xb4v0imwq\r\ncrbvPROPj/PIRcvIwFSmMY1Ad95sIumxbUz0SMqpRLMKIY4xrLc4Zp0BIGP+\r\nkAsldKKDLjIZiuDI9RoSylQtII8UBJu0oip+WbHA+25Izyzc90KVT9NNifQ3\r\nh/rz++Rf4T2atNMntkjy35Z0dJqrvcV5ygu/D0l1oXPMGPxRFNqrjpBbSPbK\r\nYOnV4fXXVKPz1VqRNmhfP1pqyptL8GVvYu8xOddqxbgfR1BH/3XtkY548ef9\r\nZjyCy+IyZNwd3iHPlbmvJszXi98jdt0pNbOJucoTu+BznrHcGeY444OB9NVY\r\nYvBQrCvZAvRZNm/XDMlYGnI9LHFm+E2p1sPamt2UWUZ2KrjPQsNtd5zl6At9\r\nFg62z5JO3mmJ+UN5HwNBR2op+QzhpPAEsumlNOOMEPmREyBcGoOpTbSWPKcO\r\n9wEbDOzw9DZK7UHgiUT9dhv5LebuJ5PXLkCJm2tYN5RBm26vMGsOdilwjMew\r\nNPsufd3fAM8odVtctHXpIPAFJ6klxzih/L8=\r\n=rMda\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-goerli.4_1674561538996_0.18369569646097994","host":"s3://npm-registry-packages"}},"2.1.0-dev.6":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.6","_id":"@keep-network/random-beacon@2.1.0-dev.6","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1c7d2d9e2ef7d8164c5db4fcc1ffbe847b30a0f1","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.6.tgz","fileCount":180,"integrity":"sha512-ufVhsRizmxcQc0HRWopNW7+/Rdbk4z74XWnMD+Z+gY1Y9w46czHa74g4LqJornOCy1lUOUWO6obpC+3JNlxctQ==","signatures":[{"sig":"MEUCIG5Zn5QD2D4tpzox94eSMOtjZdgd5CxS5uEVgdtxTNb4AiEA0oBET2vQGnWLhzpbhGNTIBONCeXl0lNgXS1evcIlPSU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25209070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSukACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp7gg/+I1+IbUTc2fhR35FKRNE8O7peUeMKtUTv7fSTuwq+aqTnrthM\r\n5TQwhAmujfXXfioRikIiBDjVyPtJtvLWJhEs/2vy48VBRx7yHHIA4S1CpZFL\r\n8SHulxG34iqos+g/FnXLH2jlEKwAY2EAFLSu9To4G+8+XFq3TfHbzEGaAv79\r\n1hHfNTTzTOdWZ7hTYHxYO5+0moN5t/F5sDxgpe27gHCzSdn0mbU7mrEoQvC6\r\nsCKGR3ys50qSBA+VbJv8Pq3TU3WFrvXRM9NYfm5EN3VNoUhoPWiQR4BqCte1\r\n+wZgfolpbaOE4MdHc+1r3xF6q/P8UvfZMq+S+kRPelkeQ1Cocx04je4nP5D3\r\n1s2pdkHcYDFDMGvZHpUyK8v6DtO6T1z02ukVaLnNtRtRCk8DVUOBEHuRPrDD\r\nkyskMp83DJhkhXqv2/Z9h98DnzpYAxKRi6g/k8i1fnioQE3xvrkk4q4ukLJ/\r\nB5sUhOSA/v8Xzb3p+fo3xoty99neJIN0bwXQJCHTWQG+yw8kj1VqLEasHI+s\r\nbtccFeUAP9WiFtYS7bxa6X5xRUVeQE23r5bSxrq2crMJ8O+pWBamvTSSXZ8r\r\nysNQed32AcjtmXyx7yflZBtdiFAcVMgNyuNkOxdB1yE51bgaaVvSqmdxUmuM\r\nmFHlqN+oYmUH1M8akFLZIJVDiCJW48LrMxs=\r\n=DKDn\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"39045fb52eff5c184b94f0289aacb96a1db662fd","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.6_1681730467884_0.12251074216079005","host":"s3://npm-registry-packages"}},"2.1.0-dev.7":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.7","_id":"@keep-network/random-beacon@2.1.0-dev.7","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"cd28c2c9893fe2f101ae06039fcd6ed50324c9f2","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.7.tgz","fileCount":180,"integrity":"sha512-9uN9o97/oQc0bMJPOiCxzUvKDjf/PD9dh0UQj/t1s1tvVmuvMHRcFTEnN/mhEYoxFi15uT443Nvsv5NVMtszcQ==","signatures":[{"sig":"MEUCIGK3NcTCl9rswoid2LGD3o2MPfuC5w5fVSSC8xwp5njrAiEAhxtfoQ3NQYdS5CKfjiqrHfV0PngTPFYd6WMJuEpJ0yQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25209070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSxQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpDwQ/8DG80PBAPev7KnQ5dWMyyruGX1niI18zKyfDDW1FymA9tbd0y\r\nTldraRIBoAJvg7GYXSWyzQ88EkZCVqs/I1ziN3vDmwsV0TeNEeFqfSWDWD6I\r\npqxOCmn6KJdiEeYL33pVpcQ99e4V7hNxKS2WKcEi2+1sjGTl8UHC6MTc8Y/6\r\nnZCvllV1Qyk9En8Ii0pYuyMDgsuHYjIbVy4NA+E79l6PwvjiCtr+wv1zCdAP\r\n0s8JqpzoPZL41IlkVAgFYhPzo5WdJOu+x3073dle2aT+uwsbRXb6xbjxZyu6\r\nGSl+8tCxiP3vm5+irtQH/XAROsRca7cGPrSLeu/loLY2FybjioSLDeYiXQCL\r\n8z4apjJaKZGfuN0KATHjPGSsbWVGTVOwaRiM/KrORAhGrlcnauYE+WcO4qqQ\r\n/wlQbN2pUMZXquaY6q7y2+6wmgFCUOtY7coxcdDBYQN4WNw37/fO0AbrXU7t\r\nmzfLBsQm1w8991qcQNKBKDE53GNKZHXMI5+28WNJ3iui80mYX8DpG2TxZ5zD\r\n1mBV6nwHPwTiDqWRE4C9pFzxFTyEbkhhTvCaVI7ydcattyTnNe2cNgI04tUV\r\nh+3GmYavwNkHsnmySPLBhRpw8cmRn9cELpJDN00gP4D8m32uSjmnssvfRVBY\r\n1HmqKWKgj+pFmMtyZRRBFzWPJjGMp8a7kPA=\r\n=S4oF\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"4da79b74e3599118c83b7f09d2320d9c1bc9aaf2","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.7_1681730639852_0.5284155004042081","host":"s3://npm-registry-packages"}},"2.1.0-dev.8":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.8","_id":"@keep-network/random-beacon@2.1.0-dev.8","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d21466250448175d2c9a21b876386196a4fe47f6","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.8.tgz","fileCount":180,"integrity":"sha512-mnQ7eh49A8NiQ+9Y83kF0Rdr9Do/s2CcXaL4L+NdLPzVJW5SvXmOMfaJ9mghvHr9pqmqcSgpVzLyHL0EnrR51Q==","signatures":[{"sig":"MEUCIQDT3rzEQDs4k3PtItoJ2NNJoj7Xc/bDLii3qllgXVBGEgIgGbAj4q/Yf1ciI81jh5kyELqoIjSxnbyKafG/YBIP2ac=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25209070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSzJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrJ5g/+MhGeQ9Gn3sKY7WGQtvvvVNc2NZUsRQqQZQTYJEO1za2XfirY\r\nnwSDAL3GZWGpmyqEZZ4NiIxHRLAfxB3W/qCAZqWwi8gV+T6jng6zqkFoYM/f\r\nvc1ZfHQZveUik7MT75yzcyWdX0zQFfJIaflLuPFs3l7//UUoDk4OGjaLc7zQ\r\nGi5xgyDpaI2x/TVTz6SHDTE9GARfJhNrNyY0QRrvkvEVlODy+rZumvse0QzY\r\nfxBmRKlXqYvg+JMqRcgYspKll4W1IgzRva3g2K2UGxrnF5yZh392v4MsG+l8\r\n6PvqKHuCgvfVumHd/41v7c7By9Umo85AkrYxiFvVxmoxU+yxUQT7rfPv/rIl\r\nXXb1K/lTeXqFQvwAnh6ycdwKUQ3Y9GQstMVl4b1bdFpm4M1IgLjwFgQd3N8O\r\nF1n8TU6DmeJYXXoy34vC9UMPfBRecj5LTpiqhoI69CbF6VXKXoVZxC3t71+m\r\ndrzfHHgEwD7uQWX2QsFbQpM7l6QzO8asK+I+Raahwalok4KSY6ne8MWEMOdM\r\ny13YAFTA1t+MPe0gDmdag1/Kjv2QELAZNLogvHsYgFk8/2fRi16zgET/R1cK\r\nU4IwRcoHU5OSAjSwYsk3wL+NMXAEucNxECwDTyvl/2FByNJ7GWyJdJXTngMS\r\nb3CztskwDAsCKnqanj8EKDmkq6WBY+jHGjE=\r\n=2E11\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"503fd277fd56b5d4c4916b67b4920bcbe8e42a7e","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.8_1681730761656_0.7022856491202647","host":"s3://npm-registry-packages"}},"2.1.0-dev.9":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.9","_id":"@keep-network/random-beacon@2.1.0-dev.9","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1058f1eff13e12d2831ec13fa61ecca399d92247","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.9.tgz","fileCount":180,"integrity":"sha512-W4/AgMRWbYU4NO7YB0URbicjH89DMYHlOCT+GbyL/znZ7DLhLXzZCMJW4EvxM1lm8/wA6NsZev/2E25mHeqDIA==","signatures":[{"sig":"MEYCIQDbRfyqiMEEKhgb4zP9ETrt5twuBLmbau8mFuzJy7ByeQIhAIiT2CQi4RgACUNTGMC0MPByzVgzYJ64MPKwFtoX8nMv","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25327001,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPlWyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHDg//Q8iiGJeZMKws24N88Oxi2RAmKx2jdIZD0fPxh+idXjnkPFTe\r\nrd64fyG6+3Tx43ojKMHZWaq2h/R9u1w7cZhmVdp6lEYFENGJfpb4cXSVYubS\r\na/FzE6UNi+/+5SMfL7ycJdPXu5wGZDkwBfFNy1f5wpf9A3QrowywAzOPQ3KD\r\nvRDbTTHhmSc8FOq39bT09RRFmsePYohQhGimy0//GRIqKdvI8h2sz7DEF9N5\r\n+nzW5Q7/R8rw8Z/UcJSQnib2aY0XQPiaC64lXAG2VBT0OZpq1tKEn9uF1rSo\r\n+I3ai4w+mf+H4Xj2HrtD5+ok81eL+yUDrXi0B4Q2NthKE9/SaYPuru8ujWs1\r\n1LfAewLrfZTppY1hVUSHeNN5JrnMlw0xZtm5sOycHDBzsQ8zsWrsFdP8m4ab\r\nIOw7y9PWvB410cd/g+JC8CPQwaUnqYSo6dIjAfkWhfvMBhgbR1D0PIVSgFgy\r\n9S3saBuhHjHg1UaiPT9zhT2rlwBOBSXaoGqZbe8ZN7DXBvmmNzydQZfY+6fU\r\nWC3wvGvPayuEVp/lISryO3Avr6gyeXSsSSACLsDu6/yXyYRJ4/VC0TjTpNaB\r\ntyHjc8Hp79tmLjm4w93eNv8PPY+A6q4zX8QJE6UYqIqrJWATlEC+GTD9qGLw\r\noDnE1fDniHXKNA2CsfEGptTDtrswe+ai/G8=\r\n=mPAD\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"0484ddbc1c19715afb897a914d0f0f633421ba78","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.9_1681806770675_0.09759763333550331","host":"s3://npm-registry-packages"}},"2.1.0-dev.10":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.10","_id":"@keep-network/random-beacon@2.1.0-dev.10","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"61c9d3e98257f40292264f4b9e1991acdc11f3c3","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.10.tgz","fileCount":180,"integrity":"sha512-NJtmjrzFimL20bul6g8lKxUPNc+lpiu9BJ3uheJOCWDL5vQ+hJGctmWqd63mvtjgO8Ks9IQsDg9wpValzSzGXg==","signatures":[{"sig":"MEQCIGdsR17QLXaEyOHMrzu+nmG5U6WIVyiGIeRzJ0M5FLbRAiBuyJy0DZ6FyHxxvhBeQmtwDXUX+ZUXcOP1HvM/lV6gSA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25311436},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"de484cb2176a6fcbaed5bef047388072d64fff56","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.1","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.16.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.10_1686055358642_0.43270526309697277","host":"s3://npm-registry-packages"}},"2.1.0-dev.11":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.11","_id":"@keep-network/random-beacon@2.1.0-dev.11","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"763a99fe2e21b834b60932e010566a244a101aeb","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.11.tgz","fileCount":181,"integrity":"sha512-7oGZBg0yWN97DUqGpcBrx7PYVUXCC4Xe/F2x/WuSbWfSw79NqDn0M++CPyEDsnVWQPFr8r1URMNpoKnS3bxK4Q==","signatures":[{"sig":"MEYCIQCheD6tJrZid54aFZjYvWOMZ8viHitP9mWOdix9t82F6gIhANZDANFmmgmObJq1+0F+kvyLrCYvpM9MnGZRNiuaS4e2","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25380948},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"9dae500c694be7d18a3f3f5d71c44730c9f5bd80","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.1","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.16.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.11_1686292742998_0.5387058519725549","host":"s3://npm-registry-packages"}},"2.1.0-dev.12":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.12","_id":"@keep-network/random-beacon@2.1.0-dev.12","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"df238753b65c0098a1a5ae7e98a00dc063bb08dd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.12.tgz","fileCount":180,"integrity":"sha512-WVZT0BEF9uZn73P3X69lO041FoKsiWXZqcj7uV6hC//kOwS/BAtaWLHldbt1NEXdr/iG2THeDpg4Lfttd1p5Uw==","signatures":[{"sig":"MEYCIQDGA+UUR/XrStt9WKsxVUJYLoxlsIRGNbOnRhR0P0YLbAIhAJPkuGlZ+eHkCsqdphzJCdJbCbnImkjEArv41L03f+ij","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25193564},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"c599f2f508031ba9fc82b39e1c21593e56247f55","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.1","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.16.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.12_1686667605768_0.3208384425792805","host":"s3://npm-registry-packages"}},"2.1.0-dev.13":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.13","_id":"@keep-network/random-beacon@2.1.0-dev.13","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8b4d20456e17cb76531a25c98370d3a6da8c8be5","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.13.tgz","fileCount":180,"integrity":"sha512-o5+LvzQB5Sqnpbu5Wr97HvU63rlw9v/O5ZGxDiWe4XwzFhC/FEnza+uWgWm1IJkFVrQj/DzYokqkzgANx/lBnA==","signatures":[{"sig":"MEUCIGVbQpN+iOqKbaSk8DUicKJ1frjnJpe3Ra/1k+Rkjd1AAiEAwrMmKt3WQFQxlcUN2yz9H3N/I3tZ2aIXQStG0reSne4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25193564},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"fabd15b8e3b280abd1cc43e629873cec163e7ef7","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.13_1686672463790_0.7554509670780276","host":"s3://npm-registry-packages"}},"2.1.0-dev.14":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.14","_id":"@keep-network/random-beacon@2.1.0-dev.14","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d9fac9fa8a5a06ea0985114c4ca79e4805c16d55","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.14.tgz","fileCount":180,"integrity":"sha512-FdVSW2VtUIcwPCrnrWUudbXOFi+SKZ6cEz7P3+gO+49DFas4ApH6lkRILD/DUHQDMV7D56TxAdw/DHt0dbA+wg==","signatures":[{"sig":"MEYCIQCp6Jl/UFp+bQt2QCfTy2LpO5MAteG2xljj2IGYlJwHMAIhAOOF+BmmutCAq6eSOi51Mjb8UhLM6vA8Yn6zz0oUpAX+","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25193564},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"e78ce3b1178badf78903c8bd31676654faa60e06","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.14_1686733742566_0.5101196804122985","host":"s3://npm-registry-packages"}},"2.1.0-dev.15":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.15","_id":"@keep-network/random-beacon@2.1.0-dev.15","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"541620c469e3bc75a5d1f7649889540b0e032e9e","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.15.tgz","fileCount":180,"integrity":"sha512-vxBICRtmqSmJtFU5hZMpwB0alhgKchyMbxk4DtLZ7T2zBjd5tjt3CqeKEk+ON09g7yL1mIxY07InP4okviUK4A==","signatures":[{"sig":"MEYCIQC/cd1hiKseJNwQ078OhFV37J6Fjjs8+t3JadmBFuN5dQIhAOVwNTf3C+wNWth4kQbbD4wgjfC+icB2S60Yw28PoIij","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25193784},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"b2c6e58d12ebc417d75403c6ab95a7417b2f9a61","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.15_1687939490472_0.2747024196332384","host":"s3://npm-registry-packages"}},"2.1.0-dev.16":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.16","_id":"@keep-network/random-beacon@2.1.0-dev.16","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9f2b5c19aa79f6ff1a5498ba7b55eb170463161d","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.16.tgz","fileCount":180,"integrity":"sha512-o+cG/VDkhUc91W+4bMplYCgOu0twSFICqarpv5k2ES8GcaafaeV8stXGhCxjvHYJjU/sfG8mhlQZhWdZixq+JQ==","signatures":[{"sig":"MEQCIDicMffmwsUxSgFntrpYaGlbFC/m3MNMKZ9snLwDsBfLAiA/lKRgk5kckds+4yWPRFIt3lKTGSSLnuMV+qL9n9lHFQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25193783},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"f48d6767d22da0adcbe4a4b21c6209446bb50b72","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.6"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.16_1688013717619_0.17989893481128005","host":"s3://npm-registry-packages"}},"2.1.0-sepolia.0":{"name":"@keep-network/random-beacon","version":"2.1.0-sepolia.0","_id":"@keep-network/random-beacon@2.1.0-sepolia.0","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"73c8ee0900583a51b49f7569aeaa0543ad150b57","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-sepolia.0.tgz","fileCount":170,"integrity":"sha512-ABlmpc8OAOzT83b3GqKDLgajv+nMfoBID76fnxXijh7NPG3Rq6+B9kxrz1E1nnC12cAATd/1worVck+zmlj77Q==","signatures":[{"sig":"MEUCIE3cVrE+DDrAR1SvHPDnE0Q3MzaCqpoH/w2DDsBj4X6QAiEAwo4u2G/c6d2SY5tutFCOP/UYmWz0UQAAAgfNhUmfV60=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24263758},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ece4f94825a344730123c41b6b55b30036b2474e","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},"_npmVersion":"9.6.7","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.17.1","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-sepolia.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-sepolia.0_1695222419065_0.7279597512708655","host":"s3://npm-registry-packages"}},"2.1.0-dev.17":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.17","_id":"@keep-network/random-beacon@2.1.0-dev.17","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5fb2621948aa2fe07ceb134ba76f737b7e6d85cd","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.17.tgz","fileCount":180,"integrity":"sha512-alfd2sHdMrX15qKzM4zwkZ3l/CXboLoeos4l3WvChW978VJIwUPm2ZIXd8tNTaHlykQ57eSSX7esaLfIjeO3Kg==","signatures":[{"sig":"MEUCIF7oKEiNXQBJPkVGCKJ8xvoYpole/UI536E3EBIaky+hAiEArktBY8PTnuVNNV3o8izWnBMfR7ATtyeeH7KbBoETh7k=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25195999},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"94ed595d967bd038da14bd1a9bebbc77843bb389","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.8"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.17_1695629079180_0.027860197423669852","host":"s3://npm-registry-packages"}},"2.1.0-sepolia.1":{"name":"@keep-network/random-beacon","version":"2.1.0-sepolia.1","_id":"@keep-network/random-beacon@2.1.0-sepolia.1","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3debde13d5f365883d88b3c1d279cc7d21984d58","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-sepolia.1.tgz","fileCount":170,"integrity":"sha512-dj6j6/msv1BqMtPbVoLo4cMhbtf4jLhKjkXmJoBXU2KYWW9wBlRB06M4DZPfUhhW8L7/1eaFJJIINATt26wBjA==","signatures":[{"sig":"MEUCIQCjq4T8WK+lmJC4OehI6DYVw7LJg6SwaxKwcoDGZqxHTwIgbUeO5Q9Y2qP7n/NuLIXdPbHdpgLz0SoWzD2heDvbgNI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24263793},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"324f66fb3f1003f6cfeb7d4149ae3f1d902dba2e","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@threshold-network/solidity-contracts":"1.3.0-sepolia.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-sepolia.1_1697549949418_0.079356089182121","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-sepolia.0":{"name":"@keep-network/random-beacon","version":"2.1.0-dapp-dev-sepolia.0","_id":"@keep-network/random-beacon@2.1.0-dapp-dev-sepolia.0","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"44315eed834337930ea231f4f3f49593258d0de8","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dapp-dev-sepolia.0.tgz","fileCount":170,"integrity":"sha512-VBXeX7dJDohtGViMoYR4mzQigI62Ye4dZ2PqlsfepFy8f4NtzIX8V2XBQFXlMapbW7loLrqau5XpF2KK5IUrFA==","signatures":[{"sig":"MEQCIGYzXTIx2Iv/dutKErzgvpCClLErUgM5gjqEF/iiRxLBAiBXtePh6YLc9mXcq7AwXTi88Nyn3iBoaLPz0gMc9q+5Cw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":24263162},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"be9e87631288028f92417c2e999be3e0945449c2","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},"_npmVersion":"9.6.7","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.17.1","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"dapp-development-sepolia"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dapp-dev-sepolia.0_1699370003788_0.7372533350388155","host":"s3://npm-registry-packages"}},"2.1.0-dev.18":{"name":"@keep-network/random-beacon","version":"2.1.0-dev.18","_id":"@keep-network/random-beacon@2.1.0-dev.18","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2647731eea35f931afb565988cc8d8cf6a34f6b7","tarball":"https://registry.npmjs.org/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.18.tgz","fileCount":173,"integrity":"sha512-UrVq///+jqOLQ5k8/aFvD1ZUMvVe49iS81U8mDoi9A005FiQzKUK9QFKv3Z0h6joG4prF/hlABRTEQ1UA7tgRA==","signatures":[{"sig":"MEQCIAS1KwWps2hYrntN5TuwWpPnrrhQxlgfcC3rw1bHbvuRAiB0QfmuupZkxnEC45qfFRcq7jPxsT1SBpCqZzbOnPv+Cw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23820574},"readme":":toc: macro\n:icons: font\n\n= Keep Random Beacon v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-random-beacon.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-random-beacon.yml?branch=main&event=push&label=Random%20Beacon%20contracts%20build[Random Beacon contracts build status]]\n\nThe Keep Network requires a trusted source of randomness for the process of\ntrustless group selection. While the network requires that randomness to function\ncorrectly, the source of randomness is itself broadly applicable. This trusted\nsource of randomness takes the form of a BLS Threshold Relay.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nThe threshold relay is a way of generating verifiable randomness that is\nresistant to bad actors both in the relay network and on the anchoring Ethereum\nblockchain. The basic functioning of the relay is:\n\n- Some number of groups exist in the relay.\n- An arbitrary seed value `v_s` counts as the first entry in the relay.\n- A request `r_i` is dispatched to the chain for a new entry.\n- The previous entry `v_s` is used to choose a group to produce the response to\n  the request.\n- `v_s` is signed by at least a subset of the chosen group members, and the\n  resulting signature is the entry generated in response to the request. It is\n  published to the anchoring blockchain as the entry `v_i`.\n- The new entry `v_i` may trigger the formation of a new group from the set of\n  all members in the relay.\n- A group expires after a certain amount of time.\n\n== Prior Work\n\nSmart contracts for the first version of the random beacon are available in\nlink:https://github.com/keep-network/keep-core/tree/main/solidity-v1[`solidity-v1` directory].\nThe new version uses the same approach for BLS signatures as v1 but replaces\nticket-based group selection with an optimistic sortition pool call. It also\nredesigns staker rewards and offers a more operator-friendly approach for\nrelay entry timeouts. Last but not least, most parameters for the relay are\nnow governable. \n\n== The Mechanism\n\n=== Group Creation\n\nNew groups are created with a fixed frequency of relay requests.\nInstead of a v1 ticket-based approach for a signing group selection, we use\na sortition pool. Group creation start transaction is embedded into relay request\ntransaction and locks a sortition pool. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain, all off-chain\nclients perform group selection by calling `RandomBeacon.selectGroup()` view\nfunction for free. After determining group members, clients should perform\noff-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain calling\n`RandomBeacon.submitDkgResult(DKG.Result calldata dkgResult)` function.\nOnce the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `RandomBeacon.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`RandomBeacon.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nThere is a timeout before which a DKG result should be submitted.\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `RandomBeacon.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nDKG timeout includes the situation when no new relay entry was produced\nand sortition could not be performed.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator to the wallet signing group multiple times.\nOff-chain DKG protocol executes in the same way as for v1 and\ninactive/disqualified members during the off-chain protocol are marked as\nineligible for <<rewards,rewards>> for a governable period of time when the DKG\nresult is approved.\n\nEach group created in the system remains active for a certain period\nof time. A group that expired is no longer selected for any new work. Group\nexpiration is performed in the relay request transaction.\n\n=== Relay Request and Relay Entry\n\nAuthorized addresses can request a new relay entry (random number) by calling\n`RandomBeacon.requestRelayEntry(IRandomBeaconConsumer callbackContract)`\nfunction and providing an optional callback parameter.\n\nIn `requestRelayEntry` transaction, groups that reached their maximum lifetime\nare getting expired and one of the remaining active groups is tasked with\nproducing a new relay entry. The off-chain clients are expected to monitor the\n`RelayEntryRequested` event. If a client is a part of a picked group they should\nstart the off-chain protocol to sign the previous relay entry producing a new one.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting relay entry to avoid front-running and minimize the cost, but no\nordering is enforced on-chain. New relay entry should be submitted using \n`RandomBeacon.submitRelayEntry(bytes calldata entry)` function.\n\n=== Callbacks\n\nRandom Beacon supports simple, low-gas-budget callbacks from a relay entry\nsubmit transaction.\n\nWhen requesting a relay entry, it is possible to pass an optional address\nparameter - this is the address of a contract implementing\n`IRandomBeaconConsumer` interface that should be called when a new relay entry\nis submitted to the chain.\n\nSmart contract consuming new relay entry needs to implement `IRandomBeaconConsumer`\ninterface. The gas limit for `__beaconCallback` is initially set to 56k gas\nwhich is enough to `SSTORE` new relay entry, `SSTORE` block height in which the entry was submitted, and to emit an event.\nFailure in the callback function does not revert the relay entry transaction.\n\n```solidity\ninterface IRandomBeaconConsumer {\n    /// @notice Receives relay entry produced by Keep Random Beacon. This function\n    /// should be called only by Keep Random Beacon.\n    ///\n    /// @param relayEntry Relay entry (random number) produced by Keep Random\n    ///                   Beacon.\n    /// @param blockNumber Block number at which the relay entry was submitted\n    ///                    to the chain.\n    function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external;\n}\n```\n\n=== Timeouts\n\nThere are two timeouts for a relay entry to be provided by a group: soft timeout\nand hard timeout.\n\n==== Soft Relay Entry Timeout\n\nIf no entry was provided within the soft timeout, all operators in the group\nstart bleeding and losing their stake. The bleeding increases linearly from 0 to\nthe slashing amount per operator over time, until the hard timeout is\nreached or until a relay entry is submitted by the group.\n\nThe soft timeout is a governable parameter. This gives a chance to start\nwith more forgiving penalties and increase them over time. In general, the\nslashing penalty should be proportional to rewards and the frequency of relay\nrequests and associated risk.\n\n==== Hard Relay Entry Timeout\n\nWhen the hard timeout is reached, anyone can notify about this fact by calling\n`RandomBeacon.reportRelayEntryTimeout()` function and receive a\n<<punishment,notifier reward>> . The group which failed to submit a relay entry\nis terminated, group members are slashed, and if there are still active groups\nin the beacon, another group is selected and tasked with producing a relay entry\nfor the given relay request. \n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n[[inactivity]]\n=== Inactivity notification\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\ngroup member key material is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block and making sure this piece of information cannot be used for\n`RandomBeacon.reportUnauthorizedSigning()`. Specifically, the signed piece of\ninformation can not become `msg.sender` for `reportUnauthorizedSigning` call.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`RandomBeacon.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from\nthe sortition pool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the beacon sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`, `submitRelayEntry`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only public knowledge transaction is `notifyDkgTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nPunishment transactions are: `challengeDkgResult`, `reportRelayEntryTimeout`,\nand `reportUnauthorizedSigning`.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a group in the threshold relay.\n|No\n|`64`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`33`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`58` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n4+s|Groups\n\n|groupLifetime\n|Group lifetime in blocks.\n|Yes\nd|`259_200 blocks` +\n_~30 days assuming 15s block time_\n\n|groupCreationFrequency\n|The number of relay requests needed to kick off a new group creation process.\n|Yes\n|`2`\n\n4+s|Relay Entry\n\n|relayEntrySoftTimeout\n|Time in blocks during which a result is expected to be submitted.\n|Yes\nd|`1280 blocks` +\n_64 members * 20 blocks = 1280 blocks_\n\n|relayEntryHardTimeout\n|Hard timeout in blocks for a group to submit the relay entry.\n|Yes\nd|`5760 blocks` +\n_~24h assuming 15s block time_\n\n|callbackGasLimit\n|Relay entry callback gas limit.\n|Yes\nd|`64_000`\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|relayEntrySubmissionFailureSlashingAmount\n|Slashing amount for not submitting relay entry.\n|Yes\nd|`400e18` +\n_400 T_\n\n|relayEntryTimeoutNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about relay entry timeout.\n|Yes\n|`100`\n\n|unauthorizedSigningSlashingAmount\n|Slashing amount when an unauthorized signing has been proved.\n|Yes\nd|`400e18` +\n_400 T_\n\n|unauthorizedSigningNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about unauthorized signing.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Random Beacon\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`235_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`41_500`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`54_500`\n\n|relayEntrySubmissionGasOffset\n|Gas that is meant to balance the relay entry submission cost.\n|Yes\n|`11_250`\n\n|authorizedRequesters\n|Authorized addresses that can request a relay entry.\n|Yes\n|\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|===\n\n== Build\n\nRandom beacon contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy these contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14.18.2\n- https://yarnpkg.com[Yarn] >=1.22.17\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n```sh\nyarn install\n```\nOnce packages are installed, you can build the smart contracts using:\n```sh\nyarn build\n```\nCompiled contracts will land in the `build/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n```sh\nyarn test\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"d9a8706d5794d70014e14e8c52e7274471e0fb03","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat check-accounts-count && USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_BEACON=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep Random Beacon","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"4.7.3","@thesis/solidity-contracts":"github:thesis/solidity-contracts#4985bcf","@keep-network/sortition-pools":"^2.0.0-pre.16","@threshold-network/solidity-contracts":"1.3.0-dev.11"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.4.7","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.2.1","prettier":"^2.4.1","typechain":"^7.0.0","typescript":"^4.4.3","@types/chai":"^4.2.22","@types/node":"^16.10.5","@types/mocha":"^9.0.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","@typechain/hardhat":"^4.0.0","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^9.0.0","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.5.1","@thesis-co/eslint-config":"github:thesis/eslint-config#v0.2.0","prettier-plugin-solidity":"^1.0.0-beta.18","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.1","@tenderly/hardhat-tenderly":"1.0.12","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.0"},"_npmOperationalInternal":{"tmp":"tmp/random-beacon_2.1.0-dev.18_1706528684895_0.7052997931364886","host":"s3://npm-registry-packages"}}},"time":{"created":"2022-03-15T20:34:36.492Z","modified":"2026-08-11T14:11:43.537Z","2.0.0-dev.0":"2022-03-15T20:34:36.864Z","2.0.0-dev.1":"2022-03-24T13:49:59.346Z","2.0.0-dev.2":"2022-03-24T14:15:13.788Z","2.0.0-dev.3":"2022-03-24T17:22:28.197Z","2.0.0-dev.4":"2022-03-28T07:50:57.727Z","2.0.0-dev.5":"2022-03-29T13:56:35.909Z","2.0.0-dev.6":"2022-03-30T11:44:13.153Z","2.0.0-dev.7":"2022-03-31T10:34:17.216Z","2.0.0-dev.8":"2022-03-31T11:15:44.733Z","2.0.0-dev.9":"2022-04-01T11:12:56.871Z","2.0.0-dev.10":"2022-04-01T11:51:19.765Z","2.0.0-dev.11":"2022-04-06T13:45:38.795Z","2.0.0-dev.12":"2022-04-08T11:39:12.985Z","2.0.0-dev.13":"2022-04-11T15:56:18.688Z","2.0.0-dev.14":"2022-04-21T15:57:45.632Z","2.0.0-dev.15":"2022-04-22T13:14:25.635Z","2.0.0-dev.16":"2022-04-22T13:57:29.844Z","2.0.0-dev.17":"2022-04-22T14:05:44.719Z","2.0.0-dev.18":"2022-04-22T18:27:17.355Z","2.0.0-dev.19":"2022-04-23T18:12:28.083Z","2.0.0-dev.20":"2022-04-25T10:21:37.513Z","2.0.0-dev.21":"2022-04-25T12:23:35.874Z","2.0.0-dev.22":"2022-04-25T19:28:20.107Z","2.0.0-dev.23":"2022-04-26T09:32:45.605Z","2.0.0-dev.24":"2022-04-26T15:04:32.504Z","2.0.0-dev.25":"2022-04-27T06:02:00.252Z","2.0.0-dev.26":"2022-04-27T11:23:25.422Z","2.0.0-dev.27":"2022-04-28T18:39:52.289Z","2.0.0-dev.28":"2022-04-29T11:34:13.625Z","2.0.0-dev.29":"2022-04-29T12:13:01.331Z","2.0.0-dev.30":"2022-04-29T16:15:09.310Z","2.0.0-dev.31":"2022-05-01T21:18:02.864Z","2.0.0-dev.32":"2022-05-02T11:20:40.171Z","2.0.0-dev.33":"2022-05-10T16:55:14.190Z","2.0.0-dev.34":"2022-05-12T14:00:54.662Z","2.0.0-dev.35":"2022-05-12T16:00:23.055Z","2.0.0-dev.36":"2022-05-12T16:52:15.937Z","2.0.0-dev.37":"2022-05-16T10:18:14.285Z","2.0.0-dev.38":"2022-05-16T15:32:39.369Z","2.0.0-dev.39":"2022-05-17T07:59:27.909Z","2.0.0-dev.40":"2022-06-16T08:48:47.204Z","2.0.0-dev.41":"2022-07-01T10:46:00.620Z","2.0.0-dev.42":"2022-07-01T13:53:55.344Z","2.0.0-dev.43":"2022-07-05T07:45:32.145Z","2.0.0-goerli.0":"2022-07-06T11:08:57.574Z","2.0.0-dev.44":"2022-07-06T12:44:55.207Z","2.0.0-dev.45":"2022-07-07T10:59:17.188Z","2.0.0-dev.46":"2022-07-08T09:19:06.411Z","2.0.0-dev.47":"2022-07-08T12:27:12.398Z","2.0.0-dev.48":"2022-07-11T07:40:11.766Z","2.0.0-dev.49":"2022-07-11T09:32:43.339Z","2.0.0-dev.50":"2022-07-14T15:41:30.856Z","2.0.0-dev.51":"2022-07-25T15:30:57.975Z","2.0.0-dev.52":"2022-07-25T15:32:07.276Z","2.0.0-dev.53":"2022-07-28T08:44:55.045Z","2.0.0-dev.54":"2022-08-01T10:12:34.082Z","2.0.0-dev.55":"2022-08-03T13:37:04.870Z","2.0.0-dev.56":"2022-08-04T12:43:04.846Z","2.0.0-goerli.1":"2022-08-04T15:20:51.308Z","2.0.0-dev.57":"2022-08-04T22:04:12.112Z","2.0.0-dev.58":"2022-08-08T08:50:52.025Z","2.0.0-dev.59":"2022-08-08T08:53:00.454Z","2.0.0-dev.60":"2022-08-08T10:47:45.670Z","2.0.0-dev.61":"2022-08-08T16:53:53.355Z","2.0.0-goerli.2":"2022-08-08T19:04:00.084Z","2.0.0-goerli.3":"2022-08-09T07:07:44.981Z","2.0.0-goerli.4":"2022-08-09T08:55:08.773Z","2.0.0-dev.62":"2022-08-09T13:33:54.114Z","2.0.0-goerli.5":"2022-08-10T09:40:43.011Z","2.0.0-dev.63":"2022-08-10T12:20:44.656Z","2.0.0-dev.64":"2022-08-15T05:01:38.714Z","2.0.0-goerli.6":"2022-08-17T17:30:44.866Z","2.0.0-dev.65":"2022-08-18T09:00:24.149Z","2.0.0-dapp-dev-goerli.0":"2022-08-18T13:38:00.696Z","2.0.0-goerli.7":"2022-08-22T14:32:06.892Z","2.0.0-goerli.8":"2022-08-23T16:45:37.776Z","2.0.0-goerli.9":"2022-08-25T12:49:07.059Z","2.0.0-dapp-dev-goerli.1":"2022-09-01T15:52:46.361Z","2.0.0-dev.66":"2022-09-02T08:56:20.136Z","2.0.0-dev.67":"2022-09-02T08:57:56.649Z","2.0.0-goerli.10":"2022-09-06T09:58:08.725Z","2.0.0-dev.68":"2022-09-06T10:13:22.492Z","2.0.0-goerli.11":"2022-09-06T10:30:51.000Z","2.0.0-goerli.12":"2022-09-12T12:15:42.336Z","2.0.0-dev.69":"2022-09-13T06:14:34.153Z","2.0.0-dev.70":"2022-09-13T15:57:05.319Z","2.0.0-dev.71":"2022-09-16T10:53:52.566Z","2.0.0-dev.72":"2022-09-22T13:58:27.783Z","2.0.0-dev.73":"2022-09-25T14:13:58.523Z","2.0.0-dev.74":"2022-09-26T21:18:21.716Z","2.0.0-dev.75":"2022-09-28T10:48:06.459Z","2.0.0-dev.76":"2022-09-28T12:49:08.683Z","2.0.0-dev.77":"2022-09-28T15:29:43.835Z","2.0.0-dev.78":"2022-09-29T10:05:31.429Z","2.0.0":"2022-09-29T12:38:29.646Z","2.1.0-dev.0":"2022-09-29T15:33:08.153Z","2.1.0-goerli.0":"2022-09-29T17:00:45.862Z","2.1.0-goerli.1":"2022-09-30T06:34:44.749Z","2.1.0-dev.1":"2022-12-20T15:59:22.566Z","2.1.0-dev.2":"2022-12-23T11:49:33.031Z","2.1.0-dev.3":"2022-12-30T12:15:35.577Z","2.1.0-dev.4":"2023-01-03T16:13:36.059Z","2.1.0-dapp-dev-goerli.0":"2023-01-05T10:08:31.028Z","2.1.0-dev.5":"2023-01-12T12:30:06.318Z","2.1.0-goerli.2":"2023-01-13T10:44:46.145Z","2.1.0-goerli.3":"2023-01-13T13:22:46.889Z","2.1.0-goerli.4":"2023-01-13T16:51:52.428Z","2.1.0-dapp-dev-goerli.1":"2023-01-19T11:02:33.615Z","2.1.0-dapp-dev-goerli.2":"2023-01-20T10:57:04.758Z","2.1.0-dapp-dev-goerli.3":"2023-01-20T11:10:56.333Z","2.1.0-goerli.5":"2023-01-20T12:26:53.007Z","2.1.0-goerli.6":"2023-01-23T23:34:40.728Z","2.1.0-dapp-dev-goerli.4":"2023-01-24T11:58:59.235Z","2.1.0-dev.6":"2023-04-17T11:21:08.263Z","2.1.0-dev.7":"2023-04-17T11:24:00.089Z","2.1.0-dev.8":"2023-04-17T11:26:01.915Z","2.1.0-dev.9":"2023-04-18T08:32:50.934Z","2.1.0-dev.10":"2023-06-06T12:42:38.911Z","2.1.0-dev.11":"2023-06-09T06:39:03.238Z","2.1.0-dev.12":"2023-06-13T14:46:45.981Z","2.1.0-dev.13":"2023-06-13T16:07:44.076Z","2.1.0-dev.14":"2023-06-14T09:09:02.825Z","2.1.0-dev.15":"2023-06-28T08:04:50.710Z","2.1.0-dev.16":"2023-06-29T04:41:57.934Z","2.1.0-sepolia.0":"2023-09-20T15:06:59.452Z","2.1.0-dev.17":"2023-09-25T08:04:39.513Z","2.1.0-sepolia.1":"2023-10-17T13:39:09.857Z","2.1.0-dapp-dev-sepolia.0":"2023-11-07T15:13:24.093Z","2.1.0-dev.18":"2024-01-29T11:44:45.097Z"},"description":"Keep Random Beacon","maintainers":[{"email":"antonio@thesis.co","name":"shadowfiend"},{"email":"kuba@akena.co","name":"nkuba8"},{"email":"heimdall@thesis.co","name":"thesis-heimdall"},{"email":"piotr.dyraga@thesis.co","name":"pdyraga"},{"email":"lukasz.zimnoch@keep.network","name":"lukasz-zimnoch"},{"email":"michalina.cienciala@keep.network","name":"michalinacienciala"},{"email":"dmitry.paremski@gmail.com","name":"dimpar"}],"readme":"","readmeFilename":""}