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/escrowtest.ling
# escrowtest.ling — end-to-end LNG escrow against a running ling-market.
#
# Plays the buyer (and, via the API, the vendor): mine until coins mature,
# set the vendor's payout address, place an order, pay it with the order
# memo, wait for the market to mark it funded, ship, confirm, and check the
# vendor payout arrives. Start the market first (see ../../ling-market):
#
# ling run market_server.ling --port 7795 --db T/market.db --lng-dir T/lng
# --lng-listen 127.0.0.1:18891 --lng-peer 127.0.0.1:18890
# --lng-test-difficulty 1 --lng-test-target 2
# ling run --interp escrowtest.ling --data-dir T/buyer --fast-kdf 1
# --listen 127.0.0.1:18890 --peer 127.0.0.1:18891
# --market-port 7795 --market-log T/server.log
use "api.ling"
use "real_backend.ling"
fn check(name, cond) {
if cond {
print("PASS ", name)
st_add("pass", 1)
} else {
print("FAIL ", name)
st_add("fail", 1)
}
return cond
}
fn pump(secs) {
bind deadline = epoch_now() + secs
while epoch_now() < deadline {
real_tick()
sleep(50)
}
return 0
}
fn json_str(json, key) {
bind marker = format("\"{}\":\"", key)
bind start = str_find(json, marker)
if start < 0 {
return ""
}
bind rest = substr(json, start + len(marker))
return substr(rest, 0, str_find(rest, "\""))
}
fn json_num(json, key) {
bind marker = format("\"{}\":", key)
bind start = str_find(json, marker)
if start < 0 {
return -1
}
bind rest = substr(json, start + len(marker))
bind end = str_find(rest, ",")
if end < 0 {
bind end = str_find(rest, "}")
}
return to_number(substr(rest, 0, end))
}
fn market(path) {
return lingtp_get("127.0.0.1", to_number(cli_arg("market-port", "7795")), path)
}
fn market_post(path, body) {
return lingtp_post("127.0.0.1", to_number(cli_arg("market-port", "7795")), path, body)
}
# The seeded vendor's TOTP secret, from the market's startup log.
fn vendor_secret() {
bind log = read_file(cli_arg("market-log", "server.log"))
bind at = str_find(log, "sweettooth/")
bind rest = substr(log, at)
bind s = str_find(rest, "totp secret=")
return trim(substr(rest, s + 12, 32))
}
fn order_status(id) {
return json_str(market(format("/api/order/get?order_id={}", id)), "status")
}
fn wait_status(id, want, secs) {
bind deadline = epoch_now() + secs
while epoch_now() < deadline && order_status(id) != want {
pump(2)
}
return order_status(id) == want
}
bind start = do {
shared_put("test_genesis_difficulty", "1")
shared_put("test_target_secs", "2")
real_init()
pump(2)
check("buyer wallet created", match real_create("buyer password 1") { ok(w) => true, bad(e) => false, })
pump(2)
bind me = real_summary().x.short_address
# Mine until some coinbases mature (60 blocks).
real_set_mining(true, 2, 100, false)
bind t0 = epoch_now()
bind deadline = epoch_now() + 900
while epoch_now() < deadline && real_summary().x.unlocked < 10 * COIN {
pump(2)
}
print("mined to height ", st_get("n_height", 0), " in ", floor(epoch_now() - t0), " s")
check("buyer has spendable LNG", real_summary().x.unlocked >= 10 * COIN)
# Vendor: log in with 2FA, point the shop's payouts at our address.
bind login = market_post("/api/vendor/login", format("username=sweettooth&password=NebulaGummyBears99!&code={}", totp_now(vendor_secret())))
bind vtoken = json_str(login, "token")
check("vendor logs in with 2FA", vtoken != "")
bind upd = market_post("/api/shop/update", format("token={}&shop_id=1&payout_address={}", vtoken, me))
check("vendor sets an LNG payout address", str_find(upd, "\"ok\":true") >= 0)
check("invalid payout address rejected", str_find(market_post("/api/shop/update", format("token={}&shop_id=1&payout_address=lng1nope", vtoken)), "\"ok\":false") >= 0)
# Buyer: place an order.
check("order without refund address rejected", str_find(market_post("/api/order/create", "product_id=1&quantity=1"), "refund_address") >= 0)
bind order = market_post("/api/order/create", format("product_id=1&quantity=2&refund_address={}", me))
bind oid = json_num(order, "order_id")
bind btoken = json_str(order, "buyer_token")
bind pay_to = json_str(order, "escrow_address")
bind memo = json_str(order, "payment_memo")
bind total = floor(to_number(json_str(order, "total_amount")) * COIN + 0.5)
check("order placed with escrow address and memo", oid > 0 && starts_with(pay_to, "lng1") && starts_with(memo, "order:"))
# Pay it.
bind lk = ["pending"]
bind deadline = epoch_now() + 90
while list_get(lk, 0) == "pending" && epoch_now() < deadline {
bind lk = real_lookup(pay_to)
pump(0.5)
}
check("market escrow address verified over the network", list_get(lk, 0) == "verified")
bind prep = real_prepare(pay_to, total, memo)
check("payment prepared", match prep { ok(p) => true, bad(e) => false, })
check("payment authorized", match real_confirm(prep.x.id, "buyer password 1") { ok(v) => true, bad(e) => false, })
bind deadline = epoch_now() + 60
while list_get(real_progress(prep.x.id), 0) == "signing" && epoch_now() < deadline {
pump(0.5)
}
check("payment sent", list_get(real_progress(prep.x.id), 0) == "sent")
check("market marks the order funded", wait_status(oid, "funded", 300))
# Vendor ships, buyer confirms → vendor payout.
check("vendor ships", str_find(market_post("/api/order/ship", format("token={}&order_id={}¬e=on its way", vtoken, oid)), "\"ok\":true") >= 0)
check("buyer confirms receipt", str_find(market_post("/api/order/confirm", format("order_id={}&buyer_token={}", oid, btoken)), "\"ok\":true") >= 0)
# The payout (memo order:<id>) reaches the vendor's address — ours.
bind want = format("order:{}", oid)
bind got = 0
bind deadline = epoch_now() + 600
while got == 0 && epoch_now() < deadline {
pump(2)
bind h = real_history(100)
bind i = 0
while i < len(h) {
bind r = list_get(h, i)
if r.direction == "in" && r.memo == want {
bind got = r.amount
}
bind i = i + 1
}
}
print("payout received: ", got / COIN, " LNG of ", total / COIN, " LNG escrowed")
check("vendor payout arrived", got > 0)
check("payout is the total minus a small network fee", got <= total && got > total - COIN / 10)
real_set_mining(false, 1, 100, false)
print("")
print(format("{} passed, {} failed", st_get("pass", 0), st_get("fail", 0)))
quit()
}