diff --git a/mirage/_ms.py b/mirage/_ms.py index 5f4d22d523dc142db4ce26c1c486bfbc8d7f916e..35e0e09054ad0809858b2c811e3d2c734d2d75dd 100644 --- a/mirage/_ms.py +++ b/mirage/_ms.py @@ -7,7 +7,4 @@ class MS: type = attr.ib() written_on = attr.ib() blockstamp = attr.ib() - - - - + timestamp = attr.ib() diff --git a/mirage/block_forge.py b/mirage/block_forge.py index 0056dc1bcb8a8071890d0e7220432daaeffd9cb1..a8227066dc123a7bedb8d830c2225c43b31359de 100644 --- a/mirage/block_forge.py +++ b/mirage/block_forge.py @@ -17,27 +17,27 @@ class BlockForge: """ currency = attr.ib(validator=attr.validators.instance_of(str)) key = attr.ib(validator=attr.validators.instance_of(SigningKey)) - _pool = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list)) + pool = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list)) blocks = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list)) user_identities = attr.ib(default=attr.Factory(dict), validator=attr.validators.instance_of(dict)) _ud = attr.ib(default=False, validator=attr.validators.instance_of(bool)) _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('mirage'))) @classmethod - def start(cls, currency, salt, password, loop): - key = SigningKey(salt, password) + def start(cls, currency, salt, password, scrypt_params, loop): + key = SigningKey(salt, password, scrypt_params) return cls(currency, key) def push(self, document): - self._pool.append(document) + self.pool.append(document) def next_dividend(self): if self._ud: try: - latest_dividend = next(reversed([b for b in self.blocks if b.ud])) + latest_dividend = next(reversed([b.ud for b in self.blocks if b.ud])) except StopIteration: return 100 - return latest_dividend * 1.1 + return int(latest_dividend * 1.1) def previous_hash(self): try: @@ -57,41 +57,42 @@ class BlockForge: return len([i for i in self.user_identities.values() if i.member]) def identities(self): - return [d for d in self._pool if type(d) is Identity] + return [d for d in self.pool if type(d) is Identity] def revocations(self): - return [r for r in self._pool if type(r) is Revocation] + return [r for r in self.pool if type(r) is Revocation] def joiners(self): - return [d for d in self._pool if type(d) is Membership and d.membership_type == 'IN' - and d.issuer in self.user_identities and not self.user_identities[d.issuer].member] + return [d for d in self.pool if type(d) is Membership and d.membership_type == 'IN' + and ((d.issuer in self.user_identities and not self.user_identities[d.issuer].member) + or d.issuer in [d.pubkey for d in self.pool if type(d) is Identity])] def actives(self): - return [d for d in self._pool if type(d) is Membership and d.membership_type == 'IN' + return [d for d in self.pool if type(d) is Membership and d.membership_type == 'IN' and d.issuer in self.user_identities and self.user_identities[d.issuer].member] def leavers(self): - return [d for d in self._pool if type(d) is Membership and d.membership_type == 'OUT' + return [d for d in self.pool if type(d) is Membership and d.membership_type == 'OUT' and d.issuer in self.user_identities and self.user_identities[d.issuer].member] def excluded(self): return [] def certifications(self): - return [d for d in self._pool if type(d) is Certification] + return [d for d in self.pool if type(d) is Certification] def transactions(self): - return [d for d in self._pool if type(d) is Transaction] + return [d for d in self.pool if type(d) is Transaction] def parameters(self): if not self.blocks: return 0.1, 86400, 100000, 10800, 40, 2629800, 31557600, 1, 604800, 604800,\ 0.9, 15778800, 5, 12, 300, 25, 40, 0.66 - def monetary_mass(self): + def monetary_mass(self, number=None): mass = 0 for b in self.blocks: - if b.ud: + if b.ud and (not number or b.number <= number): mass += b.ud * b.members_count return mass @@ -163,7 +164,8 @@ class BlockForge: self.user_identities[membership.issuer].memberships.append(MS(pubkey=membership.issuer, type=membership.membership_type, written_on=block.number, - blockstamp=membership.membership_ts)) + blockstamp=membership.membership_ts, + timestamp=block.mediantime)) for tx in block.transactions: receivers = [o.conditions.left.pubkey for o in tx.outputs @@ -171,4 +173,4 @@ class BlockForge: self.user_identities[tx.issuers[0]].tx_sent.append(tx) self.user_identities[receivers[0]].tx_received.append(tx) - self._pool = [] + self.pool = [] diff --git a/mirage/http.py b/mirage/http.py index a839038a5d96e2a8034b66da98051a8553cadde2..1cd28d3c66c21bc1a2e88605d9bd0fdb67c8b55c 100644 --- a/mirage/http.py +++ b/mirage/http.py @@ -2,6 +2,7 @@ from aiohttp import web, log, errors import json import socket from duniterpy.documents import Peer +import asyncio class Request: @@ -40,7 +41,7 @@ class HTTPServer: async def _handler(self, request, handle): await request.read() self.requests.append(Request(request.method, request.path, request.content)) - json_data, http_code = handle(request) + json_data, http_code = await handle(request) return web.Response(body=bytes(json.dumps(json_data), "utf-8"), headers={'Content-Type': 'application/json'}, status=http_code) diff --git a/mirage/node.py b/mirage/node.py index 9ac9ec329d7a56b0aead6fec22abef80e3772d81..a8418db95e92cf87416713746305a0a3604d81a3 100644 --- a/mirage/node.py +++ b/mirage/node.py @@ -1,5 +1,5 @@ import attr -from duniterpy.documents import Peer, BMAEndpoint, BlockUID +from duniterpy.documents import Peer, BMAEndpoint, BlockUID, Identity, Certification from duniterpy.api import errors from duniterpy.key import SigningKey, ScryptParams from .http import HTTPServer @@ -26,18 +26,73 @@ class Node: '/wot/lookup/{search}': node.lookup, '/wot/certifiers-of/{search}': node.certifiers_of, '/wot/certified-by/{search}': node.certified_by, + '/wot/requirements/{pubkey}': node.requirements, '/blockchain/parameters': node.parameters, '/blockchain/with/ud': node.with_ud, '/blockchain/memberships/{search}': node.memberships, '/tx/history/{search}': node.tx_history, } + post_routes = { + '/wot/add': node.add, + '/wot/certify': node.certify + } for r, h in get_routes.items(): node.http.add_route("GET", r, h) + for r, h in post_routes.items(): + node.http.add_route("POST", r, h) srv, port, url = await node.http.create_server() print("Server started on {0}".format(url)) return node - def block_by_number(self, request): + async def add(self, request): + data = await request.post() + identity = Identity.from_signed_raw(data["identity"]) + self.forge.pool.append(identity) + return {}, 200 + + async def certify(self, request): + data = await request.post() + certification = Certification.from_signed_raw(data["cert"]) + self.forge.pool.append(certification) + return {}, 200 + + async def requirements(self, request): + pubkey = request.match_info['pubkey'] + try: + user_identity = self.forge.user_identities[pubkey] + except KeyError: + try: + user_identity = next(i for i in self.forge.user_identities.values() if i.uid == pubkey) + except StopIteration: + return { + 'ucode': errors.NO_MEMBER_MATCHING_PUB_OR_UID, + 'message': "No member matching this pubkey or uid" + }, 404 + return { + "identities": [ + { + "pubkey": user_identity.pubkey, + "uid": user_identity.uid, + "meta": { + "timestamp": str(user_identity.blockstamp), + }, + "expired": user_identity.revoked, + "outdistanced": not user_identity.member, + "certifications": [ + { + "from": c.from_identity.pubkey, + "to": c.to_identity.pubkey, + "expiresIn": max(self.forge.blocks[-1].mediantime - 31557600 - c.mediantime, 0) + } for c in user_identity.certs_received + ], + "membershipPendingExpiresIn": 0, + "membershipExpiresIn": max(self.forge.blocks[-1].mediantime - 15778800 + - user_identity.memberships[-1].timestamp, 0) + }, + ] + }, 200 + + async def block_by_number(self, request): number = int(request.match_info['number']) try: block = self.forge.blocks[number] @@ -49,7 +104,7 @@ class Node: "time": block.time, "medianTime": block.mediantime, "membersCount": block.members_count, - "monetaryMass": self.forge.monetary_mass(), + "monetaryMass": self.forge.monetary_mass(number), "unitbase": block.unit_base, "issuersCount": block.different_issuers_count, "issuersFrame": block.issuers_frame, @@ -79,7 +134,7 @@ class Node: "message": "Block not found" }, 404 - def current_block(self, request): + async def current_block(self, request): try: block = self.forge.blocks[-1] return { @@ -120,7 +175,7 @@ class Node: "message": "No current block" }, 404 - def sources(self, request): + async def sources(self, request): pubkey = str(request.match_info['pubkey']) try: sources = self.forge.user_identities[pubkey].sources @@ -142,7 +197,7 @@ class Node: "sources": [] }, 200 - def peering(self, request): + async def peering(self, request): return { "version": 2, "currency": self.peer_doc().currency, @@ -156,7 +211,7 @@ class Node: "pubkey": self.peer_doc().pubkey }, 200 - def parameters(self, request): + async def parameters(self, request): return { "currency": self.forge.currency, "c": 0.0025, @@ -179,14 +234,14 @@ class Node: "percentRot": 0.66 }, 200 - def with_ud(self, request): + async def with_ud(self, request): return { "result": { "blocks": [b.number for b in self.forge.blocks if b.ud] } }, 200 - def memberships(self, request): + async def memberships(self, request): search = str(request.match_info['search']) try: user_identity = self.forge.user_identities[search] @@ -216,7 +271,7 @@ class Node: ] }, 200 - def certifiers_of(self, request): + async def certifiers_of(self, request): search = str(request.match_info['search']) try: user_identity = self.forge.user_identities[search] @@ -255,7 +310,7 @@ class Node: ] }, 200 - def certified_by(self, request): + async def certified_by(self, request): search = str(request.match_info['search']) try: user_identity = self.forge.user_identities[search] @@ -294,7 +349,7 @@ class Node: ] }, 200 - def lookup(self, request): + async def lookup(self, request): search = str(request.match_info['search']) matched = [i for i in self.forge.user_identities.values() if search in i.pubkey or search in i.uid] @@ -340,7 +395,7 @@ class Node: ] }, 200 - def tx_history(self, request): + async def tx_history(self, request): search = str(request.match_info['search']) try: user_identity = self.forge.user_identities[search]