Skip to content

Smart contracts for POSDAO (Proof of Stake Decentralized Autonomous Organization consensus), a DPOS consensus implemented in Solidity and running within EVM with swappable BFT consensus

Notifications You must be signed in to change notification settings

lab10-coop/posdao-contracts

 
 

Repository files navigation

POSDAO Smart Contracts

Implementation of the POSDAO consensus algorithm in Solidity.

About

POSDAO is a Proof-of-Stake (POS) algorithm implemented as a decentralized autonomous organization (DAO). It is designed to provide a decentralized, fair, and energy efficient consensus for public chains. The algorithm works as a set of smart contracts written in Solidity. POSDAO is implemented with a general purpose BFT consensus protocol such as AuthorityRound (AuRa) with a leader and probabilistic finality, or Honeybadger BFT (HBBFT), leaderless and with instant finality. It incentivizes actors to behave in the best interests of a network.

The algorithm provides a Sybil control mechanism for reporting malicious validators and adjusting their stake, distributing a block reward, and managing a set of validators. The authors implement the POSDAO for a sidechain based on Ethereum 1.0 protocol.

POSDAO Repositories and Resources

Smart Contract Summaries

Note: The following descriptions are for AuRa contracts only. HBBFT contract implementations are not started yet and are not listed nor described here. All contracts are located in the contracts directory.

  • BlockRewardAuRa: generates and distributes rewards according to the logic and formulas described in the white paper. Main features include:

    • distributes the entrance/exit fees from the erc-to-erc, native-to-erc, and/or erc-to-native bridges among validators pools;
    • mints native coins needed for the erc-to-native bridge;
    • makes a snapshot of the validators stakes at the beginning of each staking epoch. That snapshot is used by the StakingAuRa.claimReward function to transfer rewards to validators and their delegators.
  • Certifier: allows validators to use a zero gas price for their service transactions (see Parity Wiki for more info). The following functions are considered service transactions:

    • ValidatorSetAuRa.emitInitiateChange
    • ValidatorSetAuRa.reportMalicious
    • RandomAura.commitHash
    • RandomAura.revealNumber
  • InitializerAuRa: used once on network startup and then destroyed. This contract is needed for initializing upgradable contracts since an upgradable contract can't have the constructor. The bytecode of this contract is written by the scripts/make_spec.js into spec.json along with other contracts when initializing on genesis block.

  • RandomAuRa: generates and stores random numbers in a RANDAO manner (and controls when they are revealed by AuRa validators). Random numbers are used to form a new validator set at the beginning of each staking epoch by the ValidatorSet contract. Key functions include:

    • commitHash and revealNumber. Can only be called by the validator's node when generating and revealing their secret number (see RANDAO to understand principle). Each validator node must call these functions once per collection round. This creates a random seed which is used by ValidatorSetAuRa contract. See the white paper for more details;

    • onFinishCollectRound. This function is automatically called by the BlockRewardAuRa contract at the end of each collection round. It controls the reveal phase for validator nodes and punishes validators when they don’t reveal (see the white paper for more details on the banning protocol);

    • currentSeed. This public getter is used by the ValidatorSetAuRa contract at the latest block of each staking epoch to get the accumulated random seed for randomly choosing new validators among active pools. It can also be used by anyone who wants to use the network's random seed. Note, that its value is only updated when revealNumber function is called: that's expected to be occurred at least once per collection round which length in blocks can be retrieved with the collectRoundLength public getter. Since the revealing validator always knows the next random number before sending, your DApp should restrict any business logic actions (that depends on random) during reveals phase.

      An example of how the seed could be retrieved by an external smart contract.
      pragma solidity 0.5.11;
      
      
      interface IPOSDAORandom {
          function collectRoundLength() external view returns(uint256);
          function currentSeed() external view returns(uint256);
      }
      
      contract Example {
          IPOSDAORandom private _posdaoRandomContract; // address of RandomAuRa contract
          uint256 private _seed;
          uint256 private _seedLastBlock;
          uint256 private _updateInterval;
      
          constructor(IPOSDAORandom _randomContract) public {
              require(_randomContract != IPOSDAORandom(0));
              _posdaoRandomContract = _randomContract;
              _seed = _randomContract.currentSeed();
              _seedLastBlock = block.number;
              _updateInterval = _randomContract.collectRoundLength();
              require(_updateInterval != 0);
          }
      
          function useSeed() public {
              if (_wasSeedUpdated()) {
                  // using updated _seed ...
              } else {
                  // using _seed ...
              }
          }
      
          function _wasSeedUpdated() private returns(bool) {
              if (block.number - _seedLastBlock <= _updateInterval) {
                  return false;
              }
      
              _updateInterval = _posdaoRandomContract.collectRoundLength();
      
              uint256 remoteSeed = _posdaoRandomContract.currentSeed();
              if (remoteSeed != _seed) {
                  _seed = remoteSeed;
                  _seedLastBlock = block.number;
                  return true;
              }
              return false;
          }
      }
  • Registry: stores human-readable keys associated with addresses, like DNS information (see Parity Wiki). This contract is needed primarily to store the address of the Certifier contract (see Parity Wiki for details).

  • StakingAuRa: contains staking logic including:

    • creating, storing, and removing pools by candidates and validators;
    • staking tokens by participants (delegators, candidates, or validators) into the pools;
    • storing participants’ stakes;
    • withdrawing tokens and rewards by participants from the pools;
    • moving tokens between pools by participant.
  • TxPermission: controls the use of zero gas price by validators in service transactions, protecting the network against "transaction spamming" by malicious validators. The protection logic is declared in the allowedTxTypes function.

  • ValidatorSetAuRa: stores the current validator set and contains the logic for choosing new validators at the beginning of each staking epoch. The logic uses a random seed generated and stored by the RandomAuRa contract. Also, ValidatorSetAuRa along with modified Parity client is responsible for discovering and removing malicious validators. This contract is based on reporting ValidatorSet described in Parity Wiki.

For a detailed description of each function of the contracts, see their source code or the Autogenerated docs.

Upgrading

Since POSDAO is also going to be deployed on existing chains, this repository covers that use case too.

scripts/make_pre_upgrade_spec.js takes the template chain spec in pre-upgrade-spec.json (which is similar to the chain spec of the xdai chain) and adds the bytecode of minimal ValidatorSet and BlockReward contracts, active from block 0.

scripts/deploy_contracts_and_update_spec.js needs a running chain it connects to via RPC (env var RPC_URL) and its chain spec file (env var SPEC_FILE). It does basically the same like scripts/make_spec, except that the contracts are deployed onto a running chain (and then initialized) instead of written into the genesis block via spec file.
The fork block needs to be set via env var FORK_BLOCK. It's the caller's responsibility to choose a fork block number which lies in the future and which leaves enough time for all contract deployment and initialization transactions to be processed first.
The caller also needs to make sure that the connected node has a funded account unlocked. The address of the deployer account can be set via env var DEPLOYER_ADDR. If unset, the script tries with the first account exposed by the RPC node.

Usage

Install Dependencies

$ npm install

Testing

Note: Test development for unit testing and integration testing is in progress.

Integration test setup is available here: https://github.com/poanetwork/posdao-test-setup

To run unit tests:

$ npm run test 

Flatten

Flattened contracts can be used to verify the contract code in a block explorer like BlockScout or Etherscan. See https://forum.poa.network/t/verifying-smart-contracts/1889 for Blockscout verification instructions.

To prepare flattened version of the contracts:

$ npm run flat

Once flattened, the contracts are available in the flat directory.

Contributing

See the CONTRIBUTING document for contribution, testing and pull request protocol.

License

Pending

About

Smart contracts for POSDAO (Proof of Stake Decentralized Autonomous Organization consensus), a DPOS consensus implemented in Solidity and running within EVM with swappable BFT consensus

Resources

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 58.1%
  • Solidity 41.3%
  • Other 0.6%