Merge DATUM ngit proposal with preserved catalog entries

Reviewed ngit proposal 15ff5fb9 (pr/datum), head febdda968e. Preserve all existing catalog entries and normalize the new manifest memory limit to supported 512m syntax.

Validation: three configuration preservation tests and strict release catalog drift pass. Live miner shares, payout configuration and reboot acceptance remain separate.
This commit is contained in:
archipelago
2026-10-08 18:47:56 -04:00
11 changed files with 326 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
FROM debian:bookworm-slim@sha256:7c7b2c966bc9ee8cedfeef67e0e279108992c77681fa595db4a9d65c06ccc587 AS build
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git build-essential cmake pkg-config libjansson-dev \
libmicrohttpd-dev libsodium-dev libcurl4-openssl-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
# DATUM v0.4.1beta. Verify the commit as well as the tag.
RUN git init && git remote add origin https://github.com/OCEAN-xyz/datum_gateway.git \
&& git fetch --depth 1 origin refs/tags/v0.4.1beta \
&& git checkout --detach FETCH_HEAD \
&& test "$(git rev-parse HEAD)" = 5b061233a3d3323771b2be98e17f543e59346619 \
&& cmake -DCMAKE_BUILD_TYPE=Release . && make -j2
FROM debian:bookworm-slim@sha256:7c7b2c966bc9ee8cedfeef67e0e279108992c77681fa595db4a9d65c06ccc587
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates libjansson4 libmicrohttpd12 libsodium23 libcurl4 jq curl \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --uid 1000 --create-home datum
WORKDIR /app
COPY --from=build /src/datum_gateway /app/datum_gateway
COPY --from=build /src/www /app/www
COPY entrypoint.sh /app/entrypoint.sh
COPY configure.jq /app/configure.jq
USER 1000:1000
EXPOSE 7152 23334
ENTRYPOINT ["sh", "/app/entrypoint.sh"]
+19
View File
@@ -0,0 +1,19 @@
if type != "object" then error("Datum config must be an object") else . end
| .bitcoind.rpcurl = ("http://" + env.BITCOIN_RPC_HOST + ":8332")
| .bitcoind.rpcuser = "archipelago"
| .bitcoind.rpcpassword = env.BITCOIN_RPC_PASSWORD
# Use upstream's getbestblockhash fallback; no host bitcoind hooks needed.
| .bitcoind.notify_fallback = true
| .stratum.listen_addr = "0.0.0.0"
| .stratum.listen_port = 23334
| .mining.pool_address //= ""
| .mining.coinbase_tag_primary //= "DATUM Gateway"
| .mining.coinbase_tag_secondary //= "Archipelago"
| .api.listen_port = 7152
| .api.admin_password = env.DATUM_ADMIN_PASSWORD
| .api.modify_conf = true
| .logger.log_to_console = true
| .logger.log_to_file = false
| if .datum.pool_pass_workers == null then .datum.pool_pass_workers = true else . end
| if .datum.pool_pass_full_users == null then .datum.pool_pass_full_users = true else . end
| if .datum.pooled_mining_only == null then .datum.pooled_mining_only = true else . end
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
set -eu
umask 077
: "${BITCOIN_RPC_HOST:?Bitcoin host is required}"
: "${BITCOIN_RPC_PASSWORD:?Bitcoin RPC password is required}"
: "${DATUM_ADMIN_PASSWORD:?Datum admin password is required}"
# Keep operator settings, including the payout address, across recreation.
# Refresh platform-owned credentials and DNS names on every container start.
config=/data/config.json
if [ ! -e "$config" ]; then printf '{}\n' > "$config"; fi
tmp=$(mktemp /data/config.json.XXXXXX)
trap 'rm -f "$tmp"' EXIT HUP INT TERM
jq -e -f /app/configure.jq "$config" > "$tmp"
mv "$tmp" "$config"
exec /app/datum_gateway --config "$config"
+47
View File
@@ -0,0 +1,47 @@
"""Config upgrades must preserve payout policy and reject broken input."""
import json
import os
from pathlib import Path
import subprocess
import unittest
FILTER = Path(__file__).resolve().parents[1] / 'configure.jq'
def configure(value, host='bitcoin-core', password='new-rpc'):
return subprocess.run(['jq', '-e', '-f', str(FILTER)], input=json.dumps(value),
text=True, capture_output=True,
env={**os.environ, 'BITCOIN_RPC_HOST': host,
'BITCOIN_RPC_PASSWORD': password,
'DATUM_ADMIN_PASSWORD': 'test-admin'})
class ConfigTests(unittest.TestCase):
def test_first_run_has_no_borrowed_payout_address(self):
result = configure({})
self.assertEqual(result.returncode, 0, result.stderr)
data = json.loads(result.stdout)
self.assertEqual(data['mining']['pool_address'], '')
self.assertTrue(data['datum']['pooled_mining_only'])
self.assertTrue(data['api']['modify_conf'])
def test_restart_preserves_payout_and_explicit_false_settings(self):
original = {'mining': {'pool_address': 'operator-address'},
'datum': {'pool_pass_workers': False, 'pool_pass_full_users': False,
'pooled_mining_only': False},
'bitcoind': {'rpcurl': 'http://old-ip:8332', 'rpcpassword': 'old'}}
result = configure(original, password='quotes"and\\slashes')
self.assertEqual(result.returncode, 0, result.stderr)
data = json.loads(result.stdout)
self.assertEqual(data['mining']['pool_address'], 'operator-address')
self.assertEqual(data['datum'], original['datum'])
self.assertEqual(data['bitcoind']['rpcurl'], 'http://bitcoin-core:8332')
self.assertEqual(data['bitcoind']['rpcpassword'], 'quotes"and\\slashes')
def test_invalid_root_is_rejected(self):
for value in [None, [], 'broken', 1]:
self.assertNotEqual(configure(value).returncode, 0)
if __name__ == '__main__':
unittest.main()