L
ling-wallet
↓ 0 downloadsLing Wallet: a post-quantum privacy-coin wallet, full node and HyperTorus miner for LNG
Keywords wallet, cryptocurrency, lng, node, miner, ling
lingfu install ling-walletCode / 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") }
}