]> git.bitcoin.ninja Git - satsto.me/commitdiff
Attept to use the BDC design, roughly
authorMatt Corallo <git@bluematt.me>
Fri, 12 Jul 2024 18:22:35 +0000 (18:22 +0000)
committerMatt Corallo <git@bluematt.me>
Fri, 12 Jul 2024 20:20:19 +0000 (20:20 +0000)
clipboard-svg.js
index.html

index e39ca35d382ab309b90ea26de0c18829a6cc73a5..3704aa85d542987dd2928322a1909f9e3f9a36eb 100644 (file)
@@ -2,5 +2,5 @@
 // Copyright (c) 2019-2024 The Bootstrap Authors
 // Licensed under the MIT license, see https://github.com/twbs/icons/blob/main/LICENSE
 
-export const CLIPBOARD_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16"><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/></svg>';
-export const CLIPBOARD_CHECK_SVG ='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard-check-fill" viewBox="0 0 16 16"><path d="M6.5 0A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0zm3 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5zm6.854 7.354-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L7.5 10.793l2.646-2.647a.5.5 0 0 1 .708.708"/></svg>';
+export const CLIPBOARD_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="black" class="bi bi-clipboard" viewBox="0 0 16 16"><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z"/><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z"/></svg>';
+export const CLIPBOARD_CHECK_SVG ='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="black" class="bi bi-clipboard-check-fill" viewBox="0 0 16 16"><path d="M6.5 0A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0zm3 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5zm6.854 7.354-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L7.5 10.793l2.646-2.647a.5.5 0 0 1 .708.708"/></svg>';
index cdda863851c22f5dee1d54921ebee4b05c6ce4e8..97bee845a5c38b740921b10772579e57e034dfce 100644 (file)
                <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
                <style type="text/css">
                        body {
-                               margin:30px auto;
-                               max-width:660px;
+                               margin: 0;
+                               padding: 30px;
                                line-height:1.6;
                                font-size:14pt;
                                color:#444;
-                               padding:0 10px;
+                               background: #49797E;
+                               font-family: sans;
                        }
                        h1, h2, h3 {
+                               margin-top: 0;
                                line-height: 1.2;
                        }
-                       .fill {
-                               display: flex;
+                       a { color: inherit; }
+                       .fill { display: flex; }
+                       .fill-use { flex: 1; }
+                       .intro-text { color: white; }
+                       @media screen and (min-width: 768px) {
+                               body {
+                                       display: flex;
+                                       flex-wrap: wrap;
+                                       flex-flow: column wrap;
+                                       height: 100vh;
+                                       box-sizing: border-box;
+                                       padding: 0 0 30px 30px;
+                               }
+                               .intro-text {
+                                       flex: 0;
+                                       max-width: calc(50% - 15px);
+                                       padding-top: 30px;
+                                       padding-right: 40px;
+                                       box-sizing: border-box;
+                               }
+                               .footer {
+                                       flex: 1 0 50%;
+                                       max-width: calc(50% - 15px);
+                                       padding-right: 30px;
+                                       box-sizing: border-box;
+                               }
+                               .right {
+                                       flex: 1 0 50%;
+                                       max-width: calc(50% + 15px);
+                                       min-width: calc(50% + 15px);
+                                       order: 3;
+                               }
+                               .result {
+                                       height: 100%;
+                                       padding: 2.5% 2.5%;
+                               }
+                               .right-header {
+                                       padding: 30px 30px 20px 30px !important;
+                               }
+                       }
+                       .right-header {
+                               padding: 15px;
+                               color: white;
+                               background: #3B6367;
                        }
-                       .fill-use {
-                               flex: 1;
+                       .small-print {
+                               color: white;
+                               font-size: 11pt;
                        }
-                       .small-print, .errors {
-                               font-size:11pt;
+                       .result {
+                               background: #2D4C4F;
+                               color: white;
+                               font-size: 12pt;
                        }
-                       .button, .errors {
+                       .result-populated {
+                               padding: 15px;
+                       }
+                       .button {
                                width: fit-content;
                                margin-left: auto;
                                margin-right: auto;
                                display: block;
                        }
+                       .address-card {
+                               padding: 4px 16px;
+                               margin: 4px 8px;
+                               box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
+                               border-radius: 7px;
+                               background: white;
+                       }
+                       .address-link {
+                               font-family: mono;
+                               color: #808080;
+                               text-overflow: ellipsis;
+                               overflow: hidden;
+                               display: inline-block;
+                               width: 100%;
+                       }
+                       .address-type {
+                               color: black;
+                       }
+                       .address-copy {
+                               float: right;
+                       }
                </style>
                <title>BIP 353 Human Readable Names Resolver</title>
        </head>
        <body>
-               <h2>
-                       BIP 353 Human Readable Names Resolver
-               </h2>
-               <span class="intro-text">
+               <div class="intro-text">
+                       <h2>BIP 353 Human Readable Names Resolver</h2>
                        <p>
                                <a href="https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki">BIP 353</a> defines the way to encode simple human-readable names and map them to Bitcoin payment intructions.
                        <p>
                                If your wallet doesn't yet resolve BIP 353 names natively, this site will resolve them for you, letting you pay human readable names seamlessly.
                        </p>
-               </span>
-               <p class="input-result">
-                       <form onsubmit="return false;" method="post">
-                       <div class="fill">
-                               ₿<input class="fill-use" type="text" id="address" value="matt@satsto.me"/><br>
-                       </div>
-                       <div>
-                               Resolve name using&nbsp;<select id="server">
-                                       <option value="https://dns.google/dns-query">Google's 8.8.8.8 Public DNS server</option>
-                                       <option value="http">satsto.me's native DNS proof server</option>
-                                       <option value="https://1.1.1.1/dns-query">Cloudflare's 1.1.1.1 Public DNS server</option>
-                               </select>
+               </div>
+               <div class="right">
+                       <div class="right-header">
+                               <form onsubmit="return false;" method="post">
+                               <div class="fill">
+                                       ₿<input class="fill-use" type="text" id="address" value="matt@satsto.me"/>
+                                       <input type="submit" class="button" onclick="lookup_domain()" id="paybutton" value="Pay" />
+                               </div>
+                               <div>
+                                       Resolve name using&nbsp;<select id="server">
+                                               <option value="https://dns.google/dns-query">Google's 8.8.8.8 Public DNS server</option>
+                                               <option value="http">satsto.me's native DNS proof server</option>
+                                               <option value="https://1.1.1.1/dns-query">Cloudflare's 1.1.1.1 Public DNS server</option>
+                                       </select>
+                               </div>
+                               <div class="errors" id="errors"></div>
                        </div>
-                       <div class="errors" id="result"></div>
-                       <input type="submit" class="button" onclick="lookup_domain()" id="paybutton" value="Pay" />
-               </p>
-               <p></p>
-               <span class="footer">
+                       <div class="result" id="result"></div>
+               </div>
+               <div class="footer">
                        <p class="small-print">Note that most BIP 353 addresses rely on at least <a href="https://bolt12.org">BOLT 12</a> or <a href="https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki">Silent Payments</a> and as both are relatively new, wallet support isn't yet universal. Check that your wallet supports at least one of the two if you are unable to pay.</p>
                        <p class="small-print">While you're absolutely trusting this site to not provide you with bad code, the code we promise we served you fully validates the name using DNSSEC. Thus, no matter what server you use to resolve the name, the worst they can do is log who you're paying or tell you they're not payable. They can never lie and give you the wrong address!</p>
                        <p class="small-print">Make this site beautiful by <a href="https://github.com/TheBlueMatt/satsto.me">editing it on Github</a></p>
-               </span>
+               </div>
 
                <!-- dnssec_prover_wasm.js comes from running wasm-pack build --target web` in the `wasmpack` folder in dnssec-prover -->
                <script type="module" src="dnssec_prover_wasm.js"></script>
                                        const addr_parts = address_box.value.split("@");
                                        if (addr_parts.length != 2) {
                                                document.getElementById("paybutton").disabled = true;
-                                               document.getElementById("result").innerHTML = "Address should have exactly one @";
+                                               document.getElementById("errors").innerHTML = "Address should have exactly one @";
                                                return true;
                                        }
                                        if (addr_parts[0].length == 0) {
                                                document.getElementById("paybutton").disabled = true;
-                                               document.getElementById("result").innerHTML = "Missing user part";
+                                               document.getElementById("errors").innerHTML = "Missing user part";
                                                return true;
                                        }
                                        if (addr_parts[1].length == 0) {
                                                document.getElementById("paybutton").disabled = true;
-                                               document.getElementById("result").innerHTML = "Missing domain";
+                                               document.getElementById("errors").innerHTML = "Missing domain";
                                                return true;
                                        }
                                        if (!/^[\p{ASCII}]*$/u.test(addr_parts[0])) {
                                                document.getElementById("paybutton").disabled = true;
-                                               document.getElementById("result").innerHTML = "To protect against <a href='https://en.wikipedia.org/wiki/IDN_homograph_attack'>Homograph Attacks</a>, the user part of addres must be ASCII";
+                                               document.getElementById("errors").innerHTML = "To protect against <a href='https://en.wikipedia.org/wiki/IDN_homograph_attack'>Homograph Attacks</a>, the user part of addres must be ASCII";
                                                return true;
                                        }
                                        if (!/^[\p{ASCII}]*$/u.test(addr_parts[1])) {
                                                document.getElementById("paybutton").disabled = true;
-                                               document.getElementById("result").innerHTML = "To protect against <a href='https://en.wikipedia.org/wiki/IDN_homograph_attack'>Homograph Attacks</a>, the domain part of addres must be ASCII";
+                                               document.getElementById("errors").innerHTML = "To protect against <a href='https://en.wikipedia.org/wiki/IDN_homograph_attack'>Homograph Attacks</a>, the domain part of addres must be ASCII";
                                                return true;
                                        }
                                        document.getElementById("paybutton").disabled = false;
-                                       document.getElementById("result").innerHTML = "";
+                                       document.getElementById("errors").innerHTML = "";
                                        return true;
                                }
                                document.getElementById("address").onchange = check_text;
                                                lookup_doh(address_box.value, dom, source);
                                        }
                                }
+                               const set_result = function(val) {
+                                       const result_div = document.getElementById("result");
+                                       result_div.innerHTML = val;
+                                       result_div.classList.add("result-populated");
+                               }
                                window.lookup_http = function(addr, dom) {
                                        var request = "https://http-dns-prover.as397444.net/dnssecproof?d=" + dom + "&t=txt";
                                        fetch(request).then((resp) => {
                                                        var result = verify_byte_stream(buf, dom);
                                                        handle_result(addr, JSON.parse(result));
                                                }, () => {
-                                                       document.getElementById("result").innerHTML = "Failed to read proof from server";
+                                                       set_result("Failed to read proof from server");
                                                })
                                        }, (e) => {
-                                               document.getElementById("result").innerHTML = "Failed to fetch proof: " + e;
+                                               set_result("Failed to fetch proof: " + e);
                                        });
                                }
                                window.lookup_doh = function(addr, dom, doh_endpoint) {
                                        doh.lookup_doh(dom, "txt", doh_endpoint).then((res) => {
                                                handle_result(addr, res);
                                        }, (e) => {
-                                               document.getElementById("result").innerHTML = "Failed to fetch proof: " + e;
+                                               set_result("Failed to fetch proof: " + e);
                                        });
                                }
                                window.handle_result = function(name, result) {
                                        if (!result.hasOwnProperty("valid_from") || !result.hasOwnProperty("expires") || !result.hasOwnProperty("verified_rrs")) {
-                                               document.getElementById("result").innerHTML = "Failed to fetch valid proof";
+                                               set_result("Failed to fetch valid proof");
                                                return;
                                        }
                                        if (Date.now() / 1000 < result.valid_from) {
-                                               document.getElementById("result").innerHTML = "Proof is not yet valid (check your system clock)";
+                                               set_result("Proof is not yet valid (check your system clock)");
                                                return;
                                        }
                                        if (Date.now() / 1000 > result.expires) {
-                                               document.getElementById("result").innerHTML = "Proof has expired (check your system clock?)";
+                                               set_result("Proof has expired (check your system clock?)");
                                                return;
                                        }
                                        var bip353 = null;
                                        for (const rr of result.verified_rrs) {
                                                if (rr.type != "txt") {
-                                                       document.getElementById("result").innerHTML = "Proof was invalid";
+                                                       set_result("Proof was invalid");
                                                        return;
                                                }
                                                if (typeof rr.contents === "string" && rr.contents.toLowerCase().startsWith("bitcoin:")) {
                                                        if (bip353 !== null) {
-                                                               document.getElementById("result").innerHTML = "Address is BIP 353-invalid - it contains multiple results";
+                                                               set_result("Address is BIP 353-invalid - it contains multiple results");
                                                                return;
                                                        }
                                                        bip353 = rr.contents;
                                                }
                                        }
                                        if (bip353 === null) {
-                                               document.getElementById("result").innerHTML = "Address is BIP 353-invalid - it contains no bitcoin: URI";
+                                               set_result("Address is BIP 353-invalid - it contains no bitcoin: URI");
                                                return;
                                        }
                                        var addr_ty_table = "";
                                        var addr_idx = 0;
                                        const base_and_params = bip353.substring(8).split("?");
                                        const push_table_entry = function(ty, uri_pfx, contents) {
-                                               var value = "";
+                                               var value = "<h2 class='address-type'>" + ty + "<a class='address-copy' id='addr_copy_" + addr_idx + "' href='#' onclick=\"copy('" + contents + "', " + addr_idx + ")\">" + CLIPBOARD_SVG + "</a></h2>";
                                                if (!/^[\p{ASCII}]*$/u.test(contents)) {
                                                        value = "Invalid";
-                                               } else if (contents.length < 70) {
-                                                       value = "<a class='address-link' href='bitcoin:" + uri_pfx + contents + "'>" + contents + "</a><a class='address-copy' id='addr_copy_" + addr_idx + "' onclick=\"copy('" + contents + "', " + addr_idx + ")\">" + CLIPBOARD_SVG + "</a>";
                                                } else {
-                                                       value = "<a class='address-link' href='bitcoin:" + uri_pfx + contents + "'>" + contents.substring(0, 70) + "...</a><a class='address-copy' id='addr_copy_" + addr_idx + "' onclick=\"copy('" + contents + "', " + addr_idx + ")\">" + CLIPBOARD_SVG + "</a>";
+                                                       value += "<a class='address-link' href='bitcoin:" + uri_pfx + contents + "'>" + contents + "</a>";
                                                }
                                                addr_idx += 1;
-                                               addr_ty_table += "<div class='address-card'><span class='address-type'>" + ty + "</span>" + value + "</div>";
+                                               addr_ty_table += "<div class='address-card'>" + value + "</div><br>";
                                        };
                                        if (base_and_params[0].length != 0) {
                                                push_table_entry("On-Chain Non-Private Address", "", base_and_params[0]);
                                                        }
                                                }
                                        }
-                                       const result_elem = document.getElementById("result");
-                                       result_elem.innerHTML = "<a href=\"" + bip353 + "\">Opening your bitcoin wallet to pay " + name + "! If it doesn't work, click here.</a>";
+                                       var res = "<h2>It works!</h2>" + name + " was successfully resolved to the following addresses.<br>Your wallet should have automatically opened to pay, but if not, <a href=\"" + bip353 + "\">click here to do so.</a>";
                                        if (addr_ty_table != "") {
-                                               result_elem.innerHTML += "<br>" + addr_ty_table;
+                                               res += "<br>" + addr_ty_table;
                                        }
+                                       set_result(res);
                                        window.location = bip353;
                                }
                                window.copy = function(text, element_id) {