This document provides informative notes about good practices for deploying Enterprise Ethereum permissioned blockchains.

This Draft Specification is copyright © 2022 Enterprise Ethereum Alliance Incorporated (EEA). It is made available under the terms of the Apache License version 2.0. [[Apache2]]

This is an editors' draft of material that might be developed into Enterprise Ethereum Alliance Permissined Blockchain Deployment Guidance. It has not been approved for publication by the Enterprise Ethereum Alliance (EEA) Core Specifications Working Group nor anyone else. It is not appropriate to quote or reference this document except as "Work in Progress", and it should be considered unreliable and unstable in its present state. Please send any comments to the EEA Technical Steering Committee at [](


This document describes good practices and information that is useful when deploying an Enterprise Ethereum permissioned blockchain.

Enterprise Ethereum is the set of enterprise-focused extensions to public Ethereum that are defined in the Enterprise Ethereum Alliance Client Specification [[EEA-clients]]. These extensions provide the ability to perform private transactions, and enforce access controls (permissioning), for Ethereum blockchains that use them. Such blockchains are formally known as Enterprise Ethereum Permissioned Blockchains.

Public Ethereum is the public blockchain-based distributed computing platform featuring smart contract (programming) functionality defined by the [[Ethereum-Yellow-Paper]], [[EIPs]], and associated specifications. The mainnet is a public ethereum blockchain, with `chainId` and `NetworkId` values of 1.

There are other public ethereum blockchains, usually with slightly different characteristics. For example "testnets" like Ropsten, Rinkeby, and Goerli are all used to test Ethereum technnology.

About the EEA Client Specification

The Enterprise Ethereum Client Specification [[EEA-clients]] defines the implementation requirements for Enterprise Ethereum clients, including the interfaces to external-facing components of Enterprise Ethereum and how they are intended to be used. A partial list of the use cases [[EEA-client-usecases]] that specification attempts to address is available, as an editors' draft for a work in progress. A companion document, the Enterprise Ethereum Alliance Permissioned Blockchains Specification [[EEA-chains]] defines requirements for Enterprise Ethereum blockchains to ensure that clients that conform to the Client Specification can work interopably on blockchains that meet the requirements defined in that document.

Experimental Requirements

The Client Specification includes requirements and APIs that are described as experimental. This means that a requirement or API is in early stages of development and might change as feedback is incorporated. Implementors are encouraged to implement these experimental requirements, with the knowledge that requirements in future versions of the Specification are not guaranteed to be compatible with the current version. Please send comments and feedback on experimental portions of the Specification to the EEA via [](

Difference to public Ethereum Blockchains

An Enterprise Ethereum Permissioned Blockchain extends the standard capabilities of Ethereum with the following changes:


An Enterprise Ethereum Client is used to interact with an Enterprise Ethereum Permissioned Blockchain. Core to the interaction are the permissioning contracts. These determine whether a given node is permitted to access a blockchain, and whether a given transaction submitted is valid based on factors such as the accounts involved, whether they deploy smart contracts, and the like. An Enterprise Ethereum Permissioned Blockchain has a permissioning contract, that enables an Enterprise Ethereum Client to call the methods connectionAllowed and transactionAllowed at a known address, to determine whether to allow an incoming connection, or whether to treat a given transaction as valid. An Enterprise Ethereum Client can cache results from calling the methods of a permissioning contract, in order to minimise such calls. To do so safely, it must listen for update events emmitted by the contract, and modify its cache appropriately.

Private Transactions

An Enterprise Ethereum Client is required to implement a form of private transaction; either restricted private transactions or unrestricted private transactions. Enterprise Ethereum clients implement the `eea_sendTransaction` and `eea_sendRawTransaction` JSON-RPC-API methods to submit private transactions to the blockchain.
Restricted Private Transactions
Transactions communicated directly between the parties involved. These are notarised in a marker transaction on the blockchain, that records a transaction took place, but does not contain the details of the transaction. Instead, those details are only available to nodes that are party to the transaction.
Unrestricted Private Transactions
Transactions that are recorded on the blockchain, but only parties to the transaction have the necessary information to interpret the full meaning of the transaction, using a mechanism such as zero-knowledge proofs.

Configuration Parameters

The Enterprise Ethereum client specification defines a standard form of Network Configuration Parameters, using a JSON object in the genesis block that Enterprise Ethereum Clients are required to interpret. The Enterprise Ethereum-specific parameters defined are:
The maximum size of a smart contract to be valid for deployment. It can either specify an integer, which means a number of kilobytes, or a JSON object describing limits applied for a range of blocks, to enable nodes to correctly validate when trying to sync an existing blockchain where the value has changed over time.
The address to use when calling the connectionAllowed method.
This specifies the address to use when calling the transactionAllowed method.
The optional organizationRegistryContract
The address of an Organization Registry contract, if there is one.

Consensus Algorithms

Conforming Enterprise Ethereum Clients implement the Byzantine Fault-tolerant [[QBFT]] Consensus Algorithms defined by the EEA, and the Clique Proof of Authority algorithm defined in [[EIP-225]]. Most clients implement more consensus algorithms. Clients that also operate on the pre-merge MainNet need to implement EthHash (Proof of Work).

Organization Registry

An Enterprise Ethereum Permissioned blockchain can implement an Organization Registry. This is a contract defined in the chainspec [[EEA-chains]] that establishes if an account is acting on behalf of a known organization.

Smart Contract Tools Sublayer

Implementations need to provide deployment and debugging tools for Enterprise Ethereum smart contracts. Tools used in public Ethereum, for example, include [[Truffle]] and [[Remix]].

Implementations should also extend formal verification methods for use with Enterprise Ethereum smart contracts.

Integration Libraries

Implementations might provide integration libraries enabling convenience of interaction through additional language bindings. These libraries might include [[web3j]], [[web3.js]], [[Nethereum]], [[protocol-buffers]], or a REST API.

Enterprise Management Systems

Performance and Scalability

Performance is important to many (but not all) Enterprise Ethereum use cases. However, performance is multidimensional; attempts to require specific performance targets on a single aspect generally leads to all implementations being optimised for that target instead of meeting the varying needs of Enterprise Ethereum customers.

There are some common goals however. Specifically, increasing the cost of a blockchain over time is incompatible with many use cases, and many use cases need to ensure significant scalability is feasible.

Constant Power Requirements

The computing power to validate blocks is meant to remain constant over time, regardless of the blockchain size or the number of network participants. The constant power requirements include:

- Ensuring the power required to validate a block does not increase significantly.


Off-chain processing is a mechanism that can improve scalability. Likewise, Sharding, a mechanism proposed for Ethereum 2.0, will probably improve scalability too.

Storage and Ledger Sublayer

Implementations might implement data storage used for optional off-chain operations. For example, implementations could locally choose to cache the results from a trusted oracle or store information related to other systems extensions not covered by the [Client Specification](

Persistent storage that is resistant to brute force attacks generally uses a "defense-in-depth" approach. For example, instead of relying on restricted access to protect plain text storage, an implementation can further protect the data by encrypting it with an Authenticated Encryption with Additional Data (AEAD) algorithm, such as one described in [[RFC5116]].

## Blockchain deployment and operations In this chapter, we will discuss blockchain deployment and operation principles. ### Philosophy The initial goal of blockchain is to remove intermediates and run without interference of other agents, maximizing independence of hardware, network and software. In short order, blockchain software should run on different machines in different locations, hosted by different providers (ideally self-hosted), running different implementations of the blockchain protocol. The goal is to mimic the structure of the Internet, where there is enough information redundancy that it would be possible for the blockchain network to survive a rift of connections. A blockchain client - the name of the software interfacing with the chain - is meant to run in an adversarial environment, where every piece of information it encounters must be validated. The only information initially provided is the genesis block of the chain. The software syncs up all blocks and recomputes the state of the chain from scratch. Operators typically migrate between nodes by spinning up a new node and allowing the new node to sync with the existing node. #### Keys Blockchain nodes use a cryptographic identity to identify themselves on the network. When the chain starts, the node exposes this identity. Blockchain networks communicate boot nodes that allow users to enter the network and discover additional peers. Separately, Blockchain nodes can contain the cryptographic identity of the user operating the chain. In that case, the key is stored as a file alongside the data of the chain. The user has to unlock their account upon starting the software. Alternatively, users can use a separate wallet to sign their transactions and only send validated and signed transactions to the chain. This method provides additional security to transactions as well. #### Discovery Public chain clients connect to initial boot nodes, which help them discover new nodes, from which they can get entrance to the network and peer randomly, ensuring that they are not peered with a super-majority of dishonest nodes. In private chains, discovery is typically disabled. Instead, all peers know of all other peers at startup through a configuration file. The configuration file contains the IP, port and public key of the peers. Additional peers can be added at runtime through a command. ### Private blockchain considerations #### Private enclave Quorum adds one more layer of communications with the addition of a private enclave. Private enclaves communicate with all other private enclaves on the network, using point-to-point communications. Private enclaves hold keys to store private data associated with the private state held by the instance. #### Private blockchain consensus algorithms Quorum uses consensus algorithms such as Raft, Clique or IBFT. They all require different types of uptime: - Clique requires 100% uptime of the signer node, or no blocks can be accepted - Raft requires a leader to be elected, which requires a minimum of 3 nodes to perform the election. If the leader experiences downtime, the vote restarts. - IBFT is meant to remain reliable if two thirds of the validators are able to cast votes. Additionally, Clique and IBFT strongly tie the consensus to the identity of the clients running the network. All those considerations play a role in picking the right algorithm for your blockchain. ### Example of a private blockchain deployment - A consortium of four companies assembles to create shared infrastructure in the form of a blockchain. They elect to use Raft as its consensus algorithm. - Each participant runs a blockchain node. - They all deploy independently the software on machines they own, and send to each other the IP, port, and public key of their nodes. - They decide on an initial genesis block, allocating funds to each member. They create a genesis block file. They also create a static nodes file. All members use both files to configure their clients and turn on their blockchain. - Once the chain is able to function, they configure private enclaves that connect to their blockchain node. They share their private enclave IP and port and use it as part of the configuration of their enclaves, so they can connect directly. - They now turn on their private enclave and node and can start exchanging data. ### Deployment lifecycle #### Initial deployment Smart contracts have the opportunity to set permissions during the initialization phase of the contract. Typically, the account that deploys the contract is set as the owner. It is possible to create more complex situations with multiple owners. #### Proxy contracts Whenever a contract is upgraded, it changes its address. Therefore, it is very important to use a proxy contract that will allow it to keep the same address for all peers. In the same way, a contract should use an interface to control access to its public methods. This interface should not change. Instead, the contract should adopt additional interfaces if necessary. ## Interfacing with blockchain software ### JSON-RPC Ethereum clients expose to users an interface which accepts JSON payloads and responds with JSON data, over HTTP, websockets, or UNIX file socket. ### Querying data on chain Users may write solidity and invoke contract functions marked with the modifier “view” to execute read-only functions. The EVM executes the query and returns the output of the execution against the local node state. ### Executing transactions Users also may send transactions to be included in blocks. They can either send a signed transaction, or submit a transaction to be signed on one of the accounts on the blockchain node. Ethereum identifies different types of transactions: - Deploying a contract - Sending funds between accounts - Invoking contract functions ## Developing against a blockchain ### Frameworks web3.js and ethers.js are popular frameworks used by Ethereum to connect via JSON-RPC and perform operations transparently. They allow deployment of contracts, execution of transactions, and more as described below. It is strongly advised to connect to a blockchain node from server-side rather than client-side, as users browsers are insecure environments, and they can close a browser window while an operation is ongoing. ## Project structure We recommend, as much as it is possible, to use a single code repository for all assets. Assets generally consist of smart contracts, backend code, frontend application and deployment scripts, alongside unit tests for all components, documentation and integration tests. ### Code contributions Code contributions to the project must be made as pull requests, so the functionality is tracked as a single set of commits. Each contribution should run tests to ensure its validity, and be reviewed and approved for merge by a team member. ### Releases Releases should primarily consist of a tag of the git repository. Any package made from the repository should be signed by the developer’s GPG key and its signature should be outlined next to the download file. ## Testing ### Blockchain smart contract security testing - Perform static analysis with [Slither]( - Run the smart contracts through [Mythx]( to review them, so they can be flagged for vulnerabilities. If the development team uses Mythx, ask for the report. - In particular, check any math. All math operations should use [OpenZeppelin libraries]( to perform safe math. - Run through functional testing and make sure all functionality requested works as expected. ### Network testing Network testing consists in checking the security of the network against attackers. Tools such as Nessus or Qualys perform network tests by checking for open ports and validating the TLS and SSL cryptographic algorithms allowed to connect to the system. ### Migrations testing - Check the delta between the two releases in patch form. Make sure the changes make sense. - Check that sources can be built and match the binaries obtained. Ideally, the build process should generate binaries and archives that have the same hash as the binaries provided. - Open the smart contracts in VSCode (see [this guide]( - [Install SOLC]( (use the same version as used in development) - Run: `solc --abi --bin /path/to/contracts/file.sol` - This will generate the bytecode and ABIs. - Check that the bytecode matches what is deployed. ## Blockchain operations ### Setting up machines #### Encryption at rest All machines must use disk encryption at rest, so hard drives cannot be retrieved and read elsewhere by a malicious attacker. #### Static IPs The location of a node will be advertised directly by the public IP it advertises on the Internet, especially for public networks. For private blockchains, it’s just as important to have fixed IPs for all nodes available. If using a cloud provider such as AWS, node operators must use a static IP assignment. The DNS name of the machine matters little for peer-to-peer networking as most blockchains do not rely on DNS for discovery. #### Machine sizing A typical machine hosting a blockchain should prioritize fast I/O, especially when syncing large states for Ethereum mainnet. SSD disks are necessary. At least 2 CPUs are necessary to run crypto verifications and secure the network. A minimum of 8Gb of RAM is advised, especially if the system runs a private enclave such as Tessera. #### Firewall When running an Ethereum node, the machine should expose the following ports: - 30303 UDP and TCP for Ethereum traffic - 8545/8546 for HTTP and WS traffic - 9000 for the private enclave, if running Quorum Please note all those ports can be configured to different values as needed. Ports 8545 and 8546 must be protected from attackers trying to gain access to the JSON-RPC port. If all applications run on the same machine, this port can be closed. Otherwise, it should only be opened to applications running in the same network. Ports 30303 and 9000 must be available to all other nodes in the network. In the case of a private blockchain deployment, one can set stringent network rules to only allow access to other members. Note additional members may be added over time and those rules will need to be updated as such events occur. ### Securing keys Keys are irreplaceable in cryptographic systems. They must be protected at all times. Operators can choose options here, going from least to most stringent. - Store the key as a mnemonic sequence of words (as described in [BIP39]( in a password manager such as 1Password. - Use GPG to encrypt the key and share the key with other team members, encrypted with their GPG key for backup. - Use a cold wallet to store the key. Once set, it never leaves the wallet. The cold wallet must be inserted into a computer to sign content. - Use a hardware security module (HSM) server that is deployed in a physical data center. - Use Unbound Key Control system to automate the lifecycle of the key management. ### Genesis block generation Each network must start with a common genesis block generated initially by an operator. This operation is generally done manually with a text editor. The genesis block is written in JSON and encodes initial information about the network, allocating money to accounts and initializing state and code for accounts. An [example of genesis block]( ### Static peer configuration All peers must connect to each other using a configuration file listing all IPs, ports and public keys of each of the nodes. In the case of Ethereum, the static node configuration files contains a JSON array with a list of strings, each string representing the encoded URL of a node, starting with the enode scheme. ## Maturity model This maturity model is provided to help apply the best practices in this document, with the explicit goal of creating discussions around the development and operations of blockchain-based software. We first introduce the dimensions of our maturity model, and offer a checklist to help understand the maturity of the software across each dimension. For each dimension, the checklist will offer a deterministic measure and will allow to list a mitigation that can be fulfilled by the team. There is some overlap between dimensions, and a particular requirement may come up in a different context. In that case however, different mitigations can be offered. ### Maturity model requirement template A requirement will have the following attributes: - **Identifier**: a unique identifier allowing to refer to the requirement - **Recurrence**: a recurring time period to review the requirement - **Definition**: the formal description of the requirement - **Example**: an example of implementation of the requirement ### Security This section of the maturity model deals with the security measures and processes in place to secure the blockchain deployment and operation of the solution. It concerns the aspects of the physical and operating system operations, its networking, up to the smart contract setup and permission system, as well as how the application interfaces with it. ### Independence This section of the maturity model evaluates how much the team running the blockchain can operate independently from the other participants of the blockchain. It concerns everything from the choice of consensus algorithm, the network connectivity and information to other participants to the deployment and ownership of smart contracts. ### Flexibility This section evaluates how flexible the deployment is, and how easy it is to change the deployment to match changes to the business and operating requirements of the solution. The checklist covers every aspect of the business requirements changes, such as adding a new partner to the chain or changing a smart contract to reflect a new business flow. ## Maturity model checklist ### SEC-1 **Data is stored on hard drives encrypted at rest** "The team sets up machines in the cloud environment with encryption enabled at rest. Every year, the team conducts an audit to make sure all volumes are set to be encrypted at rest. When a volume is found to be unencrypted, the team backs up the data and create a new volume." ### SEC-2 **Networking limits access to specific ports** The team relies on an Infrastructure as code tool such as Terraform to enforce which ports are open. The team runs checks annually to make sure the configuration is adequate and no extra ports are open. ### SEC-3 **All private keys are secured** The team assigns a chain of responsibility and document their practices with regards to keys. Every quarter, they review and train on their keys. If they use passwords to secure the keys, they rotate them at the same time. ### SEC-4 **Key custody processes are implemented.** The team gives access explicitly to keys to a specific member of the team for a specific period of time. Keys are always stored encrypted and password protected. ### SEC-5 **The team implements and runs network security tests** The team maintains a set of tests to test the network of the solution. Every quarter, the team runs those tests, using network testing tools, to ensure all tests pass. Whenever a change is made to the solution, network tests run on a staging environment before deployment to production. ### SEC-6 **The team implements and runs smart contract tests** The team maintains a set of tests to test the smart contracts of the solution. Those tests run for each change to smart contracts as part of continuous integration. Functional tests also check the behavior of the system as a whole, and run for each release. ### SEC-7 **The team implements and tests for software defects** The team maintains a set of unit tests to test the solution. Those tests run for each change to the code as part of continuous integration. The team also maintains integration tests that run against a staging environment. ### SEC-8 **The team collects and analyzes logs** The team collects logs and analyzes them weekly, looking for specific error codes or error patterns that are documented and shared with the team. Every week, an operator compiles the log analysis and shares it with the team. ### SEC-9 **The team backs up and restores data** The team has developed a way to create backups, and is able to restore them to a different environment. Every month, they reset the staging environment with the latest backup, ensuring the solution is still able to perform by exercising integration tests against it. ### SEC-10 N/A **All participants in the network (smart contract enclave) must be given access to interact with any smart contract** The team must manage the database of whitelisted addresses as well as access control - should be built into the smart contracts. An unknown address should not be able to access or edit any data in a smart contract. ### IND-1 **Operators run on different machines** Operators specify and check in establishing the network that all blockchain clients run on different machines. ### IND-2 **Operators run on different cloud providers** Operators specify and check in establishing the network that nodes of the network use different cloud providers. ### IND-3 **Operators run in different geographical zones** Operators specify and check in establishing the network that nodes are dispersed geographically, with at least 2 different zones. ### IND-4 **Operators deploy their own contracts** Operators deploy the contracts that are under their responsibility. ### IND-5 **Operators can upgrade their own contracts** Operators have access and can upgrade their contracts at will. ### IND-6 **Operators host the software interfacing with the blockchain** Operators deploy and host the software interfacing with the JSON-RPC interface of the blockchain, so they may control its direct access. ### IND-7 **Operators can change their network configuration (IP, port) without breaking the network** Operators check monthly that their IP and port allocations are correct for themselves and all the partners. In the event of a change, they prepare for a migration and changing configuration files at that time and perform that change at the same time as the partners. ### IND-8 **Operators can upgrade their blockchain client without consortium downtime** Operators run tools to update their blockchain client without suffering downtime. They select to upgrade clients outside of business hours. ### IND-9 **Operators can test the system independently** Operators perform tests against the system and can stage a copy of the system on their own. They can test that the system performs to function, securely, and with appropriate performance, without involving a partner. ### FLE-1 **Operators can upgrade smart contracts at will** Operators run an automated script that securely allows them to upgrade smart contracts at will. They use a proxy contract so they can change the address of the contract without touching all other contracts. All contracts use interfaces, and are maintained so no breaking changes are introduced. ### FLE-2 **Operators have complete vision of the usage and data flowing through the system** Operators can instantly see what requests are in process in the system, what requests and data exchanges occurred in the last 30 days, and the current data stored in the system. ### FLE-3 **Operators can test changes to the system using continuous delivery** Operators build and maintain a deployment pipeline that deploys automatically changes to a staging environment, where tests run to check the behavior of the system. ### FLE-4 **Operators can issue and deploy urgent patches for any part of the system (network, blockchain, software, smart contract)** Operators can cut new releases with an automated script that requires little input. A release can then be deployed automatically. The whole process takes less than 30 minutes. ### FLE-5 **Operators can deploy changes without downtime** Operators stack changes in sequence to avoid breaking changes that would provoke down time. Developers write backwards-compatible code that is able to work with different versions of the software they interface with. Contracts can be first deployed, then used as part of execution in a separate step. ### FLE-6 **Operators can modify the consortium configuration at will** Operators have full access to the consortium configuration, and can add or remove new peers to the network using configuration files.

Permissioning Requirements

Enterprise Ethereum clients might support local key management allowing users to secure their private keys.

Clients might also support secure interaction with an external key management system for key generation and secure key storage.

For example, implementations could securely interact with a Hardware Security Module (HSM), a physical device to provide strong and secure key generation, key storage, and cryptographic processing for deployments where strong security is needed.

Permissioning Management Examples

The smart contracts that allow Enterprise Ethereum clients to apply permissioning require a management system behind them. This section provides some examples of how such a system might work, but each Enterprise Ethereum blockchain is free to implement whatever system is suitable, as long as it includes the required functions for clients to query.

Permissioning Contracts: Interoperable on chain node whitelist for testnet

This section describes an approach for achieving interoperable node permissioning using an on chain white list. The interop is desired between as many enterprise ethereum clients as possible. Whilst this example is for node permissioning, the approach can be extended to include account permissioning. The components described in the remainder of this section align with the Permissioning Management and Permissioning Enforcement sections described in the Permissioning Smart Contract section of the Enterprise Ethereum Alliance Client Specification [[EEA-clients]].

Permissioning Management

The on chain white list consists of smart contract management functions essentially implementing the following operations: - Add a node to whitelist. - Remove a node from whitelist. - Get whitelist which returns the nodes in the whitelist. These smart contract functions can be based on one of the existing node permissioning white listing smart contracts provided by one of the client vendors. These include: - [Autonity whitelist management smart contract]( - [Besu whitelist management smart contract]( - [Quorum whitelist management smart contract]( - Strato's whitelist management smart contract is the basis of the ["member groups and authorised users" contracts](#sec-permissioning-contracts-memberGroups-and-authorizedUsers) in this document These whitelist management functions are called from a Dapp or utility function via RPC utilising an authorized account to perform the transactions.

Permissioning Enforcement

Integration with EEA Spec node permissioning `connectionAllowed` smart contract function
The on chain whitelist smart contracts include an implementation of the node permissioning enforcement function `connectionAllowed` as defined in the EEA client specification. This function will access the on-chain permissioning data to determine if a connection is allowed.
Integration for clients that do not yet utilise the EEA Spec node permissioning smart contract function
Some clients do not yet utilise the EEA Spec node permissioning smart contract function `connectionAllowed`. Instead, they utilise other non-EEA smart contract functions to perform this task. In order to integrate these clients into the permissioned network, they need to operate on the same on-chain permissioning data. This is achieved by implementing the non-EEA smart contract functions which access the same on-chain permissioning data as the EEA functions.

Permissioning Contracts: Simple Authorization Whitelist

This is an example of a basic permissioning contract that maintains a whitelist of enodes allowed to participate in the Enterprise Ethereum blockchain, and a list of administrative accounts that can alter that whitelist. In this model administrators are allowed to add and remove administrators, and add and remove nodes. Clients can check whether a given connection is permitted using the `connectionAllowed` function, which checks that the enode addresses of both nodes involved in the connection are in the whitelist. When a permission rule update occurs a `NodePermissionsUpdated` event is emitted indicating that a rule change has occurred, and whether the rule change makes the Enterprise Ethereum blockchain more permissive or adds new restrictions. When deploying the contract it is initialised with the account deploying it as the first administrator. This account can then add additional administrators and add nodes to the whitelist. This contract serves as an example only. As written, it has shortcomings. These need to be considered before using the code any production environment. Some potentially desirable features that are not included in this example are:
pragma solidity >=0.4.0 <0.6.0;
contract SimplePermissioning {
    // Struct representing an enode
    struct Enode {
        bytes32 enodeHigh;
        bytes32 enodeLow;
        bytes16 enodeHost;
        uint16 enodePort;

    // Event emitted when a rules change occurs
    event NodePermissionsUpdated(
        bool addsRestrictions,
        bool addsPermissions

    // List of nodes permitted to participate in the network
    mapping(bytes => Enode) private whitelist;

    // List of accounts allowed to modify the network
    mapping(address => bool) private adminList;

    constructor() public {
        // set the contract creator as the first admin
        adminList[msg.sender] = true;

    // Guard modifier for functions that can only be invokable by admins
    modifier onlyAdmin() {
        require(adminList[msg.sender] == true, "Cannot be called except by members of the admin list");

    // Add an admin to the contract
    function addAdmin(address newAdmin) public onlyAdmin {
        adminList[newAdmin] = true;

    // Remove an admin from the contract
    function removeAdmin(address oldAdmin) public onlyAdmin {
        adminList[oldAdmin] = false;

    // Check if a connection between two nodes will be permitted
    function connectionAllowed(
        bytes32 sourceEnodeHigh,
        bytes32 sourceEnodeLow,
        bytes16 sourceEnodeIp,
        uint16 sourceEnodePort,
        bytes32 destinationEnodeHigh,
        bytes32 destinationEnodeLow,
        bytes16 destinationEnodeIp,
        uint16 destinationEnodePort
    ) public view returns (bytes32) {
        // Check that both are in the whitelist
        if (
            enodeAllowed(sourceEnodeHigh, sourceEnodeLow, sourceEnodeIp, sourceEnodePort)
            && enodeAllowed(destinationEnodeHigh, destinationEnodeLow, destinationEnodeIp, destinationEnodePort)
        ) {
            // If both are then indicate permitted by returning the first bit as set
            return 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
        } else {
            // If one or neither are permitted then indicate not permitted by unsetting the first bit
            return 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    // Check if a specified enode is in the whitelist
    function enodeAllowed(
        bytes32 sourceEnodeHigh,
        bytes32 sourceEnodeLow,
        bytes16 sourceEnodeIp,
        uint16 sourceEnodePort
    ) public view returns (bool){
        bytes memory key = computeKey(sourceEnodeHigh, sourceEnodeLow, sourceEnodeIp, sourceEnodePort);
        Enode storage whitelistSource = whitelist[key];
        if (whitelistSource.enodeHost > 0) {
            return true;

    // Add a specified enode to the whitelist
    function addEnode(
        bytes32 enodeHigh,
        bytes32 enodeLow,
        bytes16 enodeIp,
        uint16 enodePort
    ) public onlyAdmin {
        Enode memory newEnode = Enode(enodeHigh, enodeLow, enodeIp, enodePort);
        bytes memory key = computeKey(enodeHigh, enodeLow, enodeIp, enodePort);
        whitelist[key] = newEnode;
        // Emit an event indicating a permissioning update occurred that only
        // allows new nodes into the network
        emit NodePermissionsUpdated(false, true);

    // Remove a specified enode from the whitelist
    function removeEnode(
        bytes32 enodeHigh,
        bytes32 enodeLow,
        bytes16 enodeIp,
        uint16 enodePort
    ) public onlyAdmin {
        bytes memory key = computeKey(enodeHigh, enodeLow, enodeIp, enodePort);
        Enode memory zeros = Enode(bytes32(0), bytes32(0), bytes16(0), 0);
        whitelist[key] = zeros;
        // Emit an event indicating a permissioning update occurred that can
        // cause existing connections to now be disallowed
        emit NodePermissionsUpdated(true, false);

    // Compute a consistent hashkey for a given enode
    function computeKey(
        bytes32 enodeHigh,
        bytes32 enodeLow,
        bytes16 enodeIp,
        uint16 enodePort
    ) public pure returns (bytes memory) {
        return abi.encode(enodeHigh, enodeLow, enodeIp, enodePort);