tawasuyu

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 229TOML 101Markdown 82Shell 24052 Definiciones

⎇ main llimphi-wasm-net/tests/p2p.rs Rust · 133 líneas · 5.5 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1//! Certifica la distribución sobre red real: dos peers libp2p (TCP+Noise+Yamux)
2//! en proceso. B sirve el counter.wasm desde un DiskStore; A lo pide por hash,
3//! `llimphi-wasm-dist` verifica la integridad del blob recibido y la app corre.
4//!
5//! Mismo harness que minga (`libp2p_integration.rs`): listen + dial + open_stream
6//! con reintentos. Determinista (provider explícito, sin depender de timing DHT).
7
8use std::sync::Arc;
9
10use agora_core::Keypair;
11use card_net::BrahmanNet;
12use format::{ConcesionCapacidad, PERMISO_RED};
13use llimphi_wasm_core::{
14    bytecode_hash, resolve, resolve_manifest, verify_integrity, AppManifest, AppRef, BlobSource,
15    DiskStore, Hash, MapSource, TrustRing,
16};
17use llimphi_wasm_net::{fetch_blob, serve_blobs};
18use llimphi_wasm_runner::{EventPayload, WasmGuest};
19
20const COUNTER_WASM: &[u8] = include_bytes!("../../llimphi-wasm-runner/assets/counter.wasm");
21
22/// Source de un solo blob ya en mano — para pasar los bytes de red por la
23/// verificación de `dist::resolve` sin volver a la red.
24struct OneShot(Vec<u8>);
25impl BlobSource for OneShot {
26    fn fetch(&self, _hash: &Hash) -> Option<Vec<u8>> {
27        Some(self.0.clone())
28    }
29}
30
31#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
32async fn dos_peers_distribuyen_y_corren_la_app() {
33    // Dos nodos independientes (keypair efímera, peer_id nuevo).
34    let node_a = BrahmanNet::new().expect("nodo A");
35    let node_b = BrahmanNet::new().expect("nodo B");
36    let peer_b = node_b.peer_id;
37
38    // B inscribe el counter.wasm en su store y levanta el responder de blobs.
39    let dir_b = std::env::temp_dir().join("llimphi-wasm-net-srv");
40    let _ = std::fs::remove_dir_all(&dir_b);
41    let store_b = Arc::new(DiskStore::open(&dir_b).expect("store B"));
42    let hash = store_b.put(COUNTER_WASM).expect("put");
43    assert_eq!(hash, bytecode_hash(COUNTER_WASM));
44    let _srv = serve_blobs(&node_b, Arc::clone(&store_b));
45
46    // B escucha en localhost; A dializa la dirección resuelta.
47    let addr_b = node_b
48        .listen("/ip4/127.0.0.1/tcp/0".parse().unwrap())
49        .await;
50    node_a.dial(addr_b);
51
52    // A pide el bytecode por hash sobre la red real.
53    let bytes = fetch_blob(&node_a, peer_b, &hash)
54        .await
55        .expect("fetch del blob");
56
57    // Integridad: lo recibido rehashea al hash pedido (un provider malicioso no
58    // puede colar otro wasm — la red no es de fiar, el hash sí).
59    assert!(verify_integrity(&bytes, &hash));
60    assert_eq!(bytes, COUNTER_WASM);
61
62    // La cadena de dist verifica los bytes de RED y entrega una VerifiedApp...
63    let verified = resolve(&OneShot(bytes), &TrustRing::empty(), &AppRef::pure(hash))
64        .expect("resolve verifica el blob de red");
65    assert_eq!(verified.permisos, 0);
66
67    // ...y la app distribuida por la red CORRE: incrementa de verdad.
68    let mut guest = WasmGuest::load(&verified.wasm, verified.permisos).expect("carga el guest");
69    let n0 = guest.view().children[0].text.as_ref().unwrap().content.clone();
70    assert_eq!(n0, "0");
71    guest.dispatch(0, EventPayload::Click).unwrap(); // Msg::Increment
72    let n1 = guest.view().children[0].text.as_ref().unwrap().content.clone();
73    assert_eq!(n1, "1", "la app traída por la red P2P incrementa");
74}
75
76/// Descubrimiento de concesiones end-to-end sobre la red: B sirve el bytecode
77/// **y** su concesión firmada; A los trae AMBOS por hash, resuelve el manifiesto
78/// y la app corre con permisos reales (no 0).
79#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
80async fn dos_peers_distribuyen_app_con_concesion() {
81    let node_a = BrahmanNet::new().expect("nodo A");
82    let node_b = BrahmanNet::new().expect("nodo B");
83    let peer_b = node_b.peer_id;
84
85    // B inscribe el wasm y una concesión firmada (PERMISO_RED) para ese bytecode.
86    let dir_b = std::env::temp_dir().join("llimphi-wasm-net-grant-srv");
87    let _ = std::fs::remove_dir_all(&dir_b);
88    let store_b = Arc::new(DiskStore::open(&dir_b).expect("store B"));
89    let bc = store_b.put(COUNTER_WASM).expect("put wasm");
90    let kp = Keypair::from_seed([7; 32]);
91    let mensaje = format::mensaje_capacidad(&bc, PERMISO_RED);
92    let grant = ConcesionCapacidad {
93        bytecode: bc,
94        permisos: PERMISO_RED,
95        autor: kp.public_key(),
96        firma: kp.sign(&mensaje),
97    };
98    let gh = store_b.put_grant(&grant).expect("put grant");
99    let _srv = serve_blobs(&node_b, Arc::clone(&store_b));
100
101    let addr_b = node_b
102        .listen("/ip4/127.0.0.1/tcp/0".parse().unwrap())
103        .await;
104    node_a.dial(addr_b);
105
106    // A trae los DOS blobs por hash sobre la red real.
107    let wasm = fetch_blob(&node_a, peer_b, &bc).await.expect("fetch wasm");
108    let grant_blob = fetch_blob(&node_a, peer_b, &gh).await.expect("fetch grant");
109
110    // Los junta y resuelve el manifiesto: descubrimiento de concesión completo.
111    let mut src = MapSource::new();
112    src.insert(bc, wasm);
113    src.insert(gh, grant_blob);
114    let manifest = AppManifest {
115        bytecode: bc,
116        declarados: PERMISO_RED,
117        concesion: Some(gh),
118    };
119    let trust = TrustRing::new(vec![kp.public_key()]);
120    let verified = resolve_manifest(&src, &trust, &manifest).expect("resolve_manifest");
121    assert_eq!(
122        verified.permisos, PERMISO_RED,
123        "la app traída por la red corre con permisos REALES, no 0"
124    );
125
126    // Y carga con ese permiso (que gatea host_net_request) y corre.
127    let mut guest = WasmGuest::load(&verified.wasm, verified.permisos).expect("carga con permisos");
128    guest.dispatch(0, EventPayload::Click).unwrap();
129    assert_eq!(
130        guest.view().children[0].text.as_ref().unwrap().content,
131        "1"
132    );
133}