灵符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/seednode.ling
# seednode.ling — a headless LNG node for a server: no window and no wallet. It keeps the chain, relays blocks and
# transactions, tells new nodes about the peers it knows, and serves the chain to wallets that are catching up.
#
#   ling run ling/seednode.ling --data-dir /srv/lng [--listen 0.0.0.0:18780] [--peer host:port ...]
#                               [--mine THREADS --pay-to lngkey1...|@file]
#
# Mining needs somewhere to pay the block reward: --pay-to takes a wallet's key bundle (Receive > Copy bundle, the
# lngkey1… text) or @a file holding it. The node remembers it, so later starts can leave --pay-to out.
#
# At start it writes its identity public key to <data-dir>/node_identity.pub: that is what a seed list pins for this node
# (ling run ling/tools/seeds.ling sign ... host:port=@<data-dir>/node_identity.pub). Open the listen port (TCP) in the
# firewall / router.
use "backend.ling"
use "core/wallet.ling"

fn arg_value(name, d) {
    bind args = get_args()
    bind i = 0
    while i + 1 < len(args) {
        if list_get(args, i) == "--" + name { return list_get(args, i + 1) }
        bind i = i + 1
    }
    return d
}

fn node_identity(data_dir) {
    make_dir(data_dir)
    bind idfile = data_dir + "/node_identity.hex"
    if !file_exists(idfile) { file_write_hex(idfile, lw_random(32)) }
    bind seed = match file_read_hex(idfile) {
        ok(v) => v,
        bad(e) => "",
    }
    bind pk = match lw_id_keypair(seed) {
        ok(k) => lw_id_public(k),
        bad(e) => "",
    }
    # the full key (2592 bytes) goes to a file the seed tool can read: host:port=@<data>/node_identity.pub
    if pk != "" { write_file(data_dir + "/node_identity.pub", pk) }
    return pk
}

fn pay_bad(e) {
    println("  --pay-to: ", e, " (expected the lngkey1… text from Receive > Copy bundle)")
    return false
}
fn sel_pay(pay) {
    if pay != "" { return "" }
    return " (to the address given before; never given one? add --pay-to, or nothing gets mined)"
}
# the hex an lngkey1… address carries
fn base64url_hex(s) {
    return match base64url_decode(substr(str_trim(s), 7, str_len(str_trim(s)) - 7)) {
        ok(h) => h,
        bad(e) => "",
    }
}

bind start = do {
    bind data_dir = arg_value("data-dir", default_data_dir())
    println("LNG seed node")
    println("  data:     ", data_dir)
    println("  listen:   ", arg_value("listen", "0.0.0.0:18780"))
    bind pk = node_identity(data_dir)
    println("  identity: ", substr(pk, 0, 32), "…  (full key: ", data_dir, "/node_identity.pub)")
    be_init()
    bind pay = arg_value("pay-to", "")
    if str_starts_with(pay, "@") { bind pay = str_trim(read_file(substr(pay, 1, str_len(pay) - 1))) }
    if pay != "" {
        bind ok_pay = match bundle_import(pay) {
            ok(bd) => true,
            bad(e) => pay_bad(e),
        }
        if ok_pay {
            chan_try_send(st_get("node_send", ""), ["publish_bundle", base64url_hex(pay)])
            println("  pays to:  ", substr(str_trim(pay), 0, 24), "…")
        }
    }
    bind threads = to_number(arg_value("mine", "0"))
    if threads > 0 {
        be_set_mining(true, threads, 100, false)
        println("  mining on ", threads, " thread(s)", sel_pay(pay))
    }
    bind t0 = epoch_now()
    bind last = 0 - 100
    while true {
        be_tick()
        bind t = epoch_now()
        if t - last >= 30 {
            bind last = t
            bind n = be_network()
            bind seeds = if n.seeds_verified { "seed list ok" } else { "no seed list" }
            println("up ", floor(t - t0), " s  height ", n.height, "  peers ", len(n.peers), "  mempool ", n.mempool, "  ", n.route, ", ", seeds)
        }
        sleep(200)
    }
}

← Back to index