diff options
| author | Bitflash-sh <btf01@proton.me> | 2026-07-24 11:18:11 -0300 |
|---|---|---|
| committer | Bitflash-sh <btf01@proton.me> | 2026-07-24 11:18:11 -0300 |
| commit | 51d617e70f90a972185917ea0096c1ecef2bfa8b (patch) | |
| tree | 9fbbcd867de9de9d0776afdd02f594ba312c46eb /src/test_btfaddr.cpp | |
Bitflash: fair-launch release
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf
addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a
headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
Diffstat (limited to 'src/test_btfaddr.cpp')
| -rw-r--r-- | src/test_btfaddr.cpp | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/test_btfaddr.cpp b/src/test_btfaddr.cpp new file mode 100644 index 0000000..b40dcc1 --- /dev/null +++ b/src/test_btfaddr.cpp | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | // Standalone tests for btfaddr (ported from Itzal hidden.rs tests). | ||
| 2 | // Build (UCRT64): | ||
| 3 | // g++ -std=gnu++14 -DSECP256K1_STATIC -I../deps/include test_btfaddr.cpp btfaddr.cpp \ | ||
| 4 | // -L../deps/lib -lsecp256k1 -lssl -lcrypto -o test_btfaddr.exe | ||
| 5 | |||
| 6 | #include "btfaddr.h" | ||
| 7 | #include <openssl/rand.h> | ||
| 8 | #define SECP256K1_STATIC | ||
| 9 | #include <secp256k1.h> | ||
| 10 | #include <secp256k1_schnorrsig.h> | ||
| 11 | #include <secp256k1_extrakeys.h> | ||
| 12 | #include <nlohmann/json.hpp> | ||
| 13 | #include <cstdio> | ||
| 14 | #include <cstring> | ||
| 15 | #include <string> | ||
| 16 | |||
| 17 | using json = nlohmann::json; | ||
| 18 | |||
| 19 | static int g_fail = 0; | ||
| 20 | #define CHECK(cond, name) do { \ | ||
| 21 | if (cond) { printf(" ok %s\n", name); } \ | ||
| 22 | else { printf(" FAIL %s\n", name); g_fail++; } } while(0) | ||
| 23 | |||
| 24 | static std::string ToUpper(const std::string& s) | ||
| 25 | { | ||
| 26 | std::string r = s; | ||
| 27 | for (char& c : r) c = (char)toupper((unsigned char)c); | ||
| 28 | return r; | ||
| 29 | } | ||
| 30 | |||
| 31 | int main() | ||
| 32 | { | ||
| 33 | secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); | ||
| 34 | unsigned char seed[32]; RAND_bytes(seed, 32); secp256k1_context_randomize(ctx, seed); | ||
| 35 | |||
| 36 | printf("base32_roundtrips\n"); | ||
| 37 | { | ||
| 38 | std::vector<std::vector<unsigned char>> cases; | ||
| 39 | std::string txt = "hello btf shadow 0123456789"; | ||
| 40 | cases.push_back(std::vector<unsigned char>(txt.begin(), txt.end())); | ||
| 41 | cases.push_back(std::vector<unsigned char>(34, 0x00)); | ||
| 42 | cases.push_back(std::vector<unsigned char>(34, 0xff)); | ||
| 43 | for (auto& data : cases) | ||
| 44 | { | ||
| 45 | std::string enc = btf::Base32Encode(data.data(), data.size()); | ||
| 46 | std::vector<unsigned char> dec; | ||
| 47 | bool ok = btf::Base32Decode(enc, dec); | ||
| 48 | // decode may include trailing partial byte handling; compare the meaningful prefix | ||
| 49 | CHECK(ok && dec.size() >= data.size() && | ||
| 50 | memcmp(dec.data(), data.data(), data.size()) == 0, "roundtrip"); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | printf("address_roundtrips_and_is_lowercase_hostname\n"); | ||
| 55 | { | ||
| 56 | unsigned char pk[32]; memset(pk, 7, 32); | ||
| 57 | std::string a = btf::Address(pk); | ||
| 58 | CHECK(a.size() > 4 && a.compare(a.size()-4, 4, ".btf") == 0, "ends with .btf"); | ||
| 59 | CHECK(btf::IsBtf(a), "IsBtf true"); | ||
| 60 | std::string label = a.substr(0, a.size()-4); | ||
| 61 | bool lc = true; | ||
| 62 | for (unsigned char c : label) | ||
| 63 | if (!((c >= 'a' && c <= 'z') || (c >= '2' && c <= '7'))) lc = false; | ||
| 64 | CHECK(lc, "label is lowercase base32 hostname"); | ||
| 65 | unsigned char out[32]; | ||
| 66 | CHECK(btf::ParseAddress(a, out) && memcmp(out, pk, 32) == 0, "parse round-trips"); | ||
| 67 | CHECK(btf::ParseAddress(ToUpper(a), out) && memcmp(out, pk, 32) == 0, "parse case-insensitive"); | ||
| 68 | } | ||
| 69 | |||
| 70 | printf("rejects_tampered_address\n"); | ||
| 71 | { | ||
| 72 | unsigned char pk[32]; memset(pk, 9, 32); | ||
| 73 | std::string a = btf::Address(pk); | ||
| 74 | std::string t = a; | ||
| 75 | t[0] = (t[0] == 'a') ? 'b' : 'a'; | ||
| 76 | unsigned char out[32]; | ||
| 77 | CHECK(!(btf::ParseAddress(t, out) && memcmp(out, pk, 32) == 0), "tampered address rejected"); | ||
| 78 | } | ||
| 79 | |||
| 80 | printf("rejects_non_btf\n"); | ||
| 81 | { | ||
| 82 | unsigned char out[32]; | ||
| 83 | CHECK(!btf::IsBtf("example.com"), "example.com not btf"); | ||
| 84 | CHECK(!btf::ParseAddress("example.com", out), "parse example.com fails"); | ||
| 85 | CHECK(!btf::ParseAddress("not-base-32-!!!.btf", out), "parse invalid base32 fails"); | ||
| 86 | } | ||
| 87 | |||
| 88 | const std::string ENC_HEX = "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"; | ||
| 89 | |||
| 90 | // helper: fresh keypair -> (seckey, xonly pubkey) | ||
| 91 | auto newKey = [&](unsigned char sk[32], unsigned char pk[32]) { | ||
| 92 | do { RAND_bytes(sk, 32); } while (!secp256k1_ec_seckey_verify(ctx, sk)); | ||
| 93 | secp256k1_keypair kp; secp256k1_keypair_create(ctx, &kp, sk); | ||
| 94 | secp256k1_xonly_pubkey xp; secp256k1_keypair_xonly_pub(ctx, &xp, NULL, &kp); | ||
| 95 | secp256k1_xonly_pubkey_serialize(ctx, pk, &xp); | ||
| 96 | }; | ||
| 97 | |||
| 98 | printf("descriptor_signs_and_self_certifies\n"); | ||
| 99 | { | ||
| 100 | unsigned char sk[32], pk[32]; newKey(sk, pk); | ||
| 101 | std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "meeting-node-abc", 1000); | ||
| 102 | CHECK(!d.empty(), "sign produced descriptor"); | ||
| 103 | btf::Descriptor got; | ||
| 104 | CHECK(btf::VerifyDescriptor(ctx, d, pk, got), "own key verifies"); | ||
| 105 | CHECK(got.meeting_node == "meeting-node-abc" && got.enc == ENC_HEX && got.created == 1000, "fields match"); | ||
| 106 | unsigned char sk2[32], pk2[32]; newKey(sk2, pk2); | ||
| 107 | btf::Descriptor bad; | ||
| 108 | CHECK(!btf::VerifyDescriptor(ctx, d, pk2, bad), "different key rejected"); | ||
| 109 | } | ||
| 110 | |||
| 111 | printf("descriptor_rejects_tampering\n"); | ||
| 112 | { | ||
| 113 | unsigned char sk[32], pk[32]; newKey(sk, pk); | ||
| 114 | std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "node-1", 5); | ||
| 115 | json j = json::parse(d); j["meeting_node"] = "evil-node"; | ||
| 116 | btf::Descriptor o; | ||
| 117 | CHECK(!btf::VerifyDescriptor(ctx, j.dump(), pk, o), "tampered meeting_node rejected"); | ||
| 118 | json j2 = json::parse(d); j2["enc"] = "00000000000000000000000000000000000000000000000000000000deadbeef"; | ||
| 119 | CHECK(!btf::VerifyDescriptor(ctx, j2.dump(), pk, o), "tampered enc rejected"); | ||
| 120 | } | ||
| 121 | |||
| 122 | printf("full_resolve_chain_from_address\n"); | ||
| 123 | { | ||
| 124 | unsigned char sk[32], pk[32]; newKey(sk, pk); | ||
| 125 | std::string addr = btf::Address(pk); | ||
| 126 | unsigned char resolved[32]; | ||
| 127 | CHECK(btf::ParseAddress(addr, resolved), "address parses"); | ||
| 128 | std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "meeting", 1); | ||
| 129 | btf::Descriptor o; | ||
| 130 | CHECK(btf::VerifyDescriptor(ctx, d, resolved, o), "descriptor verifies under resolved key"); | ||
| 131 | } | ||
| 132 | |||
| 133 | // Show a sample address so we can eyeball the format | ||
| 134 | { | ||
| 135 | unsigned char sk[32], pk[32]; newKey(sk, pk); | ||
| 136 | printf("\nsample .btf address:\n %s\n", btf::Address(pk).c_str()); | ||
| 137 | } | ||
| 138 | |||
| 139 | printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); | ||
| 140 | secp256k1_context_destroy(ctx); | ||
| 141 | return g_fail == 0 ? 0 : 1; | ||
| 142 | } | ||