灵符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/tests/nodes/t_nodes.ling
# Two real node isolates over the real LWP-1 network, one miner isolate:
# A mines, B must sync to A's tip and learn A's address bundle via
# Dandelion++ relay. Headless (no UI).

use "../../core/params.ling"
use "../../core/codec.ling"
use "../../core/chain.ling"
use "../../core/wallet.ling"

fn check(name, cond) {
    if cond {
        print("PASS ", name)
        st_add("pass", 1)
    } else {
        print("FAIL ", name)
        st_add("fail", 1)
    }
    return cond
}

# Drain a node's status channel; remember its latest height.
fn pump(tag, ch) {
    bind more = true
    while more {
        bind m = match chan_try_recv(ch) {
            ok(v) => v,
            bad(e) => (),
        }
        if m == () {
            bind more = false
        } else {
            if list_get(m, 0) == "node_status" {
                st_set(tag + "/height", list_get(m, 1))
                st_set(tag + "/peers", len(list_get(m, 5)))
                st_set(tag + "/hashes", st_get(tag + "/hashes", 0) + list_get(m, 9))
            }
            if list_get(m, 0) == "fatal" {
                print(tag, " fatal: ", list_get(m, 1))
            }
        }
    }
    return 0
}

fn wait_for(secs, what) {
    bind deadline = epoch_now() + secs
    bind done = false
    while done == false && epoch_now() < deadline {
        pump("a", st_get("ui_a_rx", ""))
        pump("b", st_get("ui_b_rx", ""))
        if what == "connected" {
            bind done = st_get("a/peers", 0) > 0 && st_get("b/peers", 0) > 0
        }
        if what == "mined" {
            bind done = st_get("a/height", 0) >= 4
        }
        if what == "synced" {
            bind done = st_get("b/height", 0) == st_get("a/height", -1) && st_get("a/height", 0) >= 4
        }
        sleep(100)
    }
    return done
}

bind start = do {
    shared_put("test_genesis_difficulty", "1")
    bind base = env_get("TEMP", ".") + "/lwtest-nodes-" + lw_random(4)
    bind port_a = 21000 + hex_u32le(lw_random(4), 0).x % 20000
    bind addr_a = format("127.0.0.1:{}", port_a)
    bind addr_b = format("127.0.0.1:{}", port_a + 1)

    bind ui_a = chan_new(1024)
    bind ui_b = chan_new(1024)
    st_set("ui_a_rx", list_get(ui_a, 1))
    st_set("ui_b_rx", list_get(ui_b, 1))
    bind in_a = chan_new(1024)
    bind in_b = chan_new(1024)
    bind wal_a = chan_new(64)
    bind wal_b = chan_new(64)
    bind mc = chan_new(16)

    bind miner = thread_spawn("../../isolates/miner.ling", "miner_main", [0, list_get(mc, 1), list_get(in_a, 0), list_get(ui_a, 0)], [])
    check("miner isolate spawned (no capabilities)", match miner { ok(h) => true, bad(e) => false, })
    bind na = thread_spawn("../../isolates/node.ling", "node_main", [base + "/a", list_get(in_a, 1), list_get(ui_a, 0), list_get(wal_a, 0), [list_get(mc, 0)], addr_a, [], ""], ["db", "fs", "p2p"])
    bind nb = thread_spawn("../../isolates/node.ling", "node_main", [base + "/b", list_get(in_b, 1), list_get(ui_b, 0), list_get(wal_b, 0), [], addr_b, [addr_a], ""], ["db", "fs", "p2p"])
    check("node isolates spawned", match na { ok(h) => true, bad(e) => false, })

    check("nodes connect over LWP-1", wait_for(30, "connected"))

    # Publish an address for A's coinbases, then mine.
    keys_load(lw_xof("test/miner", [], 32))
    bind bundle = st_get("k_bundle", "")
    bind bh = bundle_parse(bundle).x.hash
    chan_send(list_get(in_a, 0), ["publish_bundle", bundle])
    chan_send(list_get(in_a, 0), ["mining_cfg", 1, 1, 100, 0])
    bind t0 = epoch_now()
    check("A mines blocks", wait_for(180, "mined"))
    print("A at height ", st_get("a/height", 0), " after ", floor(epoch_now() - t0), " s")
    chan_send(list_get(in_a, 0), ["mining_cfg", 0, 1, 100, 0])
    check("B syncs to A's tip", wait_for(60, "synced"))
    check("miner reported hashes", st_get("a/hashes", 0) > 0)

    # B learned A's bundle through Dandelion++ relay or a direct lookup.
    chan_send(list_get(in_b, 0), ["lookup", bh])
    bind got = false
    bind deadline = epoch_now() + 60
    while got == false && epoch_now() < deadline {
        bind m = match chan_try_recv(list_get(wal_b, 1)) {
            ok(v) => v,
            bad(e) => (),
        }
        if m != () {
            if list_get(m, 0) == "bundle_arrived" {
                bind got = true
            }
        }
        if got == false {
            db_open(base + "/b/chain.db")
            bind got = bundle_get(bh) != ""
        }
        sleep(200)
    }
    check("B obtained A's verified address bundle", got)

    chan_send(list_get(mc, 0), ["stop"])
    chan_send(list_get(in_a, 0), ["shutdown"])
    chan_send(list_get(in_b, 0), ["shutdown"])
    thread_join_timeout(na.x, 15000)
    thread_join_timeout(nb.x, 15000)
    print("")
    print(format("{} passed, {} failed", st_get("pass", 0), st_get("fail", 0)))
}

← Back to index