summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarek Mahut <marek.mahut@gmail.com>2020-03-22 17:26:55 +0100
committerMarek Mahut <marek.mahut@gmail.com>2020-03-27 19:31:01 +0100
commit870a6e262d333328b951333cf45beb4c57ee5a03 (patch)
tree51b574a45d4461864e24d00e1392c6d77f4affb4
parent4e6bf03504c9e09f067cc6dee6b5aeec43a1405c (diff)
downloadnixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar.gz
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar.bz2
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar.lz
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar.xz
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.tar.zst
nixpkgs-870a6e262d333328b951333cf45beb4c57ee5a03.zip
nixos/quorum: init
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/networking/quorum.nix229
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/quorum.nix79
4 files changed, 310 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index ccdc39eecd8..c460e7164bf 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -692,6 +692,7 @@
   ./services/networking/prosody.nix
   ./services/networking/quagga.nix
   ./services/networking/quassel.nix
+  ./services/networking/quorum.nix
   ./services/networking/quicktun.nix
   ./services/networking/racoon.nix
   ./services/networking/radicale.nix
diff --git a/nixos/modules/services/networking/quorum.nix b/nixos/modules/services/networking/quorum.nix
new file mode 100644
index 00000000000..2f612c9db68
--- /dev/null
+++ b/nixos/modules/services/networking/quorum.nix
@@ -0,0 +1,229 @@
+{ config, pkgs, lib, ... }:
+let
+
+  inherit (lib) mkEnableOption mkIf mkOption literalExample types optionalString;
+
+  cfg = config.services.quorum;
+  dataDir = "/var/lib/quorum";
+  genesisFile = pkgs.writeText "genesis.json" (builtins.toJSON cfg.genesis);
+  staticNodesFile = pkgs.writeText "static-nodes.json" (builtins.toJSON cfg.staticNodes);
+
+in {
+  options = {
+
+    services.quorum = {
+      enable = mkEnableOption "Quorum blockchain daemon";
+
+      user = mkOption {
+        type = types.str;
+        default = "quorum";
+        description = "The user as which to run quorum.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = cfg.user;
+        description = "The group as which to run quorum.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 21000;
+        description = "Override the default port on which to listen for connections.";
+      };
+
+      nodekeyFile = mkOption {
+        type = types.path;
+        default = "${dataDir}/nodekey";
+        description = "Path to the nodekey.";
+      };
+
+      staticNodes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "enode://dd333ec28f0a8910c92eb4d336461eea1c20803eed9cf2c056557f986e720f8e693605bba2f4e8f289b1162e5ac7c80c914c7178130711e393ca76abc1d92f57@0.0.0.0:30303?discport=0" ];
+        description = "List of validator nodes.";
+      };
+
+      privateconfig = mkOption {
+        type = types.str;
+        default = "ignore";
+        description = "Configuration of privacy transaction manager.";
+      };
+
+      syncmode = mkOption {
+        type = types.enum [ "fast" "full" "light" ];
+        default = "full";
+        description = "Blockchain sync mode.";
+      };
+
+      blockperiod = mkOption {
+        type = types.int;
+        default = 5;
+        description = "Default minimum difference between two consecutive block's timestamps in seconds.";
+      };
+
+      permissioned = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Allow only a defined list of nodes to connect.";
+      };
+
+      rpc = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable RPC interface.";
+        };
+
+        address = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "Listening address for RPC connections.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 22004;
+          description = "Override the default port on which to listen for RPC connections.";
+        };
+
+        api = mkOption {
+          type = types.str;
+          default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
+          description = "API's offered over the HTTP-RPC interface.";
+        };
+      };
+
+     ws = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable WS-RPC interface.";
+        };
+
+        address = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "Listening address for WS-RPC connections.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8546;
+          description = "Override the default port on which to listen for WS-RPC connections.";
+        };
+
+        api = mkOption {
+          type = types.str;
+          default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
+          description = "API's offered over the WS-RPC interface.";
+        };
+
+       origins = mkOption {
+          type = types.str;
+          default = "*";
+          description = "Origins from which to accept websockets requests";
+       };
+     };
+
+      genesis = mkOption {
+        type = types.nullOr types.attrs;
+        default = null;
+        example = literalExample '' {
+          alloc = {
+            a47385db68718bdcbddc2d2bb7c54018066ec111 = {
+              balance = "1000000000000000000000000000";
+            };
+          };
+          coinbase = "0x0000000000000000000000000000000000000000";
+          config = {
+            byzantiumBlock = 4;
+            chainId = 494702925;
+            eip150Block = 2;
+            eip155Block = 3;
+            eip158Block = 3;
+            homesteadBlock = 1;
+            isQuorum = true;
+            istanbul = {
+              epoch = 30000;
+              policy = 0;
+            };
+          };
+          difficulty = "0x1";
+          extraData = "0x0000000000000000000000000000000000000000000000000000000000000000f85ad59438f0508111273d8e482f49410ca4078afc86a961b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0";
+          gasLimit = "0x2FEFD800";
+          mixHash = "0x63746963616c2062797a616e74696e65201111756c7420746f6c6572616e6365";
+          nonce = "0x0";
+          parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000";
+          timestamp = "0x00";
+          }'';
+        description = "Blockchain genesis settings.";
+      };
+     };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.quorum ];
+    systemd.tmpfiles.rules = [
+      "d '${dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+    ];
+    systemd.services.quorum = {
+      description = "Quorum daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = {
+        PRIVATE_CONFIG = "${cfg.privateconfig}";
+      };
+      preStart = ''
+        if [ ! -d ${dataDir}/geth ]; then
+          if [ ! -d ${dataDir}/keystore ]; then
+            echo ERROR: You need to create a wallet before initializing your genesis file, run:
+            echo   # su -s /bin/sh - quorum
+            echo   $ geth --datadir ${dataDir} account new
+            echo and configure your genesis file accordingly.
+            exit 1;
+          fi
+          ln -s ${staticNodesFile} ${dataDir}/static-nodes.json
+          ${pkgs.quorum}/bin/geth --datadir ${dataDir} init ${genesisFile}
+        fi
+      '';
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = ''${pkgs.quorum}/bin/geth \
+            --nodiscover \
+            --verbosity 5 \
+            --nodekey ${cfg.nodekeyFile} \
+            --istanbul.blockperiod ${toString cfg.blockperiod} \
+            --syncmode ${cfg.syncmode} \
+            ${optionalString (cfg.permissioned)
+            "--permissioned"} \
+            --mine --minerthreads 1 \
+            ${optionalString (cfg.rpc.enable)
+            "--rpc --rpcaddr ${cfg.rpc.address} --rpcport ${toString cfg.rpc.port} --rpcapi ${cfg.rpc.api}"} \
+            ${optionalString (cfg.ws.enable)
+            "--ws --wsaddr ${cfg.ws.address} --wsport ${toString cfg.ws.port} --wsapi ${cfg.ws.api} --wsorigins ${cfg.ws.origins}"} \
+            --emitcheckpoints \
+            --datadir ${dataDir} \
+            --port ${toString cfg.port}'';
+        Restart = "on-failure";
+
+        # Hardening measures
+        PrivateTmp = "true";
+        ProtectSystem = "full";
+        NoNewPrivileges = "true";
+        PrivateDevices = "true";
+        MemoryDenyWriteExecute = "true";
+      };
+    };
+    users.users.${cfg.user} = {
+      name = cfg.user;
+      group = cfg.group;
+      description = "Quorum daemon user";
+      home = dataDir;
+      isSystemUser = true;
+    };
+    users.groups.${cfg.group} = {};
+  };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 51b463747b0..63770e0460c 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -250,6 +250,7 @@ in
   prosodyMysql = handleTest ./xmpp/prosody-mysql.nix {};
   proxy = handleTest ./proxy.nix {};
   quagga = handleTest ./quagga.nix {};
+  quorum = handleTest ./quorum.nix {};
   rabbitmq = handleTest ./rabbitmq.nix {};
   radarr = handleTest ./radarr.nix {};
   radicale = handleTest ./radicale.nix {};
diff --git a/nixos/tests/quorum.nix b/nixos/tests/quorum.nix
new file mode 100644
index 00000000000..846d2a93018
--- /dev/null
+++ b/nixos/tests/quorum.nix
@@ -0,0 +1,79 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "quorum";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ mmahut ];
+  };
+
+  nodes = {
+    machine = { ... }: {
+      services.quorum = {
+        enable = true;
+        permissioned = false;
+        staticNodes = [ "enode://dd333ec28f0a8910c92eb4d336461eea1c20803eed9cf2c056557f986e720f8e693605bba2f4e8f289b1162e5ac7c80c914c7178130711e393ca76abc1d92f57@0.0.0.0:30303?discport=0" ];
+        genesis = {
+          alloc = {
+            "189d23d201b03ae1cf9113672df29a5d672aefa3" = {
+              balance = "0x446c3b15f9926687d2c40534fdb564000000000000";
+            };
+            "44b07d2c28b8ed8f02b45bd84ac7d9051b3349e6" = {
+              balance = "0x446c3b15f9926687d2c40534fdb564000000000000";
+            };
+            "4c1ccd426833b9782729a212c857f2f03b7b4c0d" = {
+              balance = "0x446c3b15f9926687d2c40534fdb564000000000000";
+            };
+            "7ae555d0f6faad7930434abdaac2274fd86ab516" = {
+              balance = "0x446c3b15f9926687d2c40534fdb564000000000000";
+            };
+            c1056df7c02b6f1a353052eaf0533cc7cb743b52 = {
+              balance = "0x446c3b15f9926687d2c40534fdb564000000000000";
+            };
+          };
+          coinbase = "0x0000000000000000000000000000000000000000";
+          config = {
+            byzantiumBlock = 1;
+            chainId = 10;
+            eip150Block = 1;
+            eip150Hash =
+              "0x0000000000000000000000000000000000000000000000000000000000000000";
+            eip155Block = 1;
+            eip158Block = 1;
+            isQuorum = true;
+            istanbul = {
+              epoch = 30000;
+              policy = 0;
+            };
+          };
+        difficulty = "0x1";
+        extraData =
+          "0x0000000000000000000000000000000000000000000000000000000000000000f8aff869944c1ccd426833b9782729a212c857f2f03b7b4c0d94189d23d201b03ae1cf9113672df29a5d672aefa39444b07d2c28b8ed8f02b45bd84ac7d9051b3349e694c1056df7c02b6f1a353052eaf0533cc7cb743b52947ae555d0f6faad7930434abdaac2274fd86ab516b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0";
+        gasLimit = "0xe0000000";
+        gasUsed = "0x0";
+        mixHash =
+          "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365";
+        nonce = "0x0";
+        number = "0x0";
+        parentHash =
+          "0x0000000000000000000000000000000000000000000000000000000000000000";
+        timestamp = "0x5cffc201";
+		  };
+     };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_until_succeeds("mkdir -p /var/lib/quorum/keystore")
+    machine.wait_until_succeeds(
+        'echo \{\\"address\\":\\"9377bc3936de934c497e22917b81aa8774ac3bb0\\",\\"crypto\\":\{\\"cipher\\":\\"aes-128-ctr\\",\\"ciphertext\\":\\"ad8341d8ef225650403fd366c955f41095e438dd966a3c84b3d406818c1e366c\\",\\"cipherparams\\":\{\\"iv\\":\\"2a09f7a72fd6dff7c43150ff437e6ac2\\"\},\\"kdf\\":\\"scrypt\\",\\"kdfparams\\":\{\\"dklen\\":32,\\"n\\":262144,\\"p\\":1,\\"r\\":8,\\"salt\\":\\"d1a153845bb80cd6274c87c5bac8ac09fdfac5ff131a6f41b5ed319667f12027\\"\},\\"mac\\":\\"a9621ad88fa1d042acca6fc2fcd711f7e05bfbadea3f30f379235570c8e270d3\\"\},\\"id\\":\\"89e847a3-1527-42f6-a321-77de0a14ce02\\",\\"version\\":3\}\\" > /var/lib/quorum/keystore/UTC--2020-03-23T11-08-34.144812212Z--9377bc3936de934c497e22917b81aa8774ac3bb0'
+    )
+    machine.wait_until_succeeds(
+        "echo fe2725c4e8f7617764b845e8d939a65c664e7956eb47ed7d934573f16488efc1 > /var/lib/quorum/nodekey"
+    )
+    machine.wait_until_succeeds("systemctl restart quorum")
+    machine.wait_for_unit("quorum.service")
+    machine.sleep(15)
+    machine.wait_until_succeeds(
+        'geth attach /var/lib/quorum/geth.ipc --exec "eth.accounts" | grep 0x9377bc3936de934c497e22917b81aa8774ac3bb0'
+    )
+  '';
+})