python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / networking / bitcoind.nix
blob6df809a8b7648917c1996c789586f20bb3736748
1 { config, pkgs, lib, ... }:
3 with lib;
5 let
7   eachBitcoind = config.services.bitcoind;
9   rpcUserOpts = { name, ... }: {
10     options = {
11       name = mkOption {
12         type = types.str;
13         example = "alice";
14         description = lib.mdDoc ''
15           Username for JSON-RPC connections.
16         '';
17       };
18       passwordHMAC = mkOption {
19         type = types.uniq (types.strMatching "[0-9a-f]+\\$[0-9a-f]{64}");
20         example = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
21         description = lib.mdDoc ''
22           Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the
23           format \<SALT-HEX\>$\<HMAC-HEX\>.
25           Tool (Python script) for HMAC generation is available here:
26           <https://github.com/bitcoin/bitcoin/blob/master/share/rpcauth/rpcauth.py>
27         '';
28       };
29     };
30     config = {
31       name = mkDefault name;
32     };
33   };
35   bitcoindOpts = { config, lib, name, ...}: {
36     options = {
38       enable = mkEnableOption (lib.mdDoc "Bitcoin daemon");
40       package = mkOption {
41         type = types.package;
42         default = pkgs.bitcoind;
43         defaultText = literalExpression "pkgs.bitcoind";
44         description = lib.mdDoc "The package providing bitcoin binaries.";
45       };
47       configFile = mkOption {
48         type = types.nullOr types.path;
49         default = null;
50         example = "/var/lib/${name}/bitcoin.conf";
51         description = lib.mdDoc "The configuration file path to supply bitcoind.";
52       };
54       extraConfig = mkOption {
55         type = types.lines;
56         default = "";
57         example = ''
58           par=16
59           rpcthreads=16
60           logips=1
61         '';
62         description = lib.mdDoc "Additional configurations to be appended to {file}`bitcoin.conf`.";
63       };
65       dataDir = mkOption {
66         type = types.path;
67         default = "/var/lib/bitcoind-${name}";
68         description = lib.mdDoc "The data directory for bitcoind.";
69       };
71       user = mkOption {
72         type = types.str;
73         default = "bitcoind-${name}";
74         description = lib.mdDoc "The user as which to run bitcoind.";
75       };
77       group = mkOption {
78         type = types.str;
79         default = config.user;
80         description = lib.mdDoc "The group as which to run bitcoind.";
81       };
83       rpc = {
84         port = mkOption {
85           type = types.nullOr types.port;
86           default = null;
87           description = lib.mdDoc "Override the default port on which to listen for JSON-RPC connections.";
88         };
89         users = mkOption {
90           default = {};
91           example = literalExpression ''
92             {
93               alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
94               bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
95             }
96           '';
97           type = types.attrsOf (types.submodule rpcUserOpts);
98           description = lib.mdDoc "RPC user information for JSON-RPC connnections.";
99         };
100       };
102       pidFile = mkOption {
103         type = types.path;
104         default = "${config.dataDir}/bitcoind.pid";
105         description = lib.mdDoc "Location of bitcoind pid file.";
106       };
108       testnet = mkOption {
109         type = types.bool;
110         default = false;
111         description = lib.mdDoc "Whether to use the testnet instead of mainnet.";
112       };
114       port = mkOption {
115         type = types.nullOr types.port;
116         default = null;
117         description = lib.mdDoc "Override the default port on which to listen for connections.";
118       };
120       dbCache = mkOption {
121         type = types.nullOr (types.ints.between 4 16384);
122         default = null;
123         example = 4000;
124         description = lib.mdDoc "Override the default database cache size in MiB.";
125       };
127       prune = mkOption {
128         type = types.nullOr (types.coercedTo
129           (types.enum [ "disable" "manual" ])
130           (x: if x == "disable" then 0 else 1)
131           types.ints.unsigned
132         );
133         default = null;
134         example = 10000;
135         description = lib.mdDoc ''
136           Reduce storage requirements by enabling pruning (deleting) of old
137           blocks. This allows the pruneblockchain RPC to be called to delete
138           specific blocks, and enables automatic pruning of old blocks if a
139           target size in MiB is provided. This mode is incompatible with -txindex
140           and -rescan. Warning: Reverting this setting requires re-downloading
141           the entire blockchain. ("disable" = disable pruning blocks, "manual"
142           = allow manual pruning via RPC, >=550 = automatically prune block files
143           to stay under the specified target size in MiB).
144         '';
145       };
147       extraCmdlineOptions = mkOption {
148         type = types.listOf types.str;
149         default = [];
150         description = lib.mdDoc ''
151           Extra command line options to pass to bitcoind.
152           Run bitcoind --help to list all available options.
153         '';
154       };
155     };
156   };
160   options = {
161     services.bitcoind = mkOption {
162       type = types.attrsOf (types.submodule bitcoindOpts);
163       default = {};
164       description = lib.mdDoc "Specification of one or more bitcoind instances.";
165     };
166   };
168   config = mkIf (eachBitcoind != {}) {
170     assertions = flatten (mapAttrsToList (bitcoindName: cfg: [
171     {
172       assertion = (cfg.prune != null) -> (builtins.elem cfg.prune [ "disable" "manual" 0 1 ] || (builtins.isInt cfg.prune && cfg.prune >= 550));
173       message = ''
174         If set, services.bitcoind.${bitcoindName}.prune has to be "disable", "manual", 0 , 1 or >= 550.
175       '';
176     }
177     {
178       assertion = (cfg.rpc.users != {}) -> (cfg.configFile == null);
179       message = ''
180         You cannot set both services.bitcoind.${bitcoindName}.rpc.users and services.bitcoind.${bitcoindName}.configFile
181         as they are exclusive. RPC user setting would have no effect if custom configFile would be used.
182       '';
183     }
184     ]) eachBitcoind);
186     environment.systemPackages = flatten (mapAttrsToList (bitcoindName: cfg: [
187       cfg.package
188     ]) eachBitcoind);
190     systemd.services = mapAttrs' (bitcoindName: cfg: (
191       nameValuePair "bitcoind-${bitcoindName}" (
192       let
193         configFile = pkgs.writeText "bitcoin.conf" ''
194           # If Testnet is enabled, we need to add [test] section
195           # otherwise, some options (e.g.: custom RPC port) will not work
196           ${optionalString cfg.testnet "[test]"}
197           # RPC users
198           ${concatMapStringsSep  "\n"
199             (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
200             (attrValues cfg.rpc.users)
201           }
202           # Extra config options (from bitcoind nixos service)
203           ${cfg.extraConfig}
204         '';
205       in {
206         description = "Bitcoin daemon";
207         after = [ "network-online.target" ];
208         wantedBy = [ "multi-user.target" ];
209         serviceConfig = {
210           User = cfg.user;
211           Group = cfg.group;
212           ExecStart = ''
213             ${cfg.package}/bin/bitcoind \
214             ${if (cfg.configFile != null) then
215               "-conf=${cfg.configFile}"
216             else
217               "-conf=${configFile}"
218             } \
219             -datadir=${cfg.dataDir} \
220             -pid=${cfg.pidFile} \
221             ${optionalString cfg.testnet "-testnet"}\
222             ${optionalString (cfg.port != null) "-port=${toString cfg.port}"}\
223             ${optionalString (cfg.prune != null) "-prune=${toString cfg.prune}"}\
224             ${optionalString (cfg.dbCache != null) "-dbcache=${toString cfg.dbCache}"}\
225             ${optionalString (cfg.rpc.port != null) "-rpcport=${toString cfg.rpc.port}"}\
226             ${toString cfg.extraCmdlineOptions}
227           '';
228           Restart = "on-failure";
230           # Hardening measures
231           PrivateTmp = "true";
232           ProtectSystem = "full";
233           NoNewPrivileges = "true";
234           PrivateDevices = "true";
235           MemoryDenyWriteExecute = "true";
236         };
237       }
238     ))) eachBitcoind;
240     systemd.tmpfiles.rules = flatten (mapAttrsToList (bitcoindName: cfg: [
241       "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
242     ]) eachBitcoind);
244     users.users = mapAttrs' (bitcoindName: cfg: (
245       nameValuePair "bitcoind-${bitcoindName}" {
246       name = cfg.user;
247       group = cfg.group;
248       description = "Bitcoin daemon user";
249       home = cfg.dataDir;
250       isSystemUser = true;
251     })) eachBitcoind;
253     users.groups = mapAttrs' (bitcoindName: cfg: (
254       nameValuePair "${cfg.group}" { }
255     )) eachBitcoind;
257   };
259   meta.maintainers = with maintainers; [ _1000101 ];