-
Notifications
You must be signed in to change notification settings - Fork 18
/
jormungandr.nix
451 lines (405 loc) · 14.1 KB
/
jormungandr.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.jormungandr;
commonLib = import ../lib.nix;
environments = (commonLib).environments;
peerType = types.submodule {
options = {
address = mkOption {
type = types.str;
description = ''
IP address in the format of:
/ip4/127.0.0.1/tcp/8080 or /ip6/::1/tcp/8080
'';
};
};
};
in {
options = {
services.jormungandr = {
enable = mkEnableOption "jormungandr";
environment = mkOption {
type = types.str;
default = "itn_rewards_v1";
description = ''
Environment in jormungandrLib to pull configuration from.
'';
};
enableExplorer = mkEnableOption "explorer";
enableRewardsLog = mkEnableOption "rewards log";
enableRewardsReportAll = mkEnableOption "full rewards api";
package = mkOption {
type = types.package;
default = environments.${cfg.environment}.packages.jormungandr;
defaultText = "jormungandr";
description = ''
The jormungandr package that should be used.
'';
};
jcliPackage = mkOption {
type = types.package;
default = environments.${cfg.environment}.packages.jcli;
defaultText = "jormungandr-cli";
description = ''
The jormungandr-cli package that should be used.
'';
};
withBackTraces = mkOption {
type = types.bool;
default = false;
description = ''
Run with RUST_BACKTRACE=1.
'';
};
withValgrind = mkOption {
type = types.bool;
default = false;
description = ''
Run with valgrind.
'';
};
stateDir = mkOption {
type = types.str;
default = "jormungandr";
description = ''
Directory below /var/lib to store blockchain data.
This directory will be created automatically using systemd's StateDirectory mechanism.
'';
};
genesisBlockHash = mkOption {
type = types.nullOr types.str;
default = if (cfg.block0 != null) then
null
else
environments.${cfg.environment}.genesisHash;
description = ''
Genesis Block Hash
'';
};
block0 = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to the genesis block (the block0) of the blockchain.
'';
};
secrets-paths = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "/var/lib/keys/faucet-key.yaml" ];
description = ''
Path to secret yaml.
'';
};
skipBootstrap = mkOption {
type = types.bool;
default = false;
description = ''
Allow to run as a self-node
'';
};
bootstrapFromTrustedPeers = mkOption {
type = types.bool;
default = false;
description = ''
Pull initial blocks directly from trusted peers instead of fetching a
list of known peers and bootstrapping from them.
'';
};
httpFetchBlock0Service = mkOption {
type = types.nullOr types.str;
default = null;
example =
"https://github.com/input-output-hk/jormungandr-block0/raw/master/data/";
description = ''
Bootstrap the larger than normal block0 from a HTTP url
'';
};
topicsOfInterest.messages = mkOption {
type = types.str;
default = "low";
description = ''
notify other peers this node is interested about Transactions
typical setting for a non mining node: "low".
For a stakepool: "high".
'';
};
topicsOfInterest.blocks = mkOption {
type = types.str;
default = "normal";
description = ''
notify other peers this node is interested about new Blocs.
typical settings for a non mining node: "normal".
For a stakepool: "high".
'';
};
trustedPeers = mkOption {
type = types.listOf peerType;
default = environments.${cfg.environment}.trustedPeers;
description = ''
the list of nodes to connect to in order to bootstrap the p2p topology
(and bootstrap our local blockchain).
'';
};
layers.preferredList.peers = mkOption {
type = types.listOf peerType;
default = environments.${cfg.environment}.trustedPeers;
description = ''
this is a special list that allows to connect multiple nodes together
without relying on the auto peer discovery. All entries in the
preferred list are also whitelisted automatically, so they cannot be
quarantined.
'';
};
layers.preferredList.viewMax = mkOption {
type = types.int;
default = 20;
description = ''
this is the number of entries to show in the view each round the
layer will randomly select up to layers.preferredList.viewMax entries
from the whole layers.preferredList.peers list of entries.
'';
};
policy.quarantineDuration = mkOption {
type = types.str;
default = "30m";
description = ''
Time a node is quarantined before being allowed to reconnect
'';
};
policy.quarantineWhitelist = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "/ip4/127.0.0.1/tcp/3000" ];
description = ''
set a trusted list of peers that will not be quarantined in any
circumstance. It should be a list of valid addresses, for example:
["/ip4/127.0.0.1/tcp/3000"]
'';
};
maxUnreachableNodes = mkOption {
type = types.int;
default = 20;
description = ''
Number of nodes that aren't public we will allow our node to connect
'';
};
gossipInterval = mkOption {
type = types.nullOr types.str;
default = null;
example = "10s";
description = "Gossip interval";
};
maxBootstrapAttempts = mkOption {
type = types.nullOr types.int;
default = null;
example = 20;
description = ''
The number of times to retry bootstrapping from trusted peers.
If not set, default beavior, the bootstrap process will keep retrying indefinitely,
until completed successfully. If set to 0 (zero), the node will skip bootstrap all
together -- even if trusted peers are defined. If the node fails to bootstrap from
any of the trusted peers and the number of bootstrap retry attempts is exceeded,
then the node will continue to run without completing the bootstrap process. This
will allow the node to act as the first node in the p2p network (i.e. genesis node),
or immediately begin gossip with the trusted peers if any are defined.
'';
};
topologyForceResetInterval = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Force reset topology at an interval
WARNING: do not change unless you know what you're doing
'';
};
publicId = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Needed to make a node publicly reachable.
Generate with `openssl rand -hex 24`.
'';
};
publicAddress = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
the address to listen from and accept connection from.
This is the public address that will beenvironments.${cfg.environment} distributed to other peers of the network
that may find interest into participating to the blockchain dissemination with the node.
'';
};
listenAddress = mkOption {
type = types.nullOr types.str;
default = null;
example = "/ip4/0.0.0.0/tcp/8606";
description = ''
Local socket address to listen to, if different from public address.
The IP address can be given as 0.0.0.0 or :: to bind to all
network interfaces.
'';
};
maxConnections = mkOption {
type = types.nullOr types.int;
default = null;
example = 500;
description = ''
Max connections allowed
'';
};
allowPrivateAddresses = mkOption {
type = types.nullOr types.bool;
default = true;
};
rest.listenAddress = mkOption {
type = types.nullOr types.str;
default = "127.0.0.1:8607";
description = ''
Address to listen on for rest endpoint.
'';
};
rest.cors.allowedOrigins = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "yourhostname.com" ];
description = ''
CORS allowed origins
'';
};
logger.level = mkOption {
type =
types.enum [ "off" "critical" "error" "warn" "info" "debug" "trace" ];
default = "info";
example = "debug";
description = ''
Logger level: "off", "critical", "error", "warn", "info", "debug", "trace"
'';
};
logger.format = mkOption {
type = types.str;
default = "plain";
example = "json";
description = ''
log output format - plain or json.
'';
};
logger.output = mkOption {
type = types.enum [ "stderr" "syslog" "journald" "gelf" ];
default = "stderr";
example = "syslog";
description = ''
log output - stderr, syslog (unix only) or journald (linux with systemd only, must be enabled during compilation).
'';
};
logger.backend = mkOption {
type = types.str;
example = "monitoring.stakepool.cardano-testnet.iohkdev.io:12201";
description = ''
The graylog server to use as GELF backend.
'';
};
logger.logs-id = mkOption {
type = types.str;
description = ''
Used by gelf output as log source.
'';
};
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.jcliPackage ];
users.groups.jormungandr.gid = 10015;
users.users.jormungandr = {
description = "Jormungandr node daemon user";
uid = 10015;
group = "jormungandr";
};
systemd.services.jormungandr = {
description = "Jormungandr node service";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
script = let
mapPeers = map (peer: {
address = peer.address;
});
configJson = builtins.toFile "config.yaml" (builtins.toJSON ({
storage = "/var/lib/" + cfg.stateDir;
log = [{
level = cfg.logger.level;
format = cfg.logger.format;
output = (if (cfg.logger.output == "gelf") then {
gelf = {
backend = cfg.logger.backend;
log_id = cfg.logger.logs-id;
};
} else
cfg.logger.output);
}];
rest = {
listen = cfg.rest.listenAddress;
} // optionalAttrs (cfg.rest.cors.allowedOrigins != [ ]) {
cors.allowed_origins = cfg.rest.cors.allowedOrigins;
};
p2p = filterAttrs (key: value: value != null) {
public_address = cfg.publicAddress;
public_id = cfg.publicId;
trusted_peers = mapPeers cfg.trustedPeers;
layers = {
preferred_list = {
view_max = cfg.layers.preferredList.viewMax;
peers = mapPeers cfg.layers.preferredList.peers;
};
};
allow_private_addresses = cfg.allowPrivateAddresses;
topics_of_interest = cfg.topicsOfInterest;
listen_address = cfg.listenAddress;
max_connections = cfg.maxConnections;
policy = {
quarantine_duration = cfg.policy.quarantineDuration;
quarantine_whitelist = cfg.policy.quarantineWhitelist;
};
max_unreachable_nodes_to_connect_per_event =
cfg.maxUnreachableNodes;
} // optionalAttrs (cfg.topologyForceResetInterval != null) {
topology_force_reset_interval = cfg.topologyForceResetInterval;
} // optionalAttrs (cfg.gossipInterval != null) {
gossip_interval = cfg.gossipInterval;
} // optionalAttrs (cfg.maxBootstrapAttempts != null) {
max_bootstrap_attempts = cfg.maxBootstrapAttempts;
};
bootstrap_from_trusted_peers = cfg.bootstrapFromTrustedPeers;
skip_bootstrap = cfg.skipBootstrap;
} // optionalAttrs cfg.enableExplorer { explorer.enabled = true; }
// optionalAttrs (cfg.httpFetchBlock0Service != null) {
http_fetch_block0_service = cfg.httpFetchBlock0Service;
}));
secretsArgs =
concatMapStrings (p: " --secret \"${p}\"") cfg.secrets-paths;
in ''
${optionalString cfg.enableRewardsLog ''
mkdir -p /var/lib/${cfg.stateDir}/rewards
export JORMUNGANDR_REWARD_DUMP_DIRECTORY=/var/lib/${cfg.stateDir}/rewards''}
${optionalString cfg.withBackTraces "RUST_BACKTRACE=full"} exec ${
optionalString cfg.withValgrind "${pkgs.valgrind}/bin/valgrind"
} ${cfg.package}/bin/jormungandr \
${optionalString (cfg.block0 != null) "--genesis-block ${cfg.block0}"} \
${optionalString cfg.enableRewardsReportAll "--rewards-report-all"} \
${
optionalString (cfg.genesisBlockHash != null)
"--genesis-block-hash ${cfg.genesisBlockHash}"
} \
--config ${configJson}${secretsArgs}
'';
serviceConfig = {
User = "jormungandr";
Group = "jormungandr";
Restart = "always";
WorkingDirectory = "/var/lib/" + cfg.stateDir;
StateDirectory = cfg.stateDir;
LimitNOFILE = "16384";
};
};
};
}