parent
4e6bf03504
commit
870a6e262d
@ -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} = {}; |
||||
}; |
||||
} |
@ -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' |
||||
) |
||||
''; |
||||
}) |
Loading…
Reference in new issue