Describe new v2 format master
authorMatt Corallo <git@bluematt.me>
Sat, 25 Nov 2023 19:02:47 +0000 (19:02 +0000)
committerMatt Corallo <git@bluematt.me>
Sat, 25 Nov 2023 19:02:47 +0000 (19:02 +0000)
headersdns.sh
index.html
rest.html [new file with mode: 0644]
split.c
testnet_rest.html [new file with mode: 0644]

index 4d8cabd00f865f4442d364875d85c6160f0748f6..bc5a995443fca7ed5a417d37415b289f8df06637 100644 (file)
 # It is important that your secondary nameserver(s) support NOTIFYs to ensure you can
 # update them as new blocks come in.
 
+set -e
+
 SCP_TARGET="user@hostname"
 BITCOIN_CLI="~/bitcoin-cli"
 BITCOIND_REST="http://127.0.0.1/rest"
 
 mkdir -p header_zones
 cc -Wall -O2 ./split.c -o split
+cd header_zones
 
 while [ true ]; do
        if [ "$LATEST_HASH" != "$($BITCOIN_CLI getbestblockhash)" ]; then
@@ -50,10 +53,10 @@ while [ true ]; do
 
                        # split returns non-0 on error or if the zone on disk ends with the same header
                        # as what we just provided, so only scp it to our nameserver if we get 0
-                       if echo "$HEADERS" | ./split $I $COUNT 80 headers-$(($I / 10000)).zone; then
+                       if echo "$HEADERS" | ../split $I $COUNT 80 headers-$(($I / 10000)).zone; then
                                echo -e "put headers-$(($I / 10000)).zone dest/\nquit\n" | sftp $SCP_TARGET
                        fi
-                       if echo "$FILTERS" | ./split $I $COUNT 32 filterheaders-$(($I / 10000)).zone; then
+                       if echo "$FILTERS" | ../split $I $COUNT 32 filterheaders-$(($I / 10000)).zone; then
                                echo -e "put filterheaders-$(($I / 10000)).zone dest/\nquit\n" | sftp $SCP_TARGET
                        fi
                done
index b12d13de444cfaf4396896e898af36d8a7b2b5a9..9e23822e1a47f18d2236ada380536d475bff6750 100644 (file)
                        bitcoinheaders.net currently supports fetching of either full Bitcoin headers or neutrino filter headers, both with a similar format. To ensure reliable access in the face of strict DNS filters, the data is encoded in IPv6 addresses in AAAA records and DNSSec signed to prevent some basic attacks.
                </p>
                <p>
-                       Headers names are broken into sub-zones of 10,000 records, so queries should take the form of height.(height / 10000).bitcoinheaders.net (or, for basic neutrino filter headers, height.(height / 10000).basic.filter.bitcoinheaders.net).
+                       Headers names are broken into sub-zones of 10,000 records, so queries should take the form of v2.height.(height / 10000).bitcoinheaders.net (or, for basic neutrino filter headers, v2.height.(height / 10000).basic.filter.bitcoinheaders.net).
                </p>
                <p>
-                       All headers and filter headers are encoded with an arbitrary two byte prefix (currently "2001:"), followed by a 0-indexed order nibble (as nameservers often reorder responses). Bitcoin headers are then prefixed by a single version byte (currently version 0 - two 0 nibbles) and placed into the remaining 80 bytes of six IPv6 addresses. Filter headers are simply placed into three IPv6 addresses with a 17-nibble (8.5 byte) 0 suffix.
+                       All headers and filter headers are encoded with an arbitrary one byte prefix (which you must ignore, as it may change in the future), followed by a 0-indexed order byte (as nameservers often reorder responses). Entries are then prefixed by a single version byte (currently version 1) and placed into the remaining bytes of the IPv6 addresses.
                </p>
                <p>
                        The genesis block header is, thus, encoded as (note that the responses are usually not sorted):
                </p>
                <pre>
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:0:1000::
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:1000::
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:2000::3ba:3edf
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:3d7a:7b12:b27a:c72c:3e67:768f:617f
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:4c81:bc38:88a5:1323:a9fb:8aa4:b1e5
-0.0.bitcoinheaders.net.        604800  IN      AAAA    2001:5e4a:29ab:5f49:ffff:1d:1dac:2b7c</pre>
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2603:7b12:b27a:c72c:3e67:768f:617f:c81b
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2600:101::
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2601::
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2602::3b:a3ed:fd7a
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2605:ab5f:49ff:ff00:1d1d:ac2b:7c00:0
+v2.0.0.bitcoinheaders.net. 604800 IN   AAAA    2604:c388:8a51:323a:9fb8:aa4b:1e5e:4a29</pre>
                <p>which decodes to</p>
                <p class="hex">
                        0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c
                </p>
-               <p>and the first filter header encoded as:</p>
+               <p>and the 10,000th filter header encoded as:</p>
                <pre>
-0.0.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2001:9f3:c30f:c37:fb97:7cf3:e1a3:173c
-0.0.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2001:2802:139c:2020::
-0.0.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2001:1631:e8ff:119a:d308:8b6f:5b2b:ced0</pre>
+v2.10000.1.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2601:2dee:3a70:76df:a4aa:5ee9:5285:5cdf
+v2.10000.1.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2600:165:e915:4fba:5588:31fa:473:8326
+v2.10000.1.basic.filter.bitcoinheaders.net. 604800 IN AAAA 2602:25cf:4603:7300::</pre>
                <p>which decodes to</p>
                <p class="hex">
-                       9f3c30f0c37fb977cf3e1a3173c631e8ff119ad3088b6f5b2bced0802139c202
+                       65e9154fba558831fa047383262dee3a7076dfa4aa5ee952855cdf25cf460373
                </p>
                <p>
-               You can find the <a href="https://git.bitcoin.ninja?p=headersdns">scripts used to generate the zones as well as this website via git.</a>
+                       You can find the <a href="https://git.bitcoin.ninja?p=headersdns">scripts used to generate the zones as well as this website via git.</a>
                </p>
                <p>
-               If you want a full block source over a protocol other than Bitcoin-P2P, a anycast-cached Bitcoin Core REST interface endpoint is available at <a href="https://bitcoin-rest.bitcoin.ninja/rest/">https://bitcoin-rest.bitcoin.ninja/rest/</a> for mainnet and <a href="https://testnet.bitcoin-rest.bitcoin.ninja/rest/">https://testnet.bitcoin-rest.bitcoin.ninja/rest/</a> for testnet3. It should go without saying that this should only be used as a backup chain source, never as the primary chain source.
+                       If you want a full block source over a protocol other than Bitcoin-P2P, a anycast-cached Bitcoin Core REST interface endpoint is available at <a href="https://bitcoin-rest.bitcoin.ninja/">https://bitcoin-rest.bitcoin.ninja/</a> for mainnet and <a href="https://testnet.bitcoin-rest.bitcoin.ninja/">https://testnet.bitcoin-rest.bitcoin.ninja/</a> for testnet3. It should go without saying that this should only be used as a backup chain source, never as the primary chain source.
                </p>
        </body>
 </html>
diff --git a/rest.html b/rest.html
new file mode 100644 (file)
index 0000000..96337cb
--- /dev/null
+++ b/rest.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+       <head>
+               <meta charset="utf-8">
+               <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
+               <style type="text/css">
+                       body {
+                               margin:40px auto;
+                               max-width:660px;
+                               line-height:1.6;
+                               font-size:14pt;
+                               color:#444;
+                               padding:0 10px;
+                       }
+                       h1, h2, h3 {
+                               line-height: 1.2;
+                       }
+                       pre {
+                               overflow-x: scroll;
+                       }
+                       pre, .hex {
+                               font-size: 9pt;
+                       }
+                       .hex {
+                               font-family: monospace;
+                               word-break: break-all;
+                       }
+               </style>
+               <title>Anycast Bitcoin REST Endpoint</title>
+       </head>
+       <body>
+               <h2>
+                       This is an Anycast-Cached Bitcoin REST Endpoint
+               </h2>
+               <p>
+               This server provides standard access to a Bitcoin Core REST Endpoint at <a href="/rest/chaininfo.json">/rest/</a>. Note that it is cached, though responses should generally remain in sync with each other, after cache timeouts.
+               </p>
+       </body>
+</html>
diff --git a/split.c b/split.c
index 8e484d0abe7ec766b3825304af1018a1b6844884..1d70a2cbddc797dd2dac8d6349a8d37419960ce8 100644 (file)
--- a/split.c
+++ b/split.c
@@ -34,6 +34,9 @@ int main(int argc, char* argv[]) {
        size_t linelen = 0;
        getline(&hex, &linelen, stdin);
        size_t len = strlen(hex);
+       // len should include a \n but otherwise be a multiple of header_len
+       assert(len % 2 == 1);
+       assert((len-1)/2 % header_len == 0);
 
        if (start_height < total_height - 1) {
                int prev_chunk_count = (total_height - 1) / 10000;
@@ -60,7 +63,48 @@ int main(int argc, char* argv[]) {
        assert(write(out_fd, hex + ((len / header_len / 2) - 1) * header_len * 2, header_len * 2) == header_len * 2);
        assert(write(out_fd, "\n", 1) == 1);
 
+       // We add one byte for the version and then divide (rounding up) by 14 bytes per IPv6.
+       int addr_count = (header_len + 1 + 13) / 14;
        for (int i = 0; i < len / header_len / 2; i++) {
+               int zttl = ttl - i;
+               if (zttl < 60) { zttl = 60; }
+
+               for (int j = 0; j < addr_count; j++) {
+                       char fmt[7*5];
+                       memset(fmt, 0, sizeof(fmt));
+                       for (int k = 0; k < 7; k++) {
+                               char* target = fmt + k * 5;
+                               int start = j*14*2 + k*4 - 2;
+                               int count = header_len*2 - start;
+                               if (start < 0) {
+                                       start = 0;
+                                       target[0] = '0';
+                                       target[1] = '1';
+                                       target += 2;
+                                       if (count > 2) count = 2;
+                               }
+                               if (count < 0) {
+                                       fmt[k*5] = '0';
+                                       continue;
+                               }
+                               if (count > 4) count = 4;
+                               assert(count % 2 == 0);
+                               memcpy(target, &hex[i * header_len*2 + start], count);
+                               if (count < 4 && start != 0) {
+                                       target[2] = '0';
+                                       target[3] = '0';
+                               }
+                       }
+                       dprintf(out_fd, "v2.%d  %d      IN      AAAA    260%d:%s:%s:%s:%s:%s:%s:%s\n",
+                               start_height + i, zttl, j,
+                               &fmt[0 * 5],
+                               &fmt[1 * 5],
+                               &fmt[2 * 5],
+                               &fmt[3 * 5],
+                               &fmt[4 * 5],
+                               &fmt[5 * 5],
+                               &fmt[6 * 5]);
+               }
                if (header_len == 80) {
                        for (int j = 0; j < 6; j++) {
                                char fmt[7*5];
@@ -82,8 +126,6 @@ int main(int argc, char* argv[]) {
                                memcpy(fmt + 4*5, &hex[i * header_len*2 + 3*4 + offs], 4);
                                memcpy(fmt + 5*5, &hex[i * header_len*2 + 4*4 + offs], 4);
                                memcpy(fmt + 6*5, &hex[i * header_len*2 + 5*4 + offs], 4);
-                               int zttl = ttl - i;
-                               if (zttl < 60) { zttl = 60; }
                                dprintf(out_fd, "%d     %d      IN      AAAA    2001:%d%s:%s:%s:%s:%s:%s:%s\n",
                                        start_height + i, zttl, j,
                                        &fmt[0 * 5],
@@ -116,8 +158,6 @@ int main(int argc, char* argv[]) {
                                        fmt[5*5] = '0';
                                        fmt[6*5] = '0';
                                }
-                               int zttl = ttl - i;
-                               if (zttl < 60) { zttl = 60; }
                                dprintf(out_fd, "%d     %d      IN      AAAA    2001:%d%s:%s:%s:%s:%s:%s:%s\n",
                                        start_height + i, zttl, j,
                                        &fmt[0 * 5],
diff --git a/testnet_rest.html b/testnet_rest.html
new file mode 100644 (file)
index 0000000..60591c9
--- /dev/null
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+       <head>
+               <meta charset="utf-8">
+               <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
+               <style type="text/css">
+                       body {
+                               margin:40px auto;
+                               max-width:660px;
+                               line-height:1.6;
+                               font-size:14pt;
+                               color:#444;
+                               padding:0 10px;
+                       }
+                       h1, h2, h3 {
+                               line-height: 1.2;
+                       }
+                       pre {
+                               overflow-x: scroll;
+                       }
+                       pre, .hex {
+                               font-size: 9pt;
+                       }
+                       .hex {
+                               font-family: monospace;
+                               word-break: break-all;
+                       }
+               </style>
+               <title>Anycast Bitcoin Testnet3 REST Endpoint</title>
+       </head>
+       <body>
+               <h2>
+                       This is an Anycast-Cached Bitcoin Testnet3 REST Endpoint
+               </h2>
+               <p>
+               This server provides standard access to a Bitcoin Core REST Endpoint running on testnet3 at <a href="/rest/chaininfo.json">/rest/</a>. Note that it is cached, though responses should generally remain in sync with each other, after cache timeouts.
+               </p>
+       </body>
+</html>