nixos/quorum: init

This commit is contained in:
Marek Mahut 2020-03-22 17:26:55 +01:00
parent 4e6bf03504
commit 870a6e262d
4 changed files with 310 additions and 0 deletions

View File

@ -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

View File

@ -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} = {};
};
}

View File

@ -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 {};

79
nixos/tests/quorum.nix Normal file
View File

@ -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'
)
'';
})