From d37f1f8095e1ed65ff4c981d2b66d0ff0e326dee Mon Sep 17 00:00:00 2001 From: 1000101 Date: Tue, 23 Jun 2020 13:04:57 +0200 Subject: [PATCH 1/3] blockbook: add static files necessary for module --- pkgs/servers/blockbook/default.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkgs/servers/blockbook/default.nix b/pkgs/servers/blockbook/default.nix index 4afabb858c3..3dad2f1c6ce 100644 --- a/pkgs/servers/blockbook/default.nix +++ b/pkgs/servers/blockbook/default.nix @@ -61,6 +61,12 @@ buildGoModule rec { subPackages = [ "." ]; + postInstall = '' + mkdir -p $out/share/ + cp -r $src/static/templates/ $out/share/ + cp -r $src/static/css/ $out/share/ + ''; + meta = with lib; { description = "Trezor address/account balance backend"; homepage = "https://github.com/trezor/blockbook"; From de3c56ffd83f88d988923984d579d23e386c00f9 Mon Sep 17 00:00:00 2001 From: 1000101 Date: Tue, 23 Jun 2020 13:08:46 +0200 Subject: [PATCH 2/3] nixos/blockbook-frontend: init --- nixos/modules/module-list.nix | 1 + .../networking/blockbook-frontend.nix | 272 ++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 nixos/modules/services/networking/blockbook-frontend.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 0dba92f60c7..55d0c836d47 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -589,6 +589,7 @@ ./services/networking/autossh.nix ./services/networking/bird.nix ./services/networking/bitlbee.nix + ./services/networking/blockbook-frontend.nix ./services/networking/charybdis.nix ./services/networking/cjdns.nix ./services/networking/cntlm.nix diff --git a/nixos/modules/services/networking/blockbook-frontend.nix b/nixos/modules/services/networking/blockbook-frontend.nix new file mode 100644 index 00000000000..61938e51e06 --- /dev/null +++ b/nixos/modules/services/networking/blockbook-frontend.nix @@ -0,0 +1,272 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + eachBlockbook = config.services.blockbook-frontend; + + blockbookOpts = { config, lib, name, ...}: { + + options = { + + enable = mkEnableOption "blockbook-frontend application."; + + package = mkOption { + type = types.package; + default = pkgs.blockbook; + description = "Which blockbook package to use."; + }; + + user = mkOption { + type = types.str; + default = "blockbook-frontend-${name}"; + description = "The user as which to run blockbook-frontend-${name}."; + }; + + group = mkOption { + type = types.str; + default = "${config.user}"; + description = "The group as which to run blockbook-frontend-${name}."; + }; + + certFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/secrets/blockbook-frontend-${name}/certFile"; + description = '' + To enable SSL, specify path to the name of certificate files without extension. + Expecting certFile.crt and certFile.key. + ''; + }; + + configFile = mkOption { + type = with types; nullOr path; + default = null; + example = "${config.dataDir}/config.json"; + description = "Location of the blockbook configuration file."; + }; + + coinName = mkOption { + type = types.str; + default = "Bitcoin"; + example = "Bitcoin"; + description = '' + See + for current of coins supported in master (Note: may differ from release). + ''; + }; + + cssDir = mkOption { + type = types.path; + default = "${config.package}/share/css/"; + example = "${config.dataDir}/static/css/"; + description = '' + Location of the dir with main.css CSS file. + By default, the one shipped with the package is used. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/blockbook-frontend-${name}"; + description = "Location of blockbook-frontend-${name} data directory."; + }; + + debug = mkOption { + type = types.bool; + default = false; + description = "Debug mode, return more verbose errors, reload templates on each request."; + }; + + internal = mkOption { + type = types.nullOr types.str; + default = ":9030"; + example = ":9030"; + description = "Internal http server binding [address]:port."; + }; + + messageQueueBinding = mkOption { + type = types.str; + default = "tcp://127.0.0.1:38330"; + example = "tcp://127.0.0.1:38330"; + description = "Message Queue Binding address:port."; + }; + + public = mkOption { + type = types.nullOr types.str; + default = ":9130"; + example = ":9130"; + description = "Public http server binding [address]:port."; + }; + + rpc = { + url = mkOption { + type = types.str; + default = "http://127.0.0.1"; + description = "URL for JSON-RPC connections."; + }; + + port = mkOption { + type = types.port; + default = 8030; + description = "Port for JSON-RPC connections."; + }; + + user = mkOption { + type = types.str; + default = "rpc"; + example = "rpc"; + description = "Username for JSON-RPC connections."; + }; + + password = mkOption { + type = types.str; + default = "rpc"; + example = "rpc"; + description = '' + RPC password for JSON-RPC connections. + Warning: this is stored in cleartext in the Nix store!!! + Use configFile or passwordFile if needed. + ''; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing password of the RPC user. + Note: This options is ignored when configFile is used. + ''; + }; + }; + + sync = mkOption { + type = types.bool; + default = true; + description = "Synchronizes until tip, if together with zeromq, keeps index synchronized."; + }; + + templateDir = mkOption { + type = types.path; + default = "${config.package}/share/templates/"; + example = "${config.dataDir}/templates/static/"; + description = "Location of the HTML templates. By default, ones shipped with the package are used."; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + example = literalExample '' { + alternative_estimate_fee = "whatthefee-disabled"; + alternative_estimate_fee_params = "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}"; + fiat_rates = "coingecko"; + fiat_rates_params = "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}"; + coin_shortcut = "BTC"; + coin_label = "Bitcoin"; + xpub_magic = 76067358; + xpub_magic_segwit_p2sh = 77429938; + xpub_magic_segwit_native = 78792518; + }''; + description = '' + Additional configurations to be appended to coin.conf. + Overrides any already defined configuration options. + See + for current configuration options supported in master (Note: may differ from release). + ''; + }; + + extraCmdLineOptions = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-workers=1" "-dbcache=0" "-logtosderr" ]; + description = '' + Extra command line options to pass to Blockbook. + Run blockbook --help to list all available options. + ''; + }; + }; + }; +in +{ + # interface + + options = { + services.blockbook-frontend = mkOption { + type = types.attrsOf (types.submodule blockbookOpts); + default = {}; + description = "Specification of one or more blockbook-frontend instances."; + }; + }; + + # implementation + + config = mkIf (eachBlockbook != {}) { + + systemd.services = mapAttrs' (blockbookName: cfg: ( + nameValuePair "blockbook-frontend-${blockbookName}" ( + let + configFile = if cfg.configFile != null then cfg.configFile else + pkgs.writeText "config.conf" (builtins.toJSON ( { + coin_name = "${cfg.coinName}"; + rpc_user = "${cfg.rpc.user}"; + rpc_pass = "${cfg.rpc.password}"; + rpc_url = "${cfg.rpc.url}:${toString cfg.rpc.port}"; + message_queue_binding = "${cfg.messageQueueBinding}"; + } // cfg.extraConfig) + ); + in { + description = "blockbook-frontend-${blockbookName} daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + ln -sf ${cfg.templateDir} ${cfg.dataDir}/static/ + ln -sf ${cfg.cssDir} ${cfg.dataDir}/static/ + ${optionalString (cfg.rpc.passwordFile != null && cfg.configFile == null) '' + CONFIGTMP=$(mktemp) + ${pkgs.jq}/bin/jq ".rpc_pass = \"$(cat ${cfg.rpc.passwordFile})\"" ${configFile} > $CONFIGTMP + mv $CONFIGTMP ${cfg.dataDir}/${blockbookName}-config.json + ''} + ''; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = '' + ${cfg.package}/bin/blockbook \ + ${if (cfg.rpc.passwordFile != null && cfg.configFile == null) then + "-blockchaincfg=${cfg.dataDir}/${blockbookName}-config.json" + else + "-blockchaincfg=${configFile}" + } \ + -datadir=${cfg.dataDir} \ + ${optionalString (cfg.sync != false) "-sync"} \ + ${optionalString (cfg.certFile != null) "-certfile=${toString cfg.certFile}"} \ + ${optionalString (cfg.debug != false) "-debug"} \ + ${optionalString (cfg.internal != null) "-internal=${toString cfg.internal}"} \ + ${optionalString (cfg.public != null) "-public=${toString cfg.public}"} \ + ${toString cfg.extraCmdLineOptions} + ''; + Restart = "on-failure"; + WorkingDirectory = cfg.dataDir; + LimitNOFILE = 65536; + }; + } + ) )) eachBlockbook; + + systemd.tmpfiles.rules = flatten (mapAttrsToList (blockbookName: cfg: [ + "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -" + "d ${cfg.dataDir}/static 0750 ${cfg.user} ${cfg.group} - -" + ]) eachBlockbook); + + users.users = mapAttrs' (blockbookName: cfg: ( + nameValuePair "blockbook-frontend-${blockbookName}" { + name = cfg.user; + group = cfg.group; + home = cfg.dataDir; + isSystemUser = true; + })) eachBlockbook; + + users.groups = mapAttrs' (instanceName: cfg: ( + nameValuePair "${cfg.group}" { })) eachBlockbook; + }; +} From c6d346b323043cdf84517a4723e358c2fc55011b Mon Sep 17 00:00:00 2001 From: 1000101 Date: Tue, 23 Jun 2020 13:09:03 +0200 Subject: [PATCH 3/3] nixos/blockbook-frontend: add tests --- nixos/tests/all-tests.nix | 1 + nixos/tests/blockbook-frontend.nix | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 nixos/tests/blockbook-frontend.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 7056d414e9e..a210a68bfef 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -33,6 +33,7 @@ in bees = handleTest ./bees.nix {}; bind = handleTest ./bind.nix {}; bittorrent = handleTest ./bittorrent.nix {}; + blockbook-frontend = handleTest ./blockbook-frontend.nix {}; buildkite-agents = handleTest ./buildkite-agents.nix {}; boot = handleTestOn ["x86_64-linux"] ./boot.nix {}; # syslinux is unsupported on aarch64 boot-stage1 = handleTest ./boot-stage1.nix {}; diff --git a/nixos/tests/blockbook-frontend.nix b/nixos/tests/blockbook-frontend.nix new file mode 100644 index 00000000000..67d0fcab947 --- /dev/null +++ b/nixos/tests/blockbook-frontend.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "blockbook-frontend"; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ maintainers."1000101" ]; + }; + + machine = { ... }: { + services.blockbook-frontend."test" = { + enable = true; + }; + services.bitcoind = { + enable = true; + rpc = { + port = 8030; + users.rpc.passwordHMAC = "acc2374e5f9ba9e62a5204d3686616cf$53abdba5e67a9005be6a27ca03a93ce09e58854bc2b871523a0d239a72968033"; + }; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("blockbook-frontend-test.service") + + machine.wait_for_open_port(9030) + + machine.succeed("curl -sSfL http://localhost:9030 | grep 'Blockbook'") + ''; +})