Skip to content
Snippets Groups Projects
Select Git revision
  • d0b88e5969e1d892cb8d41296eb35c87a038bb7a
  • master default
  • release_0.1.53
  • 0.1.15
  • 0.1.14
  • 0.1.13
  • 0.1.11
  • 0.1.10
  • 0.1.9
9 results

node.py

Blame
  • node.py 31.08 KiB
    import attr
    from duniterpy.documents import BlockUID, Identity, Certification, Transaction
    from duniterpy.documents.peer import Peer
    from duniterpy.api.endpoint import BMAEndpoint
    from duniterpy.api import errors
    from duniterpy.key import SigningKey
    from duniterpy.key.scrypt_params import ScryptParams
    from .http import HTTPServer
    from .block_forge import BlockForge
    import logging
    
    
    @attr.s()
    class Node:
        http = attr.ib()
        forge = attr.ib()
        reject_next_post = attr.ib(default=False)
        _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("mirage")))
    
        @classmethod
        async def start(cls, port, currency, salt, password, loop):
            key = SigningKey.from_credentials(salt, password, ScryptParams(2 ** 12, 16, 1))
            node = cls(HTTPServer(port, loop), BlockForge(currency, key))
    
            get_routes = {
                "/network/peering": node.peering,
                "/blockchain/block/{number}": node.block_by_number,
                "/blockchain/current": node.current_block,
                "/tx/sources/{pubkey}": node.sources,
                "/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,
                "/tx/history/{search}/blocks/{from}/{to}": node.tx_history_range,
                "/ud/history/{search}": node.ud_history,
            }
            post_routes = {
                "/wot/add": node.add,
                "/wot/certify": node.certify,
                "/tx/process": node.process,
            }
            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)
            port, url = await node.http.create_server()
            print("Server started on {0}".format(url))
            return node
    
        async def add(self, request):
            data = await request.post()
            if self.reject_next_post:
                self.reject_next_post = False
                return {"ucode": errors.UNHANDLED, "message": "Rejected"}, 400
            identity = Identity.from_signed_raw(data["identity"])
            self.forge.pool.append(identity)
            return {}, 200
    
        async def process(self, request):
            data = await request.post()
            if self.reject_next_post:
                self.reject_next_post = False
                return {"ucode": errors.UNHANDLED, "message": "Rejected"}, 400
            transaction = Transaction.from_signed_raw(data["transaction"])
            self.forge.pool.append(transaction)
            return {}, 200
    
        async def certify(self, request):
            data = await request.post()
            if self.reject_next_post:
                self.reject_next_post = False
                return {"ucode": errors.UNHANDLED, "message": "Rejected"}, 400
            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)},
                            "wasMember": user_identity.was_member,
                            "isSentry": True,
                            "revoked": user_identity.revoked,
                            "revocation_sig": user_identity.revocation_sig,
                            "revoked_on": user_identity.revoked_on,
                            "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
                            if not user_identity.member
                            else 10000000,
                            "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]
                return (
                    {
                        "version": block.version,
                        "nonce": block.noonce,
                        "number": block.number,
                        "powMin": block.powmin,
                        "time": block.time,
                        "medianTime": block.mediantime,
                        "membersCount": block.members_count,
                        "monetaryMass": self.forge.monetary_mass(number),
                        "unitbase": block.unit_base,
                        "issuersCount": block.different_issuers_count,
                        "issuersFrame": block.issuers_frame,
                        "issuersFrameVar": block.issuers_frame_var,
                        "currency": block.currency,
                        "issuer": block.issuer,
                        "signature": block.signatures[0],
                        "hash": block.sha_hash,
                        "parameters": block.parameters if block.parameters else "",
                        "previousHash": block.prev_hash,
                        "previousIssuer": block.prev_issuer,
                        "inner_hash": block.inner_hash,
                        "dividend": block.ud,
                        "identities": [i.inline() for i in block.identities],
                        "joiners": [m.inline() for m in block.joiners],
                        "actives": [m.inline() for m in block.actives],
                        "leavers": [m.inline() for m in block.leavers],
                        "revoked": [r.inline() for r in block.revoked],
                        "excluded": [i.inline() for i in block.excluded],
                        "certifications": [c.inline() for c in block.certifications],
                        "transactions": [t.compact() for t in block.transactions],
                        "raw": block.raw(),
                    },
                    200,
                )
            except IndexError:
                return {"ucode": errors.BLOCK_NOT_FOUND, "message": "Block not found"}, 404
    
        async def current_block(self, _):
            try:
                block = self.forge.blocks[-1]
                return (
                    {
                        "version": block.version,
                        "nonce": block.noonce,
                        "number": block.number,
                        "powMin": block.powmin,
                        "time": block.time,
                        "medianTime": block.mediantime,
                        "membersCount": block.members_count,
                        "monetaryMass": self.forge.monetary_mass(),
                        "unitbase": block.unit_base,
                        "issuersCount": block.different_issuers_count,
                        "issuersFrame": block.issuers_frame,
                        "issuersFrameVar": block.issuers_frame_var,
                        "currency": block.currency,
                        "issuer": block.issuer,
                        "signature": block.signatures[0],
                        "hash": block.computed_inner_hash(),
                        "parameters": block.parameters if block.parameters else "",
                        "previousHash": block.prev_hash,
                        "previousIssuer": block.prev_issuer,
                        "inner_hash": block.inner_hash,
                        "dividend": block.ud,
                        "identities": [i.inline() for i in block.identities],
                        "joiners": [m.inline() for m in block.joiners],
                        "actives": [m.inline() for m in block.actives],
                        "leavers": [m.inline() for m in block.leavers],
                        "revoked": [r.inline() for r in block.revoked],
                        "excluded": [i.inline() for i in block.excluded],
                        "certifications": [c.inline() for c in block.certifications],
                        "transactions": [t.inline() for t in block.transactions],
                        "raw": block.raw(),
                    },
                    200,
                )
            except IndexError:
                return (
                    {"ucode": errors.NO_CURRENT_BLOCK, "message": "No current block"},
                    404,
                )
    
        async def sources(self, request):
            pubkey = str(request.match_info["pubkey"])
            try:
                sources = self.forge.user_identities[pubkey].sources
                return (
                    {
                        "currency": self.forge.currency,
                        "pubkey": pubkey,
                        "sources": [
                            {
                                "type": s.source,
                                "noffset": s.index,
                                "identifier": s.origin_id,
                                "amount": s.amount,
                                "base": s.base,
                                "conditions": "SIG({0})".format(pubkey),
                            }
                            for s in sources
                        ],
                    },
                    200,
                )
            except KeyError:
                return (
                    {"currency": self.forge.currency, "pubkey": pubkey, "sources": []},
                    200,
                )
    
        async def peering(self, _):
            return (
                {
                    "version": 2,
                    "currency": self.peer_doc().currency,
                    "endpoints": [str(self.peer_doc().endpoints[0])],
                    "status": "UP",
                    "block": str(self.peer_doc().blockUID),
                    "signature": self.peer_doc().signatures[0],
                    "raw": self.peer_doc().raw(),
                    "pubkey": self.peer_doc().pubkey,
                },
                200,
            )
    
        async def parameters(self, _):
            return (
                {
                    "currency": self.forge.currency,
                    "c": 0.0025,
                    "dt": 86400,
                    "ud0": 100000,
                    "sigPeriod": 10800,
                    "sigStock": 40,
                    "sigWindow": 2629800,
                    "sigValidity": 31557600,
                    "sigQty": 1,
                    "idtyWindow": 604800,
                    "msWindow": 604800,
                    "xpercent": 0.9,
                    "msValidity": 15778800,
                    "stepMax": 5,
                    "medianTimeBlocks": 12,
                    "avgGenTime": 300,
                    "dtDiffEval": 25,
                    "blocksRot": 40,
                    "percentRot": 0.66,
                    "udTime0": 1488970800,
                    "udReevalTime0": 1490094000,
                    "dtReeval": 15778800,
                },
                200,
            )
    
        async def with_ud(self, _):
            return (
                {"result": {"blocks": [b.number for b in self.forge.blocks if b.ud]}},
                200,
            )
    
        async def memberships(self, request):
            search = str(request.match_info["search"])
            try:
                user_identity = self.forge.user_identities[search]
            except KeyError:
                try:
                    user_identity = next(
                        i for i in self.forge.user_identities.values() if i.uid == search
                    )
                except StopIteration:
                    return (
                        {
                            "ucode": errors.NO_MEMBER_MATCHING_PUB_OR_UID,
                            "message": "No member matching this pubkey or uid",
                        },
                        404,
                    )
    
            return (
                {
                    "pubkey": user_identity.pubkey,
                    "uid": user_identity.uid,
                    "sigDate": str(user_identity.blockstamp),
                    "memberships": [
                        {
                            "version": 2,
                            "currency": self.forge.currency,
                            "membership": m.type,
                            "blockNumber": m.blockstamp.number,
                            "blockHash": m.blockstamp.sha_hash,
                            "written": m.written_on,
                        }
                        for m in user_identity.memberships
                    ],
                },
                200,
            )
    
        async def certifiers_of(self, request):
            search = str(request.match_info["search"])
            try:
                user_identity = self.forge.user_identities[search]
            except KeyError:
                try:
                    user_identity = next(
                        i for i in self.forge.user_identities.values() if i.uid == search
                    )
                except StopIteration:
                    return (
                        {
                            "ucode": errors.NO_MEMBER_MATCHING_PUB_OR_UID,
                            "message": "No member matching this pubkey or uid",
                        },
                        404,
                    )
    
            return (
                {
                    "pubkey": user_identity.pubkey,
                    "uid": user_identity.uid,
                    "sigDate": str(user_identity.blockstamp),
                    "isMember": user_identity.member,
                    "certifications": [
                        {
                            "pubkey": c.from_identity.pubkey,
                            "uid": c.from_identity.uid,
                            "isMember": c.from_identity.member,
                            "wasMember": c.from_identity.was_member,
                            "cert_time": {"block": c.block, "medianTime": c.mediantime},
                            "sigDate": str(c.from_identity.blockstamp),
                            "written": {
                                "number": c.written_on.number,
                                "hash": c.written_on.sha_hash,
                            },
                            "signature": c.signature,
                        }
                        for c in user_identity.certs_received
                    ],
                },
                200,
            )
    
        async def certified_by(self, request):
            search = str(request.match_info["search"])
            try:
                user_identity = self.forge.user_identities[search]
            except KeyError:
                try:
                    user_identity = next(
                        i for i in self.forge.user_identities.values() if i.uid == search
                    )
                except StopIteration:
                    return (
                        {
                            "ucode": errors.NO_MEMBER_MATCHING_PUB_OR_UID,
                            "message": "No member matching this pubkey or uid",
                        },
                        404,
                    )
    
            return (
                {
                    "pubkey": user_identity.pubkey,
                    "uid": user_identity.uid,
                    "sigDate": str(user_identity.blockstamp),
                    "isMember": user_identity.member,
                    "certifications": [
                        {
                            "pubkey": c.from_identity.pubkey,
                            "uid": c.from_identity.uid,
                            "isMember": c.from_identity.member,
                            "wasMember": c.from_identity.was_member,
                            "cert_time": {"block": c.block, "medianTime": c.mediantime},
                            "sigDate": str(c.from_identity.blockstamp),
                            "written": {
                                "number": c.written_on.number,
                                "hash": c.written_on.sha_hash,
                            },
                            "signature": c.signature,
                        }
                        for c in user_identity.certs_sent
                    ],
                },
                200,
            )
    
        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
            ]
    
            return (
                {
                    "partial": False,
                    "results": [
                        {
                            "pubkey": m.pubkey,
                            "uids": [
                                {
                                    "uid": m.uid,
                                    "meta": {"timestamp": str(m.blockstamp)},
                                    "revoked": m.revoked,
                                    "revoked_on": m.revoked_on,
                                    "revocation_sig": m.revocation_sig,
                                    "self": m.signature,
                                    "others": [
                                        {
                                            "pubkey": c.to_identity.pubkey,
                                            "meta": {"block_number": c.block},
                                            "uids": [c.to_identity.uid],
                                            "isMember": c.to_identity.member,
                                            "wasMember": c.to_identity.was_member,
                                            "signature": c.signature,
                                        }
                                        for c in m.certs_received
                                    ],
                                }
                            ],
                            "signed": [
                                {
                                    "pubkey": c.to_identity.pubkey,
                                    "meta": {"timestamp": str(c.to_identity.blockstamp)},
                                    "cert_time": {
                                        "block": c.block,
                                        "block_hash": str(
                                            self.forge.blocks[c.block].blockUID
                                        ),
                                    },
                                    "uid": c.to_identity.uid,
                                    "isMember": c.to_identity.member,
                                    "wasMember": c.to_identity.was_member,
                                    "revoked": c.to_identity.revoked,
                                    "revoked_on": c.to_identity.revoked_on,
                                    "revocation_sig": c.to_identity.revocation_sig,
                                    "signature": c.signature,
                                }
                                for c in m.certs_sent
                            ],
                        }
                        for m in matched
                    ],
                },
                200,
            )
    
        async def tx_history(self, request):
            search = str(request.match_info["search"])
            try:
                user_identity = self.forge.user_identities[search]
            except KeyError:
                try:
                    user_identity = next(
                        i for i in self.forge.user_identities.values() if i.uid == search
                    )
                except StopIteration:
                    return (
                        {
                            "currency": self.forge.currency,
                            "pubkey": search,
                            "history": {
                                "sent": [],
                                "received": [],
                                "sending": [],
                                "receiving": [],
                                "pending": [],
                            },
                        },
                        200,
                    )
            return (
                {
                    "currency": self.forge.currency,
                    "pubkey": user_identity.pubkey,
                    "history": {
                        "sent": [
                            {
                                "version": tx.version,
                                "issuers": tx.issuers,
                                "inputs": [i.inline(tx.version) for i in tx.inputs],
                                "unlocks": [u.inline() for u in tx.unlocks],
                                "outputs": [o.inline() for o in tx.outputs],
                                "comment": tx.comment,
                                "locktime": tx.locktime,
                                "blockstamp": str(tx.blockstamp),
                                "blockstampTime": next(
                                    [
                                        b.mediantime
                                        for b in self.forge.blocks
                                        if b.number == tx.blockstamp.number
                                    ]
                                ),
                                "signatures": tx.signatures,
                                "hash": tx.sha_hash,
                                "block_number": next(
                                    (
                                        b.number
                                        for b in self.forge.blocks
                                        if tx.sha_hash
                                        in [tx.sha_hash for tx in b.transactions]
                                    )
                                ),
                                "time": next(
                                    (
                                        b.mediantime
                                        for b in self.forge.blocks
                                        if tx.sha_hash
                                        in [tx.sha_hash for tx in b.transactions]
                                    )
                                ),
                            }
                            for tx in user_identity.tx_sent
                        ],
                        "received": [
                            {
                                "version": tx.version,
                                "issuers": tx.issuers,
                                "inputs": [i.inline(tx.version) for i in tx.inputs],
                                "unlocks": [u.inline() for u in tx.unlocks],
                                "outputs": [o.inline() for o in tx.outputs],
                                "comment": tx.comment,
                                "locktime": tx.locktime,
                                "blockstamp": str(tx.blockstamp),
                                "blockstampTime": next(
                                    [
                                        b.mediantime
                                        for b in self.forge.blocks
                                        if b.number == tx.blockstamp.number
                                    ]
                                ),
                                "signatures": tx.signatures,
                                "hash": tx.sha_hash,
                                "block_number": next(
                                    (
                                        b.number
                                        for b in self.forge.blocks
                                        if tx.sha_hash
                                        in [tx.sha_hash for tx in b.transactions]
                                    )
                                ),
                                "time": next(
                                    (
                                        b.mediantime
                                        for b in self.forge.blocks
                                        if tx.sha_hash
                                        in [tx.sha_hash for tx in b.transactions]
                                    )
                                ),
                            }
                            for tx in user_identity.tx_received
                        ],
                        "sending": [],
                        "receiving": [],
                        "pending": [],
                    },
                },
                200,
            )
    
        async def tx_history_range(self, request):
            try:
                search = str(request.match_info["search"])
                start = int(request.match_info["from"])
                end = int(request.match_info["to"])
                try:
                    user_identity = self.forge.user_identities[search]
                except KeyError:
                    try:
                        user_identity = next(
                            i
                            for i in self.forge.user_identities.values()
                            if i.uid == search
                        )
                    except StopIteration:
                        return (
                            {
                                "currency": self.forge.currency,
                                "pubkey": search,
                                "history": {
                                    "sent": [],
                                    "received": [],
                                    "sending": [],
                                    "receiving": [],
                                    "pending": [],
                                },
                            },
                            200,
                        )
                return (
                    {
                        "currency": self.forge.currency,
                        "pubkey": user_identity.pubkey,
                        "history": {
                            "sent": [
                                {
                                    "version": tx.version,
                                    "issuers": tx.issuers,
                                    "inputs": [i.inline(tx.version) for i in tx.inputs],
                                    "unlocks": [u.inline() for u in tx.unlocks],
                                    "outputs": [o.inline() for o in tx.outputs],
                                    "comment": tx.comment,
                                    "locktime": tx.locktime,
                                    "blockstamp": str(tx.blockstamp),
                                    "blockstampTime": next(
                                        (
                                            b.mediantime
                                            for b in self.forge.blocks
                                            if b.number == tx.blockstamp.number
                                        )
                                    ),
                                    "signatures": tx.signatures,
                                    "hash": tx.sha_hash,
                                    "block_number": next(
                                        (
                                            b.number
                                            for b in self.forge.blocks
                                            if tx.sha_hash
                                            in [tx.sha_hash for tx in b.transactions]
                                        )
                                    ),
                                    "time": next(
                                        (
                                            b.mediantime
                                            for b in self.forge.blocks
                                            if tx.sha_hash
                                            in [tx.sha_hash for tx in b.transactions]
                                        )
                                    ),
                                }
                                for tx in user_identity.tx_sent
                                if start <= tx.blockstamp.number <= end
                            ],
                            "received": [
                                {
                                    "version": tx.version,
                                    "issuers": tx.issuers,
                                    "inputs": [i.inline(tx.version) for i in tx.inputs],
                                    "unlocks": [u.inline() for u in tx.unlocks],
                                    "outputs": [o.inline() for o in tx.outputs],
                                    "comment": tx.comment,
                                    "locktime": tx.locktime,
                                    "blockstamp": str(tx.blockstamp),
                                    "blockstampTime": next(
                                        (
                                            b.mediantime
                                            for b in self.forge.blocks
                                            if b.number == tx.blockstamp.number
                                        )
                                    ),
                                    "signatures": tx.signatures,
                                    "hash": tx.sha_hash,
                                    "block_number": next(
                                        (
                                            b.number
                                            for b in self.forge.blocks
                                            if tx.sha_hash
                                            in [tx.sha_hash for tx in b.transactions]
                                        )
                                    ),
                                    "time": next(
                                        (
                                            b.mediantime
                                            for b in self.forge.blocks
                                            if tx.sha_hash
                                            in [tx.sha_hash for tx in b.transactions]
                                        )
                                    ),
                                }
                                for tx in user_identity.tx_received
                                if start <= tx.blockstamp.number <= end
                            ],
                            "sending": [],
                            "receiving": [],
                            "pending": [],
                        },
                    },
                    200,
                )
            except Exception as e:
                print(str(e))
    
        async def ud_history(self, request):
            search = str(request.match_info["search"])
            try:
                user_identity = self.forge.user_identities[search]
            except KeyError:
                try:
                    user_identity = next(
                        i for i in self.forge.user_identities.values() if i.uid == search
                    )
                except StopIteration:
                    return (
                        {
                            "currency": self.forge.currency,
                            "pubkey": search,
                            "history": {"history": []},
                        },
                        200,
                    )
            return (
                {
                    "currency": self.forge.currency,
                    "pubkey": user_identity.pubkey,
                    "history": {
                        "history": [attr.asdict(ud) for ud in user_identity.ud_generated]
                    },
                },
                200,
            )
    
        def peer_doc(self):
            peer = Peer(
                2,
                self.forge.currency,
                self.forge.key.pubkey,
                BlockUID.empty(),
                [BMAEndpoint("", "127.0.0.1", "", self.http.port)],
                None,
            )
            peer.sign([self.forge.key])
            return peer
    
        async def close(self):
            await self.http.close()