灵符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/ui/security.ling
# security.ling — SECURITY: password-gated recovery phrase (hides itself),
# password change, auto-lock timer and keyboard shortcuts.

bind REVEAL_SECS = 60

fn sec_wipe() {
    st_set("sec_words", [])
    st_set("sec_reveal_t", 0 - 1)
    tf_clear("sec_pw")
    tf_clear("sec_old")
    tf_clear("sec_new")
    tf_clear("sec_new2")
    st_set("sec_err", "")
    st_set("sec_msg", "")
}
fn sec_hide() {
    st_set("sec_words", [])
    st_set("sec_reveal_t", 0 - 1)
}

# Numbered placeholder grid: hatched bars instead of words.
fn veiled_grid(x, y, w) {
    bind gap = 8
    bind cw = floor((w - 3 * gap) / 4)
    bind i = 0
    while i < 24 {
        bind cx = x + (i % 4) * (cw + gap)
        bind cy = y + floor(i / 4) * 48
        fill(cx, cy, cw, 40, C_PANEL)
        outline(cx, cy, cw, 40, C_LINE_FAINT, 1)
        t_center(F_MONO, 18, cx, cy, 30, 40, pad2(i + 1), C_TEXT_FAINT)
        bind bw = 40 + ((i * 37) % 5) * 10
        cross(cx + 42, cy + 15, min(bw, cw - 54), 10, C_LINE, 3, 0.8)
        bind i = i + 1
    }
}

fn security_draw(ax, ay, aw, ah) {
    bind lw = floor(aw * 52 / 100)
    bind rec_h = 404
    bind inn = panel(ax, ay, lw, rec_h, "RECOVERY PHRASE", C_VIOLET)
    bind x = list_get(inn, 0)
    bind y = list_get(inn, 1)
    bind iw = list_get(inn, 2)
    bind gy = y + 8
    bind gh = 6 * 48 - 8
    bind t0 = st_get("sec_reveal_t", 0 - 1)
    if t0 >= 0 && u_now() - t0 > REVEAL_SECS && st_get("shot", 0) < 1 { sec_hide() }
    bind words = st_get("sec_words", [])
    if len(words) > 0 {
        word_grid(x, gy, iw, words, C_VIOLET)
        bind left = max(0, REVEAL_SECS - (u_now() - st_get("sec_reveal_t", 0)))
        if st_get("shot", 0) > 0 { bind left = REVEAL_SECS }
        bind by = gy + gh + 24
        bind bw = iw - 160
        fill(x, by + 4, bw, 6, C_LINE_FAINT)
        fill(x, by + 4, floor(bw * left / REVEAL_SECS), 6, C_VIOLET)
        t_text(F_UI, 15, x, by + 32, "Hides automatically in " + num_str(ceil(left)) + " s", C_TEXT_DIM)
        if button(x + iw - 140, by - 8, 140, 40, "Hide now", C_VIOLET, "plain", true, true, "") { sec_hide() }
    } else {
        veiled_grid(x, gy, iw)
        bind ox = x + 40
        bind ow = iw - 80
        bind oy = gy + floor(gh / 2) - 64
        fill(ox, oy, ow, 128, C_PANEL)
        outline(ox, oy, ow, 128, C_LINE_SOFT, 1)
        brackets(ox, oy, ow, 128, 8, C_VIOLET, 1)
        icon_draw("eye", ox + 20, oy + 26, C_VIOLET)
        t_text(F_UI, 18, ox + 48, oy + 32, "Hidden. Enter your password to reveal.", C_TEXT_STRONG)
        bind fw = ow - 40 - 132
        bind r = field("sec_pw", ox + 20, oy + 60, fw, 44, "password", C_VIOLET, "password", 0)
        if list_get(r, 0) { st_set("sec_err", "") }
        if button(ox + 20 + fw + 12, oy + 60, 120, 44, "Reveal", C_VIOLET, "primary", true, false, "") || list_get(r, 1) {
            bind res = be_reveal(tf_get("sec_pw"))
            match res {
                ok(ws) => sec_revealed(ws),
                bad(e) => sec_reveal_bad(e),
            }
        }
        bind by = gy + gh + 24
        if st_get("sec_err", "") != "" {
            t_text(F_UI, 16, x, by + 18, st_get("sec_err", ""), C_RED)
        } else {
            t_text(F_UI, 15, x, by + 18, "Make sure no one can see your screen. The words hide after 60 s.", C_TEXT_FAINT)
        }
    }

    bind py = ay + rec_h + 32
    bind ph = ah - rec_h - 32
    bind inp = panel(ax, py, lw, ph, "CHANGE PASSWORD", C_VIOLET)
    bind x = list_get(inp, 0)
    bind y = list_get(inp, 1) + 28
    bind iw = list_get(inp, 2)
    bind fw = floor((iw - 32) / 3)
    bind a = labeled("sec_old", x, y, fw, 44, "Current", "", C_VIOLET, "password")
    bind b = labeled("sec_new", x + fw + 16, y, fw, 44, "New", "", C_VIOLET, "password")
    bind c = labeled("sec_new2", x + 2 * (fw + 16), y, fw, 44, "Confirm new", "", C_VIOLET, "password")
    if list_get(a, 0) || list_get(b, 0) || list_get(c, 0) { st_set("sec_msg", "") }
    strength_meter(x + fw + 16, y + 60, 2 * fw + 16, 8, tf_get("sec_new"))
    bind by = py + ph - 20 - 40
    bind mism = tf_get("sec_new2") != "" && tf_get("sec_new") != tf_get("sec_new2")
    bind good = tf_get("sec_old") != "" && pw_strength(tf_get("sec_new")) >= 1 && !mism && tf_get("sec_new2") != ""
    bind mw = iw - 216
    bind msg = st_get("sec_msg", "")
    if msg != "" {
        t_para(F_UI, 16, x, by + 4, mw, 19, msg, sel(st_get("sec_msg_ok", 0) > 0, C_GREEN, C_RED))
    } else if mism {
        t_para(F_UI, 16, x, by + 4, mw, 19, "New passwords don't match.", C_RED)
    } else {
        t_para(F_UI, 15, x, by + 2, mw, 19, "Re-encrypts the wallet file. Your recovery phrase stays the same.", C_TEXT_FAINT)
    }
    bind submit = list_get(a, 1) || list_get(b, 1) || list_get(c, 1)
    if button(x + iw - 200, by, 200, 40, "Change password", C_VIOLET, "plain", good, false, "") || (submit && good) {
        bind res = be_change_password(tf_get("sec_old"), tf_get("sec_new"))
        match res {
            ok(v) => sec_pw_changed(),
            bad(e) => sec_pw_msg(e, 0),
        }
    }

    bind rx = ax + lw + 24
    bind rw = aw - lw - 24
    bind ina = panel(rx, ay, rw, 132, "AUTO LOCK", C_VIOLET)
    bind x = list_get(ina, 0)
    bind y = list_get(ina, 1)
    t_text(F_UI, 16, x, y + 10, "Lock after this long without input:", C_TEXT_DIM)
    bind pick = segmented(x, y + 28, list_get(ina, 2), ["1 min", "5 min", "10 min", "30 min", "Never"], st_get("autolock", 2), C_VIOLET)
    if pick >= 0 { st_set("autolock", pick) }

    bind ky = ay + 132 + 32
    bind ink = panel(rx, ky, rw, ah - 132 - 32, "SHORTCUTS", C_VIOLET)
    bind x = list_get(ink, 0)
    bind y = list_get(ink, 1) + 4
    bind iw = list_get(ink, 2)
    bind keys = ["Ctrl+1 … 7", "Ctrl+L", "Tab / Shift+Tab", "Enter", "Esc", "Ctrl+V", "Ctrl+A"]
    bind what = ["Switch section", "Lock now", "Next / previous field", "Confirm the focused step", "Go back a step", "Paste into a field", "Select a field's text"]
    bind i = 0
    while i < len(keys) {
        t_text(F_MONO, 20, x, y + 20, list_get(keys, i), C_TEXT_STRONG)
        t_right(F_UI, 16, x + iw, y + 20, list_get(what, i), C_TEXT_DIM)
        hline(x, y + 32, iw, C_LINE_FAINT, 1)
        bind y = y + 40
        bind i = i + 1
    }
}

fn sec_revealed(ws) {
    st_set("sec_words", ws)
    st_set("sec_reveal_t", u_now())
    tf_clear("sec_pw")
}
fn sec_reveal_bad(e) {
    st_set("sec_err", e)
    st_set("tf_all", "sec_pw")
}
fn sec_pw_changed() {
    tf_clear("sec_old")
    tf_clear("sec_new")
    tf_clear("sec_new2")
    sec_pw_msg("Password changed.", 1)
}
fn sec_pw_msg(m, good) {
    st_set("sec_msg", m)
    st_set("sec_msg_ok", good)
}

← Back to index