灵符lingfu
L

ling-wallet

↓ 0 downloads

Ling Wallet: a post-quantum privacy-coin wallet, full node and HyperTorus miner for LNG

Windows · x86-64 ✕Linux · x86-64 ✕LingOS · x86-64LingOS · Pi 5 ARM64 ✕
Keywords wallet, cryptocurrency, lng, node, miner, ling
lingfu install ling-wallet
LicenseMIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (116)

ling/tests/crypto/t_crypto.ling
# t_crypto.ling — end-to-end tests of the ling-wallet byte/number helpers
# (LING_API.md §2) and wallet cryptography builtins (§3), run from Ling.
#
#   ling run --interp t_crypto.ling
#
# Prints one PASS/FAIL line per check and a summary. Expected constants were
# produced by the original reference implementation for the same inputs, so
# passing here means the Ling builtins are byte-compatible with it.
# Builtins whose malformed-input behaviour is a *runtime error* (rather than
# bad(..)) are covered by the one-line scripts in errors/ (run_errors.sh).


fn check(label, cond) {
    if cond {
        print(format("PASS {}", label))
    } else {
        print(format("FAIL {}", label))
    }
    return cond
}

fn is_ok(r) {
    match r {
        ok(v) => { return true }
        bad(e) => { return false }
    }
}

fn is_bad(r) {
    match r {
        ok(v) => { return false }
        bad(e) => { return true }
    }
}

# The ok payload, or the string "ERR:<reason>" (never equal to a hex value).
fn val(r) {
    match r {
        ok(v) => { return v }
        bad(e) => { return format("ERR:{}", e) }
    }
}

# `n` copies of the byte `b` (2-char hex).
fn rep(b, n) {
    bind out = ""
    bind i = 0
    while (i < n) {
        bind out = out + b
        bind i = i + 1
    }
    return out
}

# Flip the low bit of byte `pos` of hex string `h`.
fn flip(h, pos) {
    bind b = val(hex_u8(h, pos))
    bind nb = b - 2 * floor(b / 2)
    bind b2 = b + 1
    if nb == 1 { bind b2 = b - 1 }
    bind tail = hex_slice(h, pos + 1, hex_len(h) - pos - 1)
    return hex_concat([hex_slice(h, 0, pos), u8_hex(b2), tail])
}

fn digest(h) {
    return lw_h256("LW1/test/digest", [h])
}

fn tseed(k) {
    return lw_h256("LW1/test/seed", [u32le_hex(k)])
}

# A ring of n members; member i is a one-time key of its own spend key.
# Returns [ring_pks, handles].
fn make_ring(n, tag) {
    bind pks = []
    bind hs = []
    bind i = 0
    while (i < n) {
        bind sp = val(lw_spend_keypair(tseed(tag * 1000 + i)))
        bind ssx = tseed(tag * 1000 + 500 + i)
        bind ot = val(lw_onetime_secret(sp, ssx, i))
        bind pks = list_push(pks, lw_onetime_public_of(ot))
        bind hs = list_push(hs, ot)
        lw_key_drop(sp)
        bind i = i + 1
    }
    return [pks, hs]
}

bind start = do {
bind T0 = time_now()
# ════════════════════════════════════════════════════════════ §2 byte helpers
print("── §2 byte and number helpers")

check("hex_concat", hex_concat(["00", "ff", "", "AB"]) == "00ffab")
check("hex_concat empty", hex_concat([]) == "")
check("hex_len", hex_len("00ff10") == 3)
check("hex_len empty", hex_len("") == 0)
check("hex_slice", hex_slice("0011223344", 1, 3) == "112233")
check("hex_slice end", hex_slice("0011223344", 5, 0) == "")
check("hex_eq equal", hex_eq("00ff", "00FF"))
check("hex_eq differ", !hex_eq("00ff", "00fe"))
check("hex_eq lengths", !hex_eq("00ff", "00ff00"))
check("u8_hex", u8_hex(255) == "ff" && u8_hex(0) == "00")
check("u32le_hex", u32le_hex(1) == "01000000" && u32le_hex(4294967295) == "ffffffff")
check("u64le_hex", u64le_hex(9007199254740991) == "ffffffffffff1f00")
check("varint_hex", varint_hex(0) == "00" && varint_hex(127) == "7f" && varint_hex(300) == "ac02")
check("varint_hex max", varint_hex(9007199254740991) == "ffffffffffffff0f")

bind vr = val(hex_varint("ff00ac02", 2))
check("hex_varint value+pos", vr[0] == 300 && vr[1] == 4)
bind vm = val(hex_varint(varint_hex(9007199254740991), 0))
check("hex_varint 2^53-1", vm[0] == 9007199254740991 && vm[1] == 8)
check("hex_varint rejects non-minimal 8000", is_bad(hex_varint("8000", 0)))
check("hex_varint rejects non-minimal ff00", is_bad(hex_varint("ff00", 0)))
check("hex_varint rejects truncated", is_bad(hex_varint("80", 0)))
check("hex_varint rejects 2^53", is_bad(hex_varint("8080808080808010", 0)))
check("hex_varint rejects past end", is_bad(hex_varint("00", 1)))
check("hex_varint bad hex", is_bad(hex_varint("0g", 0)))

check("hex_u8", val(hex_u8("00ff", 1)) == 255)
check("hex_u8 past end", is_bad(hex_u8("00ff", 2)))
check("hex_u32le", val(hex_u32le("aa78563412", 1)) == 305419896)
check("hex_u32le past end", is_bad(hex_u32le("785634", 0)))
check("hex_u64le", val(hex_u64le("ffffffffffff1f00", 0)) == 9007199254740991)
check("hex_u64le rejects >= 2^53", is_bad(hex_u64le("0000000000002000", 0)))
check("hex_u64le odd hex", is_bad(hex_u64le("000", 0)))
check("hex_u8 bad hex", is_bad(hex_u8("zz", 0)))
check("hex_u8 negative pos", is_bad(hex_u8("00", -1)))
check("hex_u8 fractional pos", is_bad(hex_u8("0000", 0.5)))

check("text_hex", text_hex("abc") == "616263")
check("text_hex utf8", text_hex("灵") == "e781b5")
check("hex_text roundtrip", val(hex_text(text_hex("ling · 灵 · ลิง"))) == "ling · 灵 · ลิง")
check("hex_text invalid utf8", is_bad(hex_text("ff")))
check("hex_text bad hex", is_bad(hex_text("6")))

check("base32 foobar", base32_encode(text_hex("foobar")) == "mzxw6ytboi")
check("base32 f", base32_encode("66") == "my")
check("base32 decode", val(base32_decode("mzxw6ytboi")) == text_hex("foobar"))
check("base32 decode uppercase", val(base32_decode("MZXW6YTBOI")) == text_hex("foobar"))
check("base32 rejects trailing bits", is_bad(base32_decode("mz")))
check("base32 rejects padding", is_bad(base32_decode("my======")))
check("base32 rejects bad char", is_bad(base32_decode("m1")))
bind b32r = lw_random(37)
check("base32 random roundtrip", val(base32_decode(base32_encode(b32r))) == b32r)

check("base64url encode", base64url_encode("fbff") == "-_8")
check("base64url decode", val(base64url_decode("-_8")) == "fbff")
check("base64url decode padded", val(base64url_decode("-_8=")) == "fbff")
check("base64url rejects std alphabet", is_bad(base64url_decode("+/8")))
check("base64url rejects bad length", is_bad(base64url_decode("a")))
check("base64url rejects trailing bits", is_bad(base64url_decode("-_9")))
bind b64r = lw_random(100)
check("base64url random roundtrip", val(base64url_decode(base64url_encode(b64r))) == b64r)

bind U256MAX = "115792089237316195423570985008687907853269984665640564039457584007913129639935"
bind TWO128 = "340282366920938463463374607431768211456"
check("big_add", big_add("18446744073709551615", "1") == "18446744073709551616")
check("big_add to max", big_add("115792089237316195423570985008687907853269984665640564039457584007913129639934", "1") == U256MAX)
check("big_sub", big_sub(TWO128, "1") == "340282366920938463463374607431768211455")
check("big_sub zero", big_sub("5", "5") == "0")
check("big_mul", big_mul("18446744073709551616", "18446744073709551616") == TWO128)
check("big_mul number arg", big_mul("600", 120) == "72000")
check("big_div", big_div("1000000000000000000000", "7") == "142857142857142857142")
check("big_div max/2^128", big_div(U256MAX, TWO128) == "340282366920938463463374607431768211455")
check("big_div max/max", big_div(U256MAX, U256MAX) == "1")
check("big_div floor small", big_div("7", "8") == "0")
check("big_cmp", big_cmp("10", "9") == 1 && big_cmp("9", "10") == -1 && big_cmp("007", "7") == 0)
check("big_cmp max", big_cmp(U256MAX, TWO128) == 1)

bind tmpf = "t_crypto_tmp.bin"
bind fdata = hex_concat(["00ff", lw_random(64)])
check("file_write_hex", is_ok(file_write_hex(tmpf, fdata)))
check("file_read_hex", val(file_read_hex(tmpf)) == fdata)
check("file_write_hex overwrite", is_ok(file_write_hex(tmpf, "c0ffee")) && val(file_read_hex(tmpf)) == "c0ffee")
check("file_write_hex bad hex", is_bad(file_write_hex(tmpf, "xyz")))
check("file_read_hex missing", is_bad(file_read_hex("t_crypto_does_not_exist.bin")))
check("file unchanged after bad write", val(file_read_hex(tmpf)) == "c0ffee")
bind sec = "secret"
check("wipe returns unit", format("{}", wipe(sec)) == "()")

# ════════════════════════════════════════════════════════════ §3 hashing
print("── §3 hashing / randomness")
check("lw_h256 vector", lw_h256("LW1/test/vec", [text_hex("abc"), ""]) == "775d4915136df882fdba4c065d7abc018a3511e94185473247f7751e130bf5ab")
check("lw_h256 no parts", lw_h256("LW1/test/vec", []) == "6b27c10171ff354603acbba5ac0a7f76c046a3ccd51faa20b3177451565dca90")
check("lw_h256 boundaries", lw_h256("LW1/a", ["6162", "63"]) != lw_h256("LW1/a", ["61", "6263"]))
check("lw_h256 domains", lw_h256("LW1/a", ["78"]) != lw_h256("LW1/b", ["78"]))
check("lw_xof vector", lw_xof("LW1/test/vec", [text_hex("abc")], 64) == "7cd66a0b9970b42a95d49007f2c8f4becc4d48a69759f60828a5933792a4dbec1dae63013e6a921a7882d8cebcd8d53343604dc0345756a55fec2bf616adeccd")
check("lw_xof prefix = h256", hex_slice(lw_xof("LW1/t", ["00"], 100), 0, 32) == lw_h256("LW1/t", ["00"]))
check("lw_xof len 0", lw_xof("LW1/t", [], 0) == "")
bind r1 = lw_random(32)
bind r2 = lw_random(32)
check("lw_random length", hex_len(r1) == 32 && hex_len(lw_random(0)) == 0)
check("lw_random fresh", r1 != r2)

# ════════════════════════════════════════════════════════════ §3 KEM
print("── §3 hybrid KEM")
bind kh = val(lw_kem_keypair(rep("03", 96)))
check("kem handle is 128-bit hex", hex_len(kh) == 16)
bind kpk = lw_kem_public(kh)
check("kem public length", hex_len(kpk) == 1600)
check("kem public matches reference", digest(kpk) == "ebb19e2b79a81e12fb8d087196fceb4df6639e3b64d6f2b20b6ca5a77f1339ef")
bind kh2 = val(lw_kem_keypair(rep("03", 96)))
check("kem handles unguessable/distinct", kh != kh2 && lw_kem_public(kh2) == kpk)
check("kem keypair bad seed length", is_bad(lw_kem_keypair(rep("03", 95))))
check("kem keypair bad hex", is_bad(lw_kem_keypair("zz")))
bind ctx = text_hex("LW1/output")
bind enc = val(lw_kem_encaps(kpk, ctx))
bind kct = enc[0]
bind kss = enc[1]
check("kem ct/ss lengths", hex_len(kct) == 1600 && hex_len(kss) == 32)
check("kem roundtrip", val(lw_kem_decaps(kh, kct, ctx)) == kss)
check("kem context binds", val(lw_kem_decaps(kh, kct, text_hex("other"))) != kss)
check("kem tamper (mlkem part)", val(lw_kem_decaps(kh, flip(kct, 100), ctx)) != kss)
check("kem tamper (x25519 part)", val(lw_kem_decaps(kh, flip(kct, 3), ctx)) != kss)
check("kem encaps randomized", val(lw_kem_encaps(kpk, ctx))[0] != kct)
check("kem encaps invalid pk", is_bad(lw_kem_encaps(rep("ff", 1600), ctx)))
check("kem encaps short pk", is_bad(lw_kem_encaps(rep("00", 32), ctx)))
check("kem encaps bad hex", is_bad(lw_kem_encaps("xyz", ctx)))
check("kem decaps short ct", is_bad(lw_kem_decaps(kh, "00", ctx)))
check("kem decaps unknown handle", is_bad(lw_kem_decaps("00000000000000000000000000000000", kct, ctx)))

# ════════════════════════════════════════════════════════════ §3 identity
print("── §3 ML-DSA-87 identity")
bind ih = val(lw_id_keypair(rep("04", 32)))
bind ipk = lw_id_public(ih)
check("id public length", hex_len(ipk) == 2592)
check("id public matches reference", digest(ipk) == "e9ef2dbd790716e02bfab783a35c0655f5b95e2b833bc0ea807596cfa657f563")
check("id fingerprint", lw_id_fingerprint(ipk) == "709b-fe8e-976e-63ff")
bind imsg = text_hex("msg")
bind isig = lw_id_sign(ih, "bundle", imsg)
check("id signature length", hex_len(isig) == 4627)
check("id verify", lw_id_verify(ipk, "bundle", imsg, isig))
check("id context separation", !lw_id_verify(ipk, "seeds", imsg, isig))
check("id wrong message", !lw_id_verify(ipk, "bundle", text_hex("msh"), isig))
check("id tampered sig", !lw_id_verify(ipk, "bundle", imsg, flip(isig, 10)))
bind ih2 = val(lw_id_keypair(rep("05", 32)))
check("id other key", !lw_id_verify(lw_id_public(ih2), "bundle", imsg, isig))
check("id verify malformed -> false", !lw_id_verify("zz", "bundle", imsg, isig) && !lw_id_verify(ipk, "bundle", imsg, "00"))
check("id keypair bad seed", is_bad(lw_id_keypair("0404")))

# ════════════════════════════════════════════════════════════ §3 LRS keys
print("── §3 LRS-87 spend / one-time keys")
bind sh = val(lw_spend_keypair(rep("07", 32)))
bind spk = lw_spend_public(sh)
check("spend public length", hex_len(spk) == 2944)
check("spend public matches reference", digest(spk) == "eae81f50d6b18df5d97f0f616a4ff0483622a5c1c5953c9f26bdda98a5935159")
check("pk_valid spend", lw_pk_valid(spk))
check("pk_valid rejects >= q", !lw_pk_valid(rep("ff", 2944)))
check("pk_valid rejects length", !lw_pk_valid(rep("00", 2943)) && !lw_pk_valid(rep("00", 2945)))
check("pk_valid rejects bad hex", !lw_pk_valid("zz"))
check("pk_valid zero key", lw_pk_valid(rep("00", 2944)))
bind ss9 = rep("09", 32)
bind otpub = val(lw_onetime_public(spk, ss9, 5))
check("onetime public matches reference", digest(otpub) == "b4c7ed815ebd55d6cd88813f4be182ef29c9c5fa29d82318f912dc4a0645c42f")
bind oth = val(lw_onetime_secret(sh, ss9, 5))
check("onetime_public == onetime_public_of(secret)", lw_onetime_public_of(oth) == otpub)
check("onetime key image matches reference", digest(lw_onetime_key_image(oth)) == "4ca71444488f116d72986c3eed7d57e6df072328848428e27358de9a2c9b964e")
bind oth_b = val(lw_onetime_secret(sh, ss9, 5))
check("key images equal for same secret", lw_onetime_key_image(oth_b) == lw_onetime_key_image(oth))
bind oth6 = val(lw_onetime_secret(sh, ss9, 6))
check("other index: different key and image", lw_onetime_public_of(oth6) != otpub && lw_onetime_key_image(oth6) != lw_onetime_key_image(oth))
check("onetime index consistency 6", val(lw_onetime_public(spk, ss9, 6)) == lw_onetime_public_of(oth6))
check("onetime public invalid spend pk", is_bad(lw_onetime_public(rep("ff", 2944), ss9, 0)))
check("onetime public bad ss length", is_bad(lw_onetime_public(spk, "0909", 0)))
check("onetime public bad index", is_bad(lw_onetime_public(spk, ss9, -1)) && is_bad(lw_onetime_public(spk, ss9, 4294967296)))
check("onetime secret wrong handle kind", is_bad(lw_onetime_secret(kh, ss9, 0)))

# ════════════════════════════════════════════════════════════ §3 ring signatures
print("── §3 LRS-87 ring signatures")


bind msg = lw_h256("LW1/test/msg", [text_hex("tx prefix")])
bind sizes = [1, 3, 11]
bind szi = 0
while (szi < len(sizes)) {
    bind n = sizes[szi]
    bind rr = make_ring(n, n)
    bind ring = rr[0]
    bind hs = rr[1]
    bind pi = floor(n / 2)
    bind ki = lw_onetime_key_image(hs[pi])
    bind t1 = time_now()
    bind sig = val(lw_ring_sign(msg, ring, pi, hs[pi]))
    bind t2 = time_now()
    bind good = lw_ring_verify(msg, ring, ki, sig)
    bind t3 = time_now()
    check(format("ring {} sign/verify (signer {})", n, pi), good)
    check(format("ring {} signature length", n), hex_len(sig) == 32 + 9600 * n)
    check(format("ring {} last-position signer", n), lw_ring_verify(msg, ring, lw_onetime_key_image(hs[n - 1]), val(lw_ring_sign(msg, ring, n - 1, hs[n - 1]))))
    check(format("ring {} wrong message", n), !lw_ring_verify(lw_h256("LW1/test/msg", []), ring, ki, sig))
    check(format("ring {} tampered ch0", n), !lw_ring_verify(msg, ring, ki, flip(sig, 0)))
    check(format("ring {} tampered z", n), !lw_ring_verify(msg, ring, ki, flip(sig, 32 + 1234)))
    check(format("ring {} tampered key image", n), !lw_ring_verify(msg, ring, flip(ki, 100), sig))
    check(format("ring {} truncated sig", n), !lw_ring_verify(msg, ring, ki, hex_slice(sig, 0, hex_len(sig) - 1)))
    if n == 11 {
        print(format("TIME ring-11 sign {} ms, verify {} ms", floor((t2 - t1) * 1000), floor((t3 - t2) * 1000)))
        bind swapped = [ring[1], ring[0], ring[2], ring[3], ring[4], ring[5], ring[6], ring[7], ring[8], ring[9], ring[10]]
        check("ring 11 reordered", !lw_ring_verify(msg, swapped, ki, sig))
        check("ring 11 other key image", !lw_ring_verify(msg, ring, lw_onetime_key_image(hs[0]), sig))
        check("ring 11 wrong signer index -> bad", is_bad(lw_ring_sign(msg, ring, 0, hs[pi])))
        check("ring 11 index out of range -> bad", is_bad(lw_ring_sign(msg, ring, 11, hs[pi])))
        check("ring 11 malformed member -> bad", is_bad(lw_ring_sign(msg, [ring[0], rep("ff", 2944)], 0, hs[0])))
        check("ring 11 malformed member verify -> false", !lw_ring_verify(msg, [rep("ff", 2944)], ki, hex_slice(sig, 0, 9632)))
        check("ring sign randomized", val(lw_ring_sign(msg, ring, pi, hs[pi])) != sig)
    }
    bind hi = 0
    while (hi < n) {
        lw_key_drop(hs[hi])
        bind hi = hi + 1
    }
    bind szi = szi + 1
}

# Linkability: the same one-time key signs in two different rings, same image.
bind la = make_ring(4, 41)
bind lb = make_ring(5, 42)
bind ringb = [lb[0][0], lb[0][1], lb[0][2], la[0][2], lb[0][4]]
bind sa = val(lw_ring_sign(msg, la[0], 2, la[1][2]))
bind sb = val(lw_ring_sign(lw_h256("LW1/test/msg2", []), ringb, 3, la[1][2]))
bind kil = lw_onetime_key_image(la[1][2])
check("linkability: both verify under the same key image", lw_ring_verify(msg, la[0], kil, sa) && lw_ring_verify(lw_h256("LW1/test/msg2", []), ringb, kil, sb))
check("ring verify empty ring", !lw_ring_verify(msg, [], kil, hex_slice(sa, 0, 32)))
check("ring verify bad msg length", !lw_ring_verify("00", la[0], kil, sa))
check("ring sign empty ring -> bad", is_bad(lw_ring_sign(msg, [], 0, la[1][2])))
check("ring sign bad msg -> bad", is_bad(lw_ring_sign("00", la[0], 2, la[1][2])))
check("ring sign non-onetime handle -> bad", is_bad(lw_ring_sign(msg, la[0], 2, kh)))
check("key_drop", lw_key_drop(la[1][2]) && !lw_key_drop(la[1][2]))
check("dropped handle unusable", is_bad(lw_ring_sign(msg, la[0], 2, la[1][2])))
check("key_drop unknown", !lw_key_drop("nope"))

# ════════════════════════════════════════════════════════════ §3 AEAD / KDF
print("── §3 AEAD / password KDF / mnemonic")
bind akey = rep("07", 32)
bind anon = rep("09", 24)
bind act = lw_aead_seal(akey, anon, text_hex("hdr"), text_hex("hello"))
check("aead seal matches reference", act == "d6178ee613489d126ae1aa2985ab6f5d9c41886328")
check("aead open", val(lw_aead_open(akey, anon, text_hex("hdr"), act)) == text_hex("hello"))
check("aead wrong aad", is_bad(lw_aead_open(akey, anon, text_hex("other"), act)))
check("aead tamper", is_bad(lw_aead_open(akey, anon, text_hex("hdr"), flip(act, 0))))
check("aead tamper tag", is_bad(lw_aead_open(akey, anon, text_hex("hdr"), flip(act, 20))))
check("aead wrong key", is_bad(lw_aead_open(rep("08", 32), anon, text_hex("hdr"), act)))
check("aead open short key -> bad", is_bad(lw_aead_open("07", anon, text_hex("hdr"), act)))
check("aead empty plaintext", val(lw_aead_open(akey, anon, "", lw_aead_seal(akey, anon, "", ""))) == "")

bind pk1 = val(lw_password_key("pw", rep("01", 16), 64, 1, 1))
check("password_key matches reference", pk1 == "50f5d8d4c1ce1573e0e43dbb1a09ef19dbf4b82667d3f97608f0628b542a5ec2")
check("password_key deterministic", val(lw_password_key("pw", rep("01", 16), 64, 1, 1)) == pk1)
check("password_key salted", val(lw_password_key("pw", rep("02", 16), 64, 1, 1)) != pk1)
check("password_key password", val(lw_password_key("pW", rep("01", 16), 64, 1, 1)) != pk1)
check("password_key bad salt", is_bad(lw_password_key("pw", "01", 64, 1, 1)))
check("password_key bad params", is_bad(lw_password_key("pw", rep("01", 16), 64, 0, 1)) && is_bad(lw_password_key("pw", rep("01", 16), 1, 1, 1)))

bind words0 = lw_mnemonic_from(rep("00", 32))
check("mnemonic 24 words", len(words0) == 24)
check("mnemonic BIP-39 vector", words0[0] == "abandon" && words0[22] == "abandon" && words0[23] == "art")
bind mseed = lw_random(32)
bind mw = lw_mnemonic_from(mseed)
bind phrase = ""
bind wi = 0
while wi < 24 {
    if wi == 0 { bind phrase = mw[wi] } else { bind phrase = phrase + " " + mw[wi] }
    bind wi = wi + 1
}
check("mnemonic roundtrip", val(lw_mnemonic_to(phrase)) == mseed)
check("mnemonic tolerant of case/space", val(lw_mnemonic_to("  " + str_upper(phrase) + "\n")) == mseed)
bind zero_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
check("mnemonic_to vector", val(lw_mnemonic_to(zero_phrase)) == rep("00", 32))
check("mnemonic bad checksum", is_bad(lw_mnemonic_to("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon")))
check("mnemonic unknown word", is_bad(lw_mnemonic_to("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon lingx")))
check("mnemonic 12 words rejected", is_bad(lw_mnemonic_to("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")))

# ════════════════════════════════════════════════════════════ §3 PoW
print("── §3 HyperTorus proof-of-work")
bind tp0 = time_now()
bind kat = lw_pow_hash(text_hex("LW1/pow/kat/0"))
bind tp1 = time_now()
check("pow KAT LW1/pow/kat/0", kat == "832df7038206cf1ba944c6ed24a74ae9e433d9c7fdb46ca502c2cf27831c048e")
bind hdr0 = hex_concat([text_hex("LW1 test header "), u64le_hex(0)])
bind tp2 = time_now()
check("pow KAT test header 0", lw_pow_hash(hdr0) == "dbfca3b3a03212d933c300f027cbd651b2ca6e710abf8b59cc16c5561ed76050")
bind tp3 = time_now()
print(format("TIME pow hash: first (alloc+hash) {} ms, reused scratchpad {} ms", floor((tp1 - tp0) * 1000), floor((tp3 - tp2) * 1000)))
check("pow reuse deterministic", lw_pow_hash(text_hex("LW1/pow/kat/0")) == kat)
bind tr = lw_pow_trace(text_hex("LW1/pow/kat/0"), 65536)
check("pow trace hash == pow hash", tr[0] == kat)
check("pow trace points", len(tr[1]) == 4 && len(tr[1][0]) == 5)
check("pow trace every 0", len(lw_pow_trace(hdr0, 0)[1]) == 0)

bind H0 = rep("00", 32)
bind HF = rep("ff", 32)
check("difficulty 0 never passes", !lw_meets_difficulty(H0, "0") && !lw_meets_difficulty(HF, "0"))
check("difficulty 1 always passes", lw_meets_difficulty(H0, "1") && lw_meets_difficulty(HF, "1"))
check("difficulty 2: 2^255-1 passes", lw_meets_difficulty(hex_concat([rep("ff", 31), "7f"]), "2"))
check("difficulty 2: 2^255 fails", !lw_meets_difficulty(hex_concat([rep("00", 31), "80"]), "2"))
check("difficulty u128 max, zero hash", lw_meets_difficulty(H0, "340282366920938463463374607431768211455"))
check("difficulty u128 max threshold", lw_meets_difficulty(hex_concat(["01", rep("00", 15), "01", rep("00", 15)]), "340282366920938463463374607431768211455") && !lw_meets_difficulty(hex_concat(["02", rep("00", 15), "01", rep("00", 15)]), "340282366920938463463374607431768211455"))
check("difficulty 3 threshold", lw_meets_difficulty(rep("55", 32), "3") && !lw_meets_difficulty(hex_concat(["56", rep("55", 31)]), "3"))
check("difficulty as number", lw_meets_difficulty(H0, 600))

# ════════════════════════════════════════════════════════════ §3 QR
print("── §3 QR")
bind qr = lw_qr("lng1abcdefghijklmnopqrstuvwxyz234567")
bind qn = len(qr)
check("qr square", qn >= 21 && len(qr[0]) == qn && len(qr[qn - 1]) == qn)
check("qr version size", qn - 21 - 4 * floor((qn - 21) / 4) == 0)
check("qr finder pattern", hex_slice(text_hex(qr[0]), 0, 7) == text_hex("1111111") && hex_slice(text_hex(qr[1]), 0, 7) == text_hex("1000001"))

lw_key_drop(kh)
lw_key_drop(kh2)
lw_key_drop(ih)
lw_key_drop(ih2)
lw_key_drop(sh)
lw_key_drop(oth)
lw_key_drop(oth_b)
lw_key_drop(oth6)
print(format("TIME total {} s", floor((time_now() - T0) * 10) / 10))
}

← Back to index