灵符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/tools/seeds.ling
# seeds.ling — seed-list administration (what `lwd identity` / `lwd sign-seeds` used to do).
#
#   ling run ling/tools/seeds.ling identity --data-dir D
#       the identity public key of the node whose data folder is D (made now if that node has none yet);
#       this is what a seed list pins for that node
#   ling run ling/tools/seeds.ling sign --authority keys/seed-authority.key --out ling/assets/seeds.lwseeds
#                                       host:port=@path/node_identity.pub [host:port=IDENTITY_HEX ...]
#       a seed list signed by the seed authority; host may be a DNS name or an IP, the key given inline or as @file
#   ling run ling/tools/seeds.ling show ling/assets/seeds.lwseeds
#       what a seed list holds, and whether this build's authority signed it
#
# The wallet copies ling/assets/seeds.lwseeds into its data folder on start when it is newer (real_backend.ling).

use "../core/params.ling"

fn after_script() {
    bind all = get_args()
    bind i = 0
    bind at = 0 - 1
    while i < len(all) {
        if at < 0 && str_ends_with(list_get(all, i), "seeds.ling") { bind at = i }
        bind i = i + 1
    }
    bind out = []
    bind j = at + 1
    while j < len(all) {
        bind out = list_push(out, list_get(all, j))
        bind j = j + 1
    }
    return out
}

# value of --name in the args, or d
fn opt(args, name, d) {
    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 read_hex(path) {
    return match file_read_hex(path) {
        ok(v) => v,
        bad(e) => "",
    }
}

fn identity_public(seed_hex) {
    return match lw_id_keypair(seed_hex) {
        ok(k) => lw_id_public(k),
        bad(e) => "",
    }
}

fn cmd_identity(args) {
    bind dir = opt(args, "data-dir", "")
    if dir == "" {
        println("identity: --data-dir D is required")
        return 1
    }
    make_dir(dir)
    bind idfile = dir + "/node_identity.hex"
    if !file_exists(idfile) {
        file_write_hex(idfile, lw_random(32))
        println("(made a new node identity in ", idfile, ")")
    }
    bind pk = identity_public(read_hex(idfile))
    if pk == "" {
        println("identity: ", idfile, " is not a 32-byte identity seed")
        return 1
    }
    write_file(dir + "/node_identity.pub", pk)
    println(pk)
    println("(also in ", dir, "/node_identity.pub; sign with host:port=@", dir, "/node_identity.pub)")
    return 0
}

fn cmd_sign(args) {
    bind auth_path = opt(args, "authority", "")
    bind out = opt(args, "out", "")
    if auth_path == "" || out == "" {
        println("sign: --authority KEYFILE and --out FILE are required")
        return 1
    }
    bind auth = read_hex(auth_path)
    if str_len(auth) != 64 {
        println("sign: ", auth_path, " is not a 32-byte authority key")
        return 1
    }
    # the key must be the one this build trusts, or every wallet would reject the list
    if identity_public(auth) != seed_authority_hex() {
        println("sign: that key is not this network's seed authority (core/params.ling SEED_AUTHORITY_HEX)")
        return 1
    }
    bind entries = []
    bind i = 0
    while i < len(args) {
        bind a = list_get(args, i)
        if str_starts_with(a, "--") {
            bind i = i + 2
        } else {
            bind eq = str_find(a, "=")
            if eq > 0 {
                bind key = substr(a, eq + 1, str_len(a) - eq - 1)
                # =@file: the key from a node_identity.pub
                if str_starts_with(key, "@") { bind key = str_trim(read_file(substr(key, 1, str_len(key) - 1))) }
                bind entries = list_push(entries, [substr(a, 0, eq), key])
            }
            bind i = i + 1
        }
    }
    if len(entries) == 0 {
        println("sign: give at least one host:port=IDENTITY_HEX")
        return 1
    }
    bind list = p2p_seeds_sign(NET_ID, floor(epoch_now()), entries, auth)
    file_write_hex(out, list)
    println("signed ", len(entries), " seed(s) into ", out)
    return cmd_show_file(out)
}

fn cmd_show_file(path) {
    bind raw = read_hex(path)
    return match p2p_seeds_verify(raw, seed_authority_hex()) {
        ok(info) => show_info(info),
        bad(e) => show_bad(path, e),
    }
}
fn show_info(info) {
    println("network ", list_get(info, 0), sel_net(list_get(info, 0)), ", signed at unix ", list_get(info, 1))
    bind seeds = list_get(info, 2)
    bind i = 0
    while i < len(seeds) {
        bind s = list_get(seeds, i)
        println("  ", list_get(s, 0), "  identity ", substr(list_get(s, 1), 0, 16), "…")
        bind i = i + 1
    }
    return 0
}
fn sel_net(n) {
    if n == NET_ID { return " (this network)" }
    return " (NOT this network)"
}
fn show_bad(path, e) {
    println(path, ": ", e)
    return 1
}

bind start = do {
    bind args = after_script()
    bind cmd = ""
    if len(args) > 0 { bind cmd = list_get(args, 0) }
    bind code = 1
    if cmd == "identity" { bind code = cmd_identity(args) }
    if cmd == "sign" { bind code = cmd_sign(args) }
    if cmd == "show" && len(args) > 1 { bind code = cmd_show_file(list_get(args, 1)) }
    if cmd != "identity" && cmd != "sign" && cmd != "show" {
        println("usage: ling run ling/tools/seeds.ling identity|sign|show ...  (see the top of this file)")
    }
    if code != 0 { println("failed") }
}

← Back to index