Răsfoiți Sursa

Add gossip code, improve client, generally get things working more

Billy Barrow 2 ani în urmă
părinte
comite
46d2123e88

+ 4 - 26
README.md

@@ -3,32 +3,10 @@
 A system for finding application peers and resolving names over mesh-like networks
 
 TODO:
-- Verify CertifiedNameInfo against system trust
-- Create a NameInfoStore, along with a FilesystemNameInfoStore and maybe a memory one too
 - Build propogation mechanism
 - Add propogation rate limit
-- Build tooling for generating and propogating NameInfo
-- Peer auto-discovery using LAN multicast, known seed servers, and optionally Yggdrasil nodeinfo (although this may not actually be needed)
-- Riddle (callback) mode, removing `CHALLENGE` requests.
-- OpenPGP domains, in the form `[fingerprint].rns` i.e. "c3a6-5e46-7b54-77df-3c4c-9790-4d22-b3ca-5b32-ff66.rns"
+- Create a NameInfoStore, along with a FilesystemNameInfoStore and maybe a memory one too
+- Verify CertifiedNameInfo against system trust
 - Server class should implement (or inherit) a "Service" class, with another implementation being "DaemonClient" when a Riddle Daemon is implemented
-
-## Riddles v2
-- Sent with `RIDDLE` request, accepted with `OK`
-- Responded to with `CALLBACK` request
-- Callback accepted with `SOLVED` reply
-- `RIDDLE` arguments: group id, ttl, expiry, reply port
-- `RIDDLE` items: checksum/id, riddle public key, author public key, reply public key, riddle data
-- `CALLBACK` arguments: riddle checksum/id
-- `CALLBACK` items: answer data, connecion info public key
-- `SOLVED` arguments: none
-- `SOLVED` items: encrypted and signed IP/port data
-
-- Riddles are sent to peers who are in the correct group
-- Peers receive the riddle and then forward onto other peers in the group - holding on to the riddle ID and metadata
-- When a peer is reached that can solve the riddle, it encrypts its answer with the `reply public key` and then signes the encrypted answer with the private key derrived from the riddle matching the `riddle public key`.
-- Thet peer then sends a `CALLBACK` request to the peer it received the riddle from.
-- The peer servicing the `CALLBACK` request verifies the riddle with the public signature, and if correct forwards the request to the peer it received the riddle from.
-- The originating peer once receiving and verifying the `CALLBACK` request, encrypts its IP/Port information with the `connection info public key` and then signs the encrypted data using the `author private key` from the riddle. The peer then replys with `SOLVED` and the encrypted/signed data.
-- Intermediate peers verify the `SOLVED` reply by verifying the encrypted data against the `author public key` of the riddle before passing back the reply.
-- At any point in the chain, if a peer is not happy it can reply with `NOT-ACCEPTED` and codes in the 1xx range.
+- Build tooling for generating and propogating NameInfo.
+- OpenPGP domains, in the form `[fingerprint].rns` i.e. "c3a6-5e46-7b54-77df-3c4c-9790-4d22-b3ca-5b32-ff66.rns"

+ 12 - 2
src/infra/gen-domain/domain-gen.vala

@@ -2,17 +2,27 @@ using Riddle;
 
 public static int main(string[] args) {
 
+    var client = new Client();
+
     var name = args[1];
     var ip = args[2];
     var key = args.length > 3 ? args[3] : DecentralisedNameInfo.generate_key();
-    var expire = new DateTime.now_utc().add_days(90);
+    var expire = new DateTime.now_utc().add_days(1);
 
     var props = new Invercargill.Sequence<NameProperty>();
     props.add(new NameProperty(NamePropertyType.ADDRESS, ip, new string[0]));
 
     var info = new DecentralisedNameInfo(name, key, expire, props);
 
-    print(@"Domain generation summary:\n\tPrivate Key: \t$key\n\tDomain Name: \t$(info.name)\n\tName Info:\t$(info.get_encoded())\n");
+    print(@"Domain generation summary:\n\tPrivate Key: \t$key\n\tDomain Name: \t$(info.name)\n\n");
+
+    print("Propogating domain...\n");
+    var servers = 0;
+    while(servers == 0){
+        Thread.usleep(4000000);
+        servers = client.propogate(info);
+    }
+    print(@"Propogated domain to $servers Riddle server(s).\n");
 
     return 0;
 }

+ 21 - 2
src/infra/resolver/resolver.py

@@ -2,13 +2,32 @@ import dnslib.server
 import dnslib.dns
 import dnslib
 
+from gi.repository import Riddle
+
 class TestResolver(dnslib.server.BaseResolver):
+
+     def __init__(self):
+        self.client = Riddle.Client.new()   
+
      def resolve(self,request: dnslib.dns.DNSRecord,handler: dnslib.server.DNSHandler):
         print(type(request), type(handler))
         print(type(request.questions[0]))
-        print(request.questions[0])
+        domain = str(request.questions[0].get_qname())[:-1]
+        print(domain)
+
+        answers = self.client.who_is(domain).to_object_array()
+
+        print(len(answers))
         reply = request.reply()
-        reply.add_answer(*dnslib.RR.fromZone("abc.com 60 A 1.2.3.4"))
+        zone_data = set()
+        for answer in answers:
+                print(answer)
+                for zone in answer.to_zone_data():
+                        zone_data.add(zone)
+                        
+        for zone in zone_data:
+                reply.add_answer(*dnslib.RR.fromZone(zone))
+
         reply.header.rcode = getattr(dnslib.RCODE,'NXDOMAIN')
         return reply
 

+ 8 - 7
src/infra/server/server.vala

@@ -2,18 +2,19 @@ using Riddle;
 
 public static int main(string[] args) {
 
-    if(args.length != 2) {
-        printerr("Please specify port only\n");
+    if(args.length != 3) {
+        printerr("Please specify address and port only\n");
         return -1;
     }
 
-    var server = new Server((uint16)int.parse(args[1]));
+    var server = new Server(args[1], (uint16)int.parse(args[2]));
 
     var loop = new MainLoop();
-    server.run.begin((obj, res) => {
-            server.run.end(res);
-            loop.quit();
-        });
+    //  server.run.begin((obj, res) => {
+    //          server.run.end(res);
+    //          loop.quit();
+    //      });
+    server.start.begin();
     loop.run();
 
     return 0;

+ 54 - 40
src/lib/Client.vala

@@ -2,19 +2,33 @@ using Invercargill;
 
 namespace Riddle {
 
-    public class Client {
+    public class Client : Object {
 
-        private Invercargill.Sequence<PeerDiscoverer> discoverers;
-        private Invercargill.Sequence<InetSocketAddress> servers;
-        private InetSocketAddress? self_server;
-        private SocketClient socket_client;
+        private Invercargill.Sequence<PeerDiscoverer> discoverers = new Invercargill.Sequence<PeerDiscoverer> ();
+        private Invercargill.Sequence<InetSocketAddress> servers = new Invercargill.Sequence<InetSocketAddress>();
+        private Invercargill.Sequence<Message> join_messages = new Invercargill.Sequence<Message>();
+        private InetSocketAddress? self_server = null;
+
+        
 
         public Client() {
-            socket_client = new SocketClient();
-            discoverers = new Invercargill.Sequence<PeerDiscoverer> ();
             discoverers.add (new KnownHostDiscoverer());
             discoverers.add (new LanDiscoverer());
+            setup();
+        }
+
+        public Client.with_server(InetSocketAddress server_address) {
+            var lan_discoverer = new LanDiscoverer();
+            discoverers.add (new KnownHostDiscoverer());
+            discoverers.add (lan_discoverer);
+            self_server = server_address;
+
+            setup();
 
+            lan_discoverer.advertise(self_server);
+        }
+
+        private void setup() {
             foreach (var discoverer in discoverers) {
                 discoverer.peer_discovered.connect(new_server_found);
                 discoverer.begin();
@@ -32,36 +46,52 @@ namespace Riddle {
                 return;
             }
 
-            servers.add(address);
+            Enumerable<Message> j_msgs;
+            lock(join_messages) {
+                servers.add(address);
+                j_msgs = join_messages.to_sequence();
+            }
+
+            foreach (var message in j_msgs) {
+                raw_request(message, single(address), 5000, MessageType.OK);
+            }
+
         }
 
         public int join(string group, uint port) {
-            var msg = new Message(MessageType.JOIN, new string[] { group, port.to_string() }, new string[0]);
-            var responses = send_message_to_servers(msg);
-            return responses.where(r => r.message_type == MessageType.OK).count();
+            lock(join_messages) {
+                var msg = new Message(MessageType.JOIN, new string[] { group, port.to_string() }, new string[0]);
+                var responses = raw_request(msg, servers, 10000, MessageType.OK);
+                join_messages.add(msg);
+                return responses.where(r => r.message_type == MessageType.OK).count();
+            }
         }
 
         public int leave(string group, uint port) {
+            lock(join_messages) {
+                join_messages = join_messages.where(m => m.arguments[0] != group && m.arguments[1] != port.to_string()).to_sequence();
+            }
             var msg = new Message(MessageType.LEAVE, new string[] { group, port.to_string() }, new string[0]);
-            var responses = send_message_to_servers(msg);
+            var responses = raw_request(msg, servers, 10000, MessageType.OK);
             return responses.where(r => r.message_type == MessageType.OK).count();
         }
 
         public int propogate(NameInfo info) {
             var msg = new Message(MessageType.PROPOGATE, new string[] { info.name, "10" }, new string[] { info.get_encoded() });
-            var responses = send_message_to_servers(msg);
+            var responses = raw_request(msg, servers, 10000, MessageType.OK);
             return responses.where(r => r.message_type == MessageType.OK).count();
         }
 
         public Enumerable<InetSocketAddress> who_is_in(string group) {
             var who_in = new Message(MessageType.WHO_IN, new string[] { group }, new string[0]);
-            var responses = send_message_to_servers(who_in);
+            var responses = raw_request(who_in, servers, 2000, MessageType.ANSWER);
             return responses.select_many<InetSocketAddress>(r => ate(r.items).select<InetSocketAddress>(i => parse_address(i)));
         }
 
         public Enumerable<NameInfo> who_is(string domain) {
             var who_is = new Message(MessageType.WHO_IS, new string[] { domain }, new string[0]);
-            var responses = send_message_to_servers(who_is);
+            var responses = raw_request(who_is, servers, 2000, MessageType.ANSWER);
+
             if(domain.has_suffix(".rns")) {
                 return responses.select_many<NameInfo>(r => ate(r.items).select<NameInfo>(i => new DecentralisedNameInfo.from_string(i)));
             }
@@ -69,14 +99,20 @@ namespace Riddle {
             return responses.select_many<NameInfo>(r => ate(r.items).select<NameInfo>(i => new CertifiedNameInfo.from_string(i)));
         }
 
-        public int riddle(RiddleEnvelope riddle) {
+        public int riddle(RiddleEnvelope riddle, Enumerable<InetSocketAddress> servers) {
             var msg = riddle.to_message();
-            var responses = send_message_to_servers(msg);
+            var responses = raw_request(msg, servers, 10000, MessageType.OK);
             return responses.where(r => r.message_type == MessageType.OK).count();
         }
 
+        public Enumerable<Message> raw_request(Message msg, Enumerable<InetSocketAddress> servers, int64 timeout = 10000, MessageType? filter = null) {
+            var manager = new RequestManager(servers);
+            manager.start_request(msg);
+            return manager.get_responses(timeout, filter);
+        }
+
         public InetSocketAddress? callback(SolutionEnvelope solution, InetSocketAddress server, uint8[] author_signing_key, uint8[] reply_public_key, uint8[] reply_secret_key) throws Error {
-            var socket = socket_client.connect(server);
+            var socket = new SocketClient().connect(server);
             var dis = new DataInputStream(socket.input_stream);
             var dos = new DataOutputStream(socket.output_stream);
 
@@ -101,28 +137,6 @@ namespace Riddle {
             return (a1.address.equal(a2.address) && a1.port == a2.port);
         }
 
-        private Invercargill.Sequence<Message> send_message_to_servers(Message message) {
-            return servers.parallel_select<Message?>(server => {
-                try {
-                    var socket = socket_client.connect(server);
-                    var dis = new DataInputStream(socket.input_stream);
-                    var dos = new DataOutputStream(socket.output_stream);
-
-                    message.send(dos);
-                    var reply =  Message.from_stream(dis);
-
-                    if(reply.message_type == MessageType.NOT_ACCEPTED) {
-                        warning(@"Got NOT-ACCEPTED response from $(server.address): $(reply.arguments[0]) $(reply.arguments[1])");
-                    }
-                    return reply;
-                }
-                catch(Error e) {
-                    warning(@"Client message send failed: $(e.message)");
-                    return null;
-                }
-            }).where(m => m != null).to_sequence();
-        }
-
         private InetSocketAddress parse_address(string address) {
             var parts = address.split(" ", 3);
             var isa = new InetSocketAddress.from_string(parts[0], uint.parse(parts[1]));

+ 3 - 3
src/lib/DecentralisedNameInfo.vala

@@ -109,9 +109,9 @@ namespace Riddle {
 
         private const string ENCODING_CHARS = "abcdefghijklmnopqrstuvwxyz234567";
         private static string encode_hash(uint8[] data) {
-            var s = new char[6];
-            for(var i = 0; i < 6; i++) {
-                var v = data[i*5];
+            var s = new char[8];
+            for(var i = 0; i < 8; i++) {
+                var v = data[i*4];
                 var ev = v / 8;
                 s[i] = ENCODING_CHARS[ev];
             }

+ 2 - 0
src/lib/LanDiscoverer.vala

@@ -60,6 +60,7 @@ namespace Riddle {
         }
 
         public void query_peers() throws Error {
+            print("Query\n");
             var query = "Riddle PQ".data;
             multicast_socket.send_to (multicast_address, query);
         }
@@ -74,6 +75,7 @@ namespace Riddle {
         }
 
         private void send_advertisement() throws Error {
+            print("Advertise\n");
             var advertisement = @"Riddle PA $(server_address.address) $(server_address.port)".data;
             multicast_socket.send_to (multicast_address, advertisement);
         }

+ 1 - 1
src/lib/Message.vala

@@ -91,7 +91,7 @@ namespace Riddle {
         }
     }
 
-    public class Message {
+    public class Message : Object {
 
         public MessageType message_type { get; set; }
         public string[] arguments { get; set; }

+ 23 - 2
src/lib/NameInfo.vala

@@ -8,7 +8,7 @@ namespace Riddle {
         NOT_IN_DATE
     }
 
-    public abstract class NameInfo {
+    public abstract class NameInfo : Object {
 
         public string name { get; protected set; }
         public DateTime effective { get; protected set; }
@@ -73,9 +73,13 @@ namespace Riddle {
             return checksum == Base64.encode(digest);
         }
 
+        public string[] to_zone_data() {
+            return properties.select<string>(p => p.to_zone(name)).where(s => s != null).to_array();
+        }
+
     }
 
-    public class NameProperty {
+    public class NameProperty : Object {
 
         public NamePropertyType property_type { get; set; }
         public string value { get; set; }
@@ -99,6 +103,23 @@ namespace Riddle {
             this.arguments = arguments;
         }
 
+        public string? to_zone(string name) {
+            switch (property_type) {
+                case NamePropertyType.ADDRESS:
+                    var adr = new InetAddress.from_string(value);
+                    switch(adr.family) {
+                        case SocketFamily.IPV4:
+                            return @"$name 60 A $(adr.to_string())";
+                        case SocketFamily.IPV6:
+                            return @"$name 60 AAAA $(adr.to_string())";
+                        default:
+                            return null;
+                    }
+                default:
+                    return null;
+            }
+        }
+
     }
 
     public enum NamePropertyType {

+ 1 - 1
src/lib/PeerDiscoverer.vala

@@ -1,6 +1,6 @@
 namespace Riddle {
 
-    public abstract class PeerDiscoverer {
+    public abstract class PeerDiscoverer : Object {
 
         protected abstract void discover_peers();
         public signal void peer_discovered(InetSocketAddress address);

+ 112 - 0
src/lib/RequestManager.vala

@@ -0,0 +1,112 @@
+using Invercargill;
+
+namespace Riddle {
+
+    internal class RequestManager {
+
+        private Invercargill.Sequence<Message> responses;
+        private Enumerable<InetSocketAddress> servers;
+        private SocketClient socket_client;
+
+        private int completed_threads = 0;
+        private int total_threads = 0;
+
+        private Cond cond = Cond ();
+        private Mutex mutex = Mutex ();
+
+        public RequestManager(Enumerable<InetSocketAddress> servers) {
+            this.servers = servers.to_sequence ();
+            responses = new Invercargill.Sequence<Message>();
+            socket_client = new SocketClient();
+        }
+
+        public void start_request(Message message) {
+            var server_array = servers.to_array();
+            total_threads = server_array.length;
+            for(var i = 0; i < total_threads; i++) {
+                var server = server_array[i];
+                new Thread<bool>("RequestManager Request Thread", () => make_request(message, server, i));
+            }
+        }
+
+        public Enumerable<Message> get_all_responses() {
+            mutex.lock();
+            while(completed_threads < total_threads) {
+                cond.wait(mutex);
+            }
+            var r = responses.where(r => r != null).to_sequence();
+            mutex.unlock();
+            return r;
+        }
+
+        public Enumerable<Message> get_responses_for(int64 timeout) {
+            mutex.lock();
+            var end_time = GLib.get_monotonic_time() * 5 * TimeSpan.MILLISECOND;
+            while(completed_threads < total_threads) {
+                if(!cond.wait_until(mutex, end_time)) {
+                    break;
+                }
+            }
+            var r = responses.where(r => r != null).to_sequence();
+            mutex.unlock();
+            return r;
+        }
+
+        public Enumerable<Message> get_responses(int64 timeout, MessageType? filter = null) {
+            mutex.lock();
+            var end_time = GLib.get_monotonic_time() * 5 * TimeSpan.MILLISECOND;
+            while(completed_threads < total_threads) {
+                if(!cond.wait_until(mutex, end_time)) {
+                    if(responses.any(m => m != null && (filter == null || m.message_type == filter))) {
+                        break;
+                    }
+                    cond.wait(mutex);
+                }
+            }
+            var r = responses.where(r => r != null).to_sequence();
+            mutex.unlock();
+            return r;
+        }
+
+        public Enumerable<Message> get_received_responses() {
+            mutex.lock();
+            var r = responses.where(r => r != null).to_sequence();
+            mutex.unlock();
+            return r;
+        }
+
+        private bool make_request(Message message, InetSocketAddress server, int index) {
+            var error = false;
+            Message reply = null;
+            try {
+                var socket = socket_client.connect(server);
+                var dis = new DataInputStream(socket.input_stream);
+                var dos = new DataOutputStream(socket.output_stream);
+
+                message.send(dos);
+                reply = Message.from_stream(dis);
+
+                if(reply.message_type == MessageType.NOT_ACCEPTED) {
+                    warning(@"Got NOT-ACCEPTED response from $(server.address): $(reply.arguments[0]) $(reply.arguments[1])");
+                }
+
+            }
+            catch(Error e) {
+                warning(@"Client message send failed: $(e.message)");
+                error = true;
+            }
+
+            mutex.lock();
+            responses.add(reply);
+            completed_threads++;
+            cond.broadcast();
+            mutex.unlock();
+
+            return !error;
+        }
+
+        
+
+    }
+
+}

+ 1 - 1
src/lib/RiddleEnvelope.vala

@@ -3,7 +3,7 @@ using Invercargill;
 namespace Riddle {
 
 
-    public class RiddleEnvelope {
+    public class RiddleEnvelope : Object {
 
         public string group_name { get; set; }
         public int ttl { get; set; }

+ 86 - 26
src/lib/Server.vala

@@ -5,46 +5,73 @@ namespace Riddle {
     public class Server {
 
         public const int REGISTRATION_TIMEOUT_US = 600000000;
+        public const int GOSSIP_INTERVAL_US = 2000000;
+        public const string RIDDLE_SERVER_GROUP = "Riddle";
+
         private SocketService service;
         private Gee.HashMap<string, Invercargill.Sequence<Registration>> registrations = new Gee.HashMap<string, Invercargill.Sequence<Registration>>();
         private Gee.HashMap<string, RiddleEnvelope> riddles = new Gee.HashMap<string, RiddleEnvelope>();
         private Gee.HashMap<string, NameInfo> names = new Gee.HashMap<string, NameInfo>();
+        private Client client;
+
+        private AsyncQueue<Gossip> gossip = new AsyncQueue<Gossip>();
+        private Thread<bool> gossip_thread;
+        private uint16 port;
         
         
-        public Server(uint16 port) throws Error {
+        public Server(string address, uint16 port) throws Error {
+            this.port = port;
+            var add = new InetAddress.from_string(address);
             service = new SocketService ();
             service.add_inet_port (port, null);
+
+            client = new Client.with_server(new InetSocketAddress(add, port));
+            client.join(RIDDLE_SERVER_GROUP, port);
+            gossip_thread = new Thread<bool>("Gossip", spread_gossip);
+        }
+
+        public async void start() {
+            service.incoming.connect((c, o) => {
+                handle_connection.begin(c);
+                return false;
+            });
             service.start ();
         }
 
-        public async void run() {
-            while(true) {
-                SocketConnection connection = null;
-                try {
-                    print("Waiting for connection\n");
-                    connection = yield service.accept_async(null);
-                    print("New connection\n");
-                    var dis = new DataInputStream(connection.input_stream);
-                    var dos = new DataOutputStream(connection.output_stream);
-
-                    var message = yield Message.from_stream_async(dis);
-                    print(@"Received $(MessageType.to_string(message.message_type)) message\n");
-                    var reply = service_message(message, (InetSocketAddress)connection.get_remote_address());
-                    reply.send(dos);
-                    yield dos.flush_async();
+        private async void handle_connection(SocketConnection connection) {
+            try {
+                print("New connection\n");
+                var dis = new DataInputStream(connection.input_stream);
+                var dos = new DataOutputStream(connection.output_stream);
+
+                var message = yield Message.from_stream_async(dis);
+                print(@"Received $(MessageType.to_string(message.message_type)) message\n");
+                var reply = service_message(message, (InetSocketAddress)connection.get_remote_address());
+                reply.send(dos);
+                yield dos.flush_async();
+                yield connection.close_async();
+                print("Connection closed.\n");
+            }
+            catch(Error e) {
+                warning(@"Error servicing connection: $(e.message)");
+                try{ 
                     yield connection.close_async();
                 }
-                catch(Error e) {
-                    warning(@"Error servicing connection: $(e.message)");
-                    try{ 
-                        yield connection.close_async();
-                    }
-                    catch(Error e2) {
-                        warning(@"Error closing connection after initial error: $(e2.message)");
-                    }
+                catch(Error e2) {
+                    warning(@"Error closing connection after initial error: $(e2.message)");
                 }
             }
+        }
 
+        private bool spread_gossip() {
+            while (true) {
+                // What's the goss?
+                var goss = gossip.pop();
+                var members = get_group_registrations(goss.group).select<InetSocketAddress>(r => r.address);
+                print("Spreading gossip!\n");
+                client.raw_request(goss.message, members, 30000);
+                Thread.usleep(GOSSIP_INTERVAL_US);
+            }
         }
 
         private Message service_message(Message msg, InetSocketAddress origin) throws Error {
@@ -122,12 +149,20 @@ namespace Riddle {
                 return new Message(MessageType.NOT_ACCEPTED, new string[] { "203", "outside-date-range" }, new string[0]);
             }
 
+            if(ttl > 0) {
+                msg.arguments[1] = (ttl - 1).to_string();
+                gossip_to(RIDDLE_SERVER_GROUP, msg);
+            }
+
             return new Message(MessageType.OK, new string[0], new string[0]);
         }
 
         private Message handle_who_in(Message msg) throws Error {
             cleanup_registrations();
             var addresses = get_group_registrations(msg.arguments[0]).select<string>(r => @"$(r.address.address.to_string()) $(r.address.port)");
+            if(addresses.count() == 0) {
+                return new Message(MessageType.UNKNOWN, new string[0], new string[0]);
+            }
             return new Message(MessageType.ANSWER, new string[0], addresses.to_array());
         }
 
@@ -152,9 +187,13 @@ namespace Riddle {
                 riddles.set(riddle_envelope.identifier, riddle_envelope);
             }
             
-            // Forward riddle on here
+            var to_forward = riddle_envelope.forward(port);
+            if(to_forward != null) {
+                gossip_to(riddle_envelope.group_name, to_forward.to_message());
+            }
+
             // Pass to application to solve then compact when done.
-            
+            riddle_envelope.compact();
             return new Message(MessageType.OK, new string[0], new string[0]);
         }
 
@@ -252,6 +291,22 @@ namespace Riddle {
             return null;
         }
 
+        private void gossip_to(string group, Message message) {
+            var g = new Gossip() {
+                message = message,
+                group = group
+            };
+            gossip.push(g);
+        }
+
+        private void gossip_next(string group, Message message) {
+            var g = new Gossip() {
+                message = message,
+                group = group
+            };
+            gossip.push_front(g);
+        }
+
     }
 
     private class Registration {
@@ -260,5 +315,10 @@ namespace Riddle {
         public DateTime timestamp { get; set; }
 
     }
+
+    private class Gossip {
+        public Message message { get; set; }
+        public string group { get; set; }
+    }
     
 }

+ 1 - 1
src/lib/Solution.vala

@@ -1,7 +1,7 @@
 namespace Riddle {
 
 
-    public sealed class Solution {
+    public sealed class Solution : Object {
 
         public uint8[] data { get; private set; }
         private uint8[] secret { get; private set; }

+ 1 - 1
src/lib/SolutionEnvelope.vala

@@ -1,7 +1,7 @@
 namespace Riddle {
 
 
-    public class SolutionEnvelope {
+    public class SolutionEnvelope : Object{
 
         public string identifier { get; set; }
         public uint8[] signed_data { get; set; }

+ 1 - 0
src/lib/meson.build

@@ -30,6 +30,7 @@ sources += files('DecentralisedNameInfo.vala')
 sources += files('PeerDiscoverer.vala')
 sources += files('KnownHostDiscoverer.vala')
 sources += files('LanDiscoverer.vala')
+sources += files('RequestManager.vala')