124 lines
4.2 KiB
Python
124 lines
4.2 KiB
Python
"""
|
|
Quick prototype of Betanet layers L1, L2, L3 and L5 in an asyncio simulation.
|
|
|
|
Disclaimer: This is a simplified educational prototype. Cryptography is simulated using
|
|
hash/HMAC placeholders (not real Ed25519/X25519). Replace placeholders with real
|
|
crypto libraries (PyNaCl / cryptography) for production-quality code.
|
|
|
|
Mapping to spec:
|
|
- L1: Path and AS-hop validation (simple path object + signature checks)
|
|
- L2: HTX session with outer 'fingerprint' mirroring and inner Noise-like handshake
|
|
- L3: Overlay mesh with simple peerstore and content exchange (bitswap-like)
|
|
- L5: Self-certifying IDs and a tiny alias ledger with finality simulation
|
|
"""
|
|
|
|
import asyncio
|
|
import time
|
|
|
|
from src.alias_ledger import AliasLedger
|
|
from src.ashop import ASHop
|
|
from src.betanet_cryptography import KeyPair
|
|
from src.htx_frame import FT_CLOSE, FT_KEY_UPDATE, FT_PING, FT_STREAM
|
|
from src.htx_session import HTXSession
|
|
from src.overlay_node import OverlayNode
|
|
from src.path import L1Path, PathSegment
|
|
|
|
|
|
async def demo():
|
|
# Create AS keys for L1 hop validation
|
|
as_keys = {
|
|
"AS1": KeyPair(),
|
|
"AS2": KeyPair(),
|
|
"AS3": KeyPair(),
|
|
}
|
|
|
|
# Create overlay nodes (L3) with keys (these also act as service endpoints L5)
|
|
nodeA = OverlayNode("Alice", KeyPair())
|
|
nodeB = OverlayNode("Bob", KeyPair())
|
|
nodeC = OverlayNode("Carol", KeyPair())
|
|
|
|
# Connect peers (mesh)
|
|
nodeA.add_peer(nodeB)
|
|
nodeB.add_peer(nodeC)
|
|
nodeA.add_peer(nodeC)
|
|
|
|
# L5: register an alias for Carol
|
|
ledger = AliasLedger()
|
|
ledger.register_alias("carol.service", nodeC.kp, seq=1, exp=int(time.time()) + 3600)
|
|
|
|
# L1: Build a SCION-like path A -> AS1 -> AS2 -> C
|
|
hop1_sig = as_keys["AS1"].sign(b"AS1")
|
|
hop2_sig = as_keys["AS2"].sign(b"AS2")
|
|
segment = PathSegment([ASHop("AS1", hop1_sig), ASHop("AS2", hop2_sig)])
|
|
path = L1Path([segment])
|
|
assert path.validate(as_keys), "L1 path validation failed"
|
|
print("[L1] Path validated")
|
|
|
|
# L2: Setup HTX session between A and C (outer fingerprint mirrored)
|
|
fingerprint = "origin:example.com:ja3hash" # placeholder
|
|
sessionAC_local = HTXSession(nodeA.kp, nodeC.kp.pub, fingerprint)
|
|
sessionAC_remote = HTXSession(nodeC.kp, nodeA.kp.pub, fingerprint)
|
|
# link peers
|
|
sessionAC_local.peer = sessionAC_remote
|
|
sessionAC_remote.peer = sessionAC_local
|
|
|
|
await asyncio.gather(
|
|
sessionAC_local.perform_handshake(), sessionAC_remote.perform_handshake()
|
|
)
|
|
|
|
# Start receive loops
|
|
async def handler_a(ftype, sid, plain):
|
|
print(
|
|
f"[L2][A] Received frame type={ftype} sid={sid} payload={plain.decode(errors='ignore')}"
|
|
)
|
|
|
|
async def handler_c(ftype, sid, plain):
|
|
print(
|
|
f"[L2][C] Received frame type={ftype} sid={sid} payload={plain.decode(errors='ignore')}"
|
|
)
|
|
|
|
asyncio.create_task(sessionAC_local.recv_loop(handler_a))
|
|
asyncio.create_task(sessionAC_remote.recv_loop(handler_c))
|
|
|
|
# L3: publish content from Carol
|
|
cid = nodeC.publish(b"Hello from Carol's content!")
|
|
print(f"[L3] Carol published CID {cid[:16]}...")
|
|
|
|
# A resolves 'carol.service' via L5
|
|
resolved_pk_hex = ledger.resolve("carol.service")
|
|
if not resolved_pk_hex:
|
|
print("[L5] Alias resolution failed")
|
|
return
|
|
print(f"[L5] Resolved carol.service -> {resolved_pk_hex[:16]}...")
|
|
|
|
# A asks overlay for CID (bitswap)
|
|
content = await nodeA.bitswap_get(cid)
|
|
if content:
|
|
print("[L3] Alice retrieved content via overlay:", content.data)
|
|
else:
|
|
print("[L3] Alice failed to retrieve content from overlay")
|
|
|
|
# A sends a message to C over HTX inner frames (L2)
|
|
await sessionAC_local.send_frame(
|
|
FT_STREAM, 1, b"GET /resource HTTP/1.1\r\nHost: carol.service\r\n\r\n"
|
|
)
|
|
await asyncio.sleep(0.2)
|
|
|
|
# Simulate KEY_UPDATE
|
|
await sessionAC_local.send_frame(FT_KEY_UPDATE, None, b"KEY_UPDATE")
|
|
await asyncio.sleep(0.2)
|
|
|
|
# Send a ping
|
|
await sessionAC_local.send_frame(FT_PING, None, b"PING")
|
|
await asyncio.sleep(0.2)
|
|
|
|
# Close
|
|
await sessionAC_local.send_frame(FT_CLOSE, None, b"GOODBYE")
|
|
await asyncio.sleep(0.2)
|
|
|
|
print("\nDemo complete.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(demo())
|