灵符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/main.ling
# Ling Wallet — desktop UI.
# Run:   lingfu run                  (from the repo root; live node, on the JIT)
#        lingfu run --mock           (simulated backend)
#        ling run --interp main.ling (the tree-walking interpreter)
# Shots: lingfu run --mock --screenshot out.png --screen wallet [--frames N] [--prof]

use "backend.ling"
use "ui/theme.ling"
use "ui/gfx.ling"
use "ui/input.ling"
use "ui/widgets.ling"
use "ui/icons.ling"
use "ui/qr.ling"
use "ui/torus.ling"
use "ui/shell.ling"
use "ui/onboard.ling"
use "ui/wallet.ling"
use "ui/send.ling"
use "ui/receive.ling"
use "ui/mining.ling"
use "ui/network.ling"
use "ui/security.ling"
use "ui/about.ling"

bind SHOT_PW = "correct-horse-42"

fn set_rate(fps) {
    if st_get("fps", 0) != fps {
        set_fps(fps)
        st_set("fps", fps)
    }
}

# Screenshot mode: put the app into the named state.
fn shot_setup(name) {
    st_set("shot", 1)
    st_set("shot_done", 0)
    bind needs_wallet = name != "onboarding" && name != "create" && name != "restore"
    if needs_wallet && name != "words" && name != "verify" { be_create(SHOT_PW) }
    if name == "create" {
        st_set("ob_step", "password")
        tf_set("ob_pw", "correct horse battery 42")
    }
    if name == "words" || name == "verify" {
        bind r = be_create(SHOT_PW)
        match r {
            ok(ws) => ob_created(ws),
            bad(e) => println(e),
        }
        st_set("ob_ask", [2, 11, 19])
    }
    if name == "verify" {
        st_set("ob_step", "verify")
        tf_set("ob_a0", "lattice")
    }
    if name == "restore" {
        st_set("ob_step", "restore")
        tf_set("rs0", "orbit")
        tf_set("rs1", "candy")
    }
    if name == "unlock" {
        be_lock()
        tf_set("unlock_pw", "wrongpass")
        st_set("unlock_err", "Wrong password.")
    }
    bind sec = index_of(["wallet", "send", "receive", "mining", "network", "security", "about"], name)
    if sec >= 0 { st_set("section", sec) }
    if str_starts_with(name, "send") {
        st_set("section", 1)
        tf_set("send_to", "lng1q8w3x5ffm2kzv7d4hn3j9t0yqe6c2rplx4sa8gu")
        tf_set("send_amt", "12.5")
        tf_set("send_memo", "invoice 2291")
    }
    if str_starts_with(name, "security") { st_set("section", 5) }
    if name == "security-revealed" {
        bind r2 = be_reveal(SHOT_PW)
        match r2 {
            ok(ws) => sec_revealed(ws),
            bad(e) => println(e),
        }
    }
}

# Steps that need time to pass first (lookups, signing).
fn shot_step(name, el) {
    if st_get("shot_step", 0) > 0 { return () }
    if str_starts_with(name, "send-") && el > 1.4 {
        st_set("shot_step", 1)
        bind to = tf_get("send_to")
        bind r = be_prepare(to, parse_lng("12.5"), "invoice 2291")
        match r {
            ok(p) => send_prepared(p),
            bad(e) => println(e),
        }
        if name == "send-authorize" {
            send_go("authorize")
            tf_set("send_pw", "correct-horse")
        }
        if name == "send-sent" {
            bind p2 = st_get("send_p", ())
            be_confirm(p2.id, SHOT_PW)
            send_signed()
        }
    }
}
fn shot_min_time(name) {
    if name == "send-sent" { return 3.6 }
    if str_starts_with(name, "send") { return 2.0 }
    if name == "mining" || name == "onboarding" { return 2.0 }
    return 0.6
}

bind start = do {
    # screenshot mode types a published password and renders the seed words: it only ever runs on the mock backend
    if cli_arg("screenshot", "") != "" { st_set("be_mock", 1) }
    be_init()
    open_window(1200, 780, "Ling Wallet", 1)
    px_flat()
    if !fonts_load() { println("fonts missing; run from the ling/ folder") }
    st_set("autolock", 2)
    st_set("section", 0)
    st_set("autofocus", 1)
    st_set("activity_t", 0)
    send_reset()
    sec_wipe()
    tz_clear("tz")
    tz_clear("deco")
    bind shot_out = cli_arg("screenshot", "")
    bind shot_name = cli_arg("screen", "wallet")
    bind shot_frames = to_number(cli_arg("frames", "20"))
    bind prof = index_of(get_args(), "--prof") >= 0
    bind t0 = time_now()
    if shot_out != "" { shot_setup(shot_name) }
    bind frame = 0
    bind last_draw = 0 - 10
    bind running = true
    set_rate(60)
    while window_is_open() && running {
        bind t = time_now()
        bind input = u_begin(t)
        be_tick()
        if shot_out != "" { shot_step(shot_name, t - t0) }
        bind idle_for = t - st_get("activity_t", 0)
        bind redraw = input || animating() || toast_live() || t - last_draw > 0.5 || shot_out != ""
        if focus_get() != "" && t - last_draw > 0.3 { bind redraw = true }
        if redraw {
            app_frame()
            u_end()
            bind last_draw = t
            if prof {
                st_add("prof_ms", (time_now() - t) * 1000)
                st_add("prof_n", 1)
                if st_get("prof_n", 0) >= 60 {
                    println("draw ms avg: ", round(st_get("prof_ms", 0) / st_get("prof_n", 1) * 10) / 10)
                    st_set("prof_ms", 0)
                    st_set("prof_n", 0)
                }
            }
        }
        if shot_out != "" {
            set_rate(60)
        } else if idle_for < 1.5 || focus_get() != "" {
            # a focused field keeps the loop polling keys at full rate; idle
            # frames skip drawing, so this costs little
            set_rate(60)
        } else if animating() {
            set_rate(30)
        } else {
            set_rate(10)
        }
        present()
        bind frame = frame + 1
        if shot_out != "" && frame >= shot_frames && t - t0 >= shot_min_time(shot_name) {
            bind path = screenshot(shot_name)
            if file_exists(shot_out) { remove_file(shot_out) }
            rename_file(path, shot_out)
            println("saved ", shot_out)
            bind running = false
        }
    }
}

← Back to index