def __init__(self, server_name: str, server_address, handler,
                 chainbase_address_):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address_
        self.peer = PeerManager()
        self.workers = Pool()
        self.state = 'worker'
        self.self_mined_header = None
        self.self_signed_micro_block = list()

        fd_ = open('ad1.txt', 'r')
        public_key = b''
        private_key = b''
        for index, line in enumerate(fd_.readlines()):
            if index == 0:
                line = line.rstrip()
                pr, pu = line.split('ENDDING')
                temp = bytes(pr[2:-1], encoding='utf-8')
                temp = temp.replace(b'\r\n', b'\n')
                private_key = temp.replace(b'\\n', b'\n')
                temp = bytes(pu[2:-1], encoding='utf-8')
                temp = temp.replace(b'\r\n', b'\n')
                public_key = temp.replace(b'\\n', b'\n')
                break
        fd_.close()

        self.public_key = public_key
        self.private_key = private_key

        super().__init__(server_address, handler, bind_and_activate=True)
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address_):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**236 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address_
        self.peer = PeerManager()
        self.workers = Pool()

        super().__init__(server_address, handler, bind_and_activate=True)
Exemple #3
0
    def __init__(self, server_name: str, server_address, handler, chainbase_address_):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2 ** 234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address_
        self.peer = PeerManager()
        self.workers = Pool()
        self.trans_size = 0
        self.trans_size_ = 0
        self.cache = []
        self.block_received = []
        self.ass_chain = dict()

        super().__init__(server_address, handler, bind_and_activate=True)
Exemple #4
0
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address):
        self.name = server_name
        self.chainbase_address = chainbase_address
        self.peer = PeerManager()
        self.workers = Pool()

        super().__init__(server_address, handler, bind_and_activate=True)
class PoWServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address
        self.peer = PeerManager()
        self.workers = Pool()
        self.start = 0
        self.end = 0
        self.usage = 0
        self.usage_two = 0

        super().__init__(server_address, handler, bind_and_activate=True)

    def serve_forever(self, poll_interval=0.5):

        self.init_prev_hash()
        self.init_target()
        self.start_miner()

        super().serve_forever()

    def start_miner(self):
        self.__set_mine(True)
        self.start = time.time()
        ore = self.workers.apply_async(mine,
                                       args=(self.prev_hash, self.target),
                                       callback=partial(
                                           self.on_new_block_mined, self))

    @staticmethod
    def stop_miner():
        PoWServer.__set_mine(False)

    @staticmethod
    def on_new_block_mined(self: 'PoWServer', result):
        """
        try to add the block that the server itself mines to the chainbase
        :param self: the instance of PoWServer
        :param future: Future object contains mining result
        :return: None
        """
        prev_hash_, target_, nonce = result
        print('return')
        print(nonce)
        if prev_hash_ == self.prev_hash and target_ == self.target:

            if nonce < 0:  # mining is stopped by stop_miner
                self.end = time.time()
                self.usage_two += self.end - self.start
                print('usage_two', self.usage)
                return
            self.end = time.time()
            self.usage += self.end - self.start
            self.usage_two += self.end - self.start
            print('usage', self.usage)
            print('usage_two', self.usage_two)
            block = self.make_block(
                nonce)  # mining stops because a nonce have been found

            print('block mined:')
            print(block.show_block())

            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK,
                                    content=block.b)
            assert self.add_block(block) is True
            self.prev_hash = block.hash
            self.start_miner()  # start a new miner
        else:
            print('ok')

    def on_new_block_received(self, block):
        print('block receiced')
        block = Block.unpack(block)
        if self.add_block(block):
            print('try to stop current miner')
            self.stop_miner()  # stop current miner
            self.prev_hash = block.hash
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK,
                                    content=block.b)
            print('try to start a new miner')
            self.start_miner()  # start a new miner

    def init_prev_hash(self):
        """get previous hash from chainbase when initializing"""
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_PREVIOUS_HASH, b''))
            *_, msgtype, content = recv_parser(s)

            self.prev_hash = content
            print('prev_hash = ', content)

    def init_target(self):
        pass

    def make_block(self, nonce) -> Block:
        trans = self.__get_trans()

        info = Attachment()
        info.add_data(b'mined by ' + self.name.encode())
        info.ready()

        block = Block(
            0,  # todo: get index
            timestamp=time.time(),
            blockdata=BlockData(trans, info),
            previous_hash=self.prev_hash,
            nonce=nonce)
        return block

    def add_block(self, block: Block) -> bool:
        """
        add the block to the chainbase
        :param block: binary block
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_WRITE, block.b))
            *_, msgtype, content = recv_parser(s)

            print('result of adding block', content)
        return msgtype == MsgType.TYPE_RESPONSE_OK

    def acquire_block(self):
        pass

    @staticmethod
    def __keep_mining() -> bool:
        if MINE_SWITCH.value == 1:
            return True
        else:
            return False

    @staticmethod
    def __set_mine(state: bool):
        if state:
            MINE_SWITCH.value = 1
        else:
            MINE_SWITCH.value = 0

    def __get_trans(self) -> List[Transaction]:
        # self.chainbase_address
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_TRANS_READ, b''))
            *_, msgtype, content = recv_parser(s)

            trans = []  # todo: the first transaction should reward the miner,
            # todo: the server may have a property named owner_address
            if msgtype == MsgType.TYPE_RESPONSE_OK:
                trans += batch_parser(content)

            return [Transaction.unpack(t) for t in trans]

    @staticmethod
    def mine(prev_hash, target):
        """
        find a valid nonce
        :param prev_hash:
        :param target:
        :return: Tuple of (prev_hash, target, nonce)
        """
        seed()
        initial = randrange(0, MINE_TOP)  # [0, 2**32]

        print('mining')

        for nonce in range(initial, MINE_TOP):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return prev_hash, target, -1
            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

        for nonce in range(0, initial):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return prev_hash, target, -1
            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

    @staticmethod
    def __calc_hash(
            prev_hash,
            nonce: int) -> bytes:  # calculate SHA256(SHA256(prev_hash+nonce))
        sha = hashlib.sha256()
        sha.update(prev_hash)
        sha.update(struct.pack('=I', nonce))
        hash_ = sha.digest()
        sha = hashlib.sha256()
        sha.update(hash_)
        hash_ = sha.digest()

        return hash_
class PoWServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address_):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address_
        self.peer = PeerManager()
        self.workers = Pool()
        self.state = 'worker'
        self.self_mined_header = None
        self.self_signed_micro_block = list()

        fd_ = open('ad1.txt', 'r')
        public_key = b''
        private_key = b''
        for index, line in enumerate(fd_.readlines()):
            if index == 0:
                line = line.rstrip()
                pr, pu = line.split('ENDDING')
                temp = bytes(pr[2:-1], encoding='utf-8')
                temp = temp.replace(b'\r\n', b'\n')
                private_key = temp.replace(b'\\n', b'\n')
                temp = bytes(pu[2:-1], encoding='utf-8')
                temp = temp.replace(b'\r\n', b'\n')
                public_key = temp.replace(b'\\n', b'\n')
                break
        fd_.close()

        self.public_key = public_key
        self.private_key = private_key

        super().__init__(server_address, handler, bind_and_activate=True)

    def serve_forever(self, poll_interval=0.5):

        self.start_miner()

        super().serve_forever()

    def start_miner(self):
        self.__set_mine(True)
        print('ok')
        ore = self.workers.apply_async(mine,
                                       args=(self.target, ),
                                       callback=partial(
                                           self.on_new_micro_block_mined,
                                           self))

    @staticmethod
    def stop_miner():
        PoWServer.__set_mine(False)

    @staticmethod
    def on_new_macro_block_header_mined(self: 'PoWServer', result):
        """
        try to add the block that the server itself mines to the chainbase
        :param self: the instance of PoWServer
        :param result: Future object contains mining result
        :return: None
        """
        target_, nonce = result

        if target_ == self.target:

            if nonce < 0:  # mining is stopped by stop_miner
                return

            macro_block_header = self.make_macro_block_header(nonce)

            print('macro_block_header mined:')
            print(macro_block_header.show_macro_block_header())
            self.peer.sendall_block(
                msgtype=MsgType.TYPE_NEW_MACRO_BLOCK_HEADER,
                content=macro_block_header.b)

            if self.add_macro_block_header(macro_block_header) is True:
                self.state = 'leader'
                self.self_mined_header = macro_block_header
            else:
                self.start_miner()

    @staticmethod
    def on_new_micro_block_mined(self: 'PoWServer', result):
        """
        try to add the block that the server itself mines to the chainbase
        :param self: the instance of PoWServer
        :param result: Future object contains mining result
        :return: None
        """
        target_, nonce = result

        if target_ == self.target:

            if nonce < 0:  # mining is stopped by stop_miner
                return

            micro_block = self.make_micro_block(nonce)

            print('macro_block_header mined:')
            print(micro_block.show_block())
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_MICRO_BLOCK,
                                    content=micro_block.b)

            if self.add_micro_block(micro_block) is True:
                self.start_miner()
            else:
                self.start_miner()

    def on_new_macro_block_header_received(self, macro_block_header):
        print('macro_block_header received')
        macro_block_header = MacroBlockHeader.unpack(macro_block_header)
        if self.add_macro_block_header(macro_block_header):

            self.peer.sendall_block(
                msgtype=MsgType.TYPE_NEW_MACRO_BLOCK_HEADER,
                content=macro_block_header.b)

            if self.state == 'worker':
                pass
            if self.state == 'leader':
                macro_block_body = self.make_macro_block_body(
                    macro_block_header)
                print('sending macro_block_body', macro_block_body.b)
                self.on_new_macro_block_body_received(macro_block_body.b)
                self.state = 'worker'
                self.self_mined_header = None
                self.start_miner()  # start a new miner

    def on_new_macro_block_body_received(self, macro_block_body):
        print('macro_block_body received')
        macro_block_body = MacroBlockBody.unpack(macro_block_body)
        if self.add_macro_block_body(macro_block_body):
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_MACRO_BLOCK_BODY,
                                    content=macro_block_body.b)

    def on_new_micro_block_received(self, micro_block):
        print('micro_block received')
        micro_block = MicroBlock.unpack(micro_block)
        if self.add_micro_block(micro_block):
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_MICRO_BLOCK,
                                    content=micro_block.b)
        if self.state == 'worker':
            pass
        else:
            self.self_signed_micro_block.append(micro_block)

    def init_target(self):
        pass

    def make_macro_block_body(
            self, macro_block_header: MacroBlockHeader) -> 'MacroBlockBody':
        ref_hash = list()
        for i in self.self_signed_micro_block:
            ref_hash.append(i.hash)
        ref_hash = sorted(ref_hash,
                          key=lambda micro_block: micro_block.timestamp)
        self.self_signed_micro_block.clear()

        macro_block_body = MacroBlockBody(hash_=macro_block_header.hash,
                                          ref_hash=ref_hash)
        private_key = load_pem_private_key(self.private_key, None,
                                           default_backend())
        macro_block_body.ready(private_key)

        return macro_block_body

    def make_macro_block_header(self, nonce):
        sha = hashlib.sha256()
        sha.update(self.public_key)
        public_key_hash = sha.digest()
        # parent_hash = self.get_parent_hash()
        parent_hash = [
            b'G\xfdk\x88\xda5\xff\x8c\x97t\x9f\xcb\xe0\xa8\x07S\x8b9t:.9\x1d\xee\xf4\xb1\xda\xd1r\xaf\xfcu'
        ]
        macro_block_header = MacroBlockHeader(
            0,  # todo: get index
            timestamp=time.time(),
            public_key_hash=public_key_hash,
            parent_hash=parent_hash,
            nonce=nonce)
        return macro_block_header

    def make_micro_block(self, nonce) -> MicroBlock:
        trans = self.__get_trans()
        if len(trans) > 1000:
            trans = trans[:1000]
        info = Attachment()
        info.add_data(b'mined by ' + self.name.encode())
        info.ready()

        micro_block = MicroBlock(
            0,  # todo: get index
            timestamp=time.time(),
            blockdata=BlockData(trans, info),
            previous_hash=bytes(32),
            nonce=nonce)
        return micro_block

    def __get_trans(self) -> List[Transaction]:
        # self.chainbase_address
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_TRANS_READ, b''))
            *_, msgtype, content = recv_parser(s)

            trans = []

            if msgtype == MsgType.TYPE_RESPONSE_OK:
                trans += batch_parser(content)

            private_key = ec.generate_private_key(ec.SECP256K1,
                                                  default_backend())
            public_key = private_key.public_key()
            public_key = public_key.public_bytes(
                Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
            sha = hashlib.sha256()
            sha.update(public_key)
            public_key_hash = sha.digest()
            ipt = TransInput([(public_key_hash, 0)], public_key_hash)
            fd_ = open('ad1.txt', 'r')
            for index, line in enumerate(fd_.readlines()):
                if index == 0:
                    line = line.rstrip()
                    pr, pu = line.split('ENDDING')
                    temp = bytes(pu[2:-1], encoding='utf-8')
                    temp = temp.replace(b'\r\n', b'\n')
                    public_key = temp.replace(b'\\n', b'\n')
                    sha = hashlib.sha256()
                    sha.update(public_key)
                    public_key_hash = sha.digest()
                    break
            fd_.close()
            opt = TransOutput([(20, public_key_hash)])
            tran = Transaction(ipt, opt, 0)
            tran.ready(private_key)
            trans.append(tran.b)
            # result = self.get_miner_credit(public_key_hash, 5)
            # print(result)
            print('len = ', len(trans))
            return [Transaction.unpack(t) for t in trans]

    def add_macro_block_header(self,
                               macro_block_header: MacroBlockHeader) -> bool:
        """
        add the macro_block_header to the chainbase
        :param macro_block_header: binary macro_block_header
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(
                send_handler(MsgType.TYPE_MACRO_BLOCK_HEADER_WRITE,
                             macro_block_header.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    def add_micro_block(self, micro_block: MicroBlock) -> bool:
        """
        add the micro_block to the chainbase
        :param micro_block: binary micro_block
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(
                send_handler(MsgType.TYPE_MICRO_BLOCK_WRITE, micro_block.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    def add_macro_block_body(self, macro_block_body: MacroBlockBody) -> bool:
        """
        add the macro_block_body to the chainbase
        :param macro_block_body: binary macro_block_body
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(
                send_handler(MsgType.TYPE_MACRO_BLOCK_BODY_WRITE,
                             macro_block_body.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    def get_parent_hash(self) -> list:
        """
        get pivot chain macro_block_header and tips in local DAG
        :return: a list of hash (the first hash refers to voting edge, others refer to reference edges)
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_GET_PARENT_HASH, None))
            *_, msgtype, content = recv_parser(s)
        result = list()
        len_ = int(len(content) / 32)
        for i in range(len_):
            result.append(content[i * 32:(i + 1) * 32])
        return result

    @staticmethod
    def __keep_mining() -> bool:
        if MINE_SWITCH.value == 1:
            return True
        else:
            return False

    @staticmethod
    def __set_mine(state: bool):
        if state:
            MINE_SWITCH.value = 1
        else:
            MINE_SWITCH.value = 0

    @staticmethod
    def mine(target):
        """
        find a valid nonce
        :param target:
        :return: Tuple of (target, nonce)
        """
        seed()
        initial = randrange(0, MINE_TOP)  # [0, 2**32]
        print('mining')
        for nonce in range(initial, MINE_TOP):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return target, -1
            hash_ = PoWServer.__calc_hash(time.time(), nonce)

            if hash_ < target:
                return target, nonce

        for nonce in range(0, initial):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return target, -1

            hash_ = PoWServer.__calc_hash(time.time(), nonce)

            if hash_ < target:
                return target, nonce

    @staticmethod
    def __calc_hash(
            timestamp,
            nonce: int) -> bytes:  # calculate SHA256(SHA256(prev_hash+nonce))
        sha = hashlib.sha256()
        sha.update(struct.pack('=d', timestamp))
        sha.update(struct.pack('=I', nonce))
        hash_ = sha.digest()
        sha = hashlib.sha256()
        sha.update(hash_)
        hash_ = sha.digest()

        return hash_
Exemple #7
0
class PoWServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address_):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address_
        self.peer = PeerManager()
        self.workers = Pool()
        self.trans_size = 0
        self.trans_size_ = 0
        self.cache = []
        self.block_received = []
        self.ass_chain = dict()

        super().__init__(server_address, handler, bind_and_activate=True)

    def serve_forever(self, poll_interval=0.5):

        self.init_prev_hash()

        self.start_miner()

        super().serve_forever()

    def start_miner(self):
        self.__set_mine(True)
        print('ok')
        ore = self.workers.apply_async(mine,
                                       args=(self.prev_hash, self.target),
                                       callback=partial(
                                           self.on_new_block_mined, self))

    @staticmethod
    def stop_miner():
        PoWServer.__set_mine(False)

    @staticmethod
    def on_new_block_mined(self: 'PoWServer', result):
        """
        try to add the block that the server itself mines to the chainbase
        :param self: the instance of PoWServer
        :param result: Future object contains mining result
        :return: None
        """
        prev_hash_, target_, nonce = result

        if prev_hash_ == self.prev_hash and target_ == self.target:

            if nonce < 0:  # mining is stopped by stop_miner
                return

            block = self.make_block(
                nonce)  # mining stops because a nonce have been found
            # lightblock = self.make_lightblock(block)
            self.trans_size += sys.getsizeof(block.b)
            # self.trans_size_ += sys.getsizeof(lightblock.b)
            print('block mined:')
            print(block.show_block())
            # print(self.trans_size)
            # print(self.trans_size_)
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK,
                                    content=block.b)
            # self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_LIGHTBLOCK, content=lightblock.b)
            if self.add_block(block) is True:
                self.prev_hash = block.hash
                self.start_miner()  # start a new miner
            else:
                print('\n\nwrong\n\n')
                self.init_target()
                self.start_miner()

    def on_new_block_received(self, block):
        print('block received')
        block = Block.unpack(block)
        if self.add_block(block):
            self.block_received.append(block)
            print('try to stop current miner')
            self.stop_miner()  # stop current miner
            self.prev_hash = block.hash
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK,
                                    content=block.b)
            print('try to start a new miner')
            self.start_miner()  # start a new miner

    # def on_new_lightblock_received(self, lightblock):
    #     print('lightblock received')
    #     lightblock = LightBlock.unpack(lightblock)
    #     if self.add_lightblock(lightblock):
    #         print('add light block succeed')
    #         print('try to stop current miner')
    #         self.stop_miner()  # stop current miner
    #         self.prev_hash = lightblock.hash
    #         self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_LIGHTBLOCK, content=lightblock.b)
    #         print('try to start a new miner')
    #         self.start_miner()  # start a new miner

    def init_prev_hash(self):
        """get previous hash from chainbase when initializing"""
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_PREVIOUS_HASH, b''))
            *_, msgtype, content = recv_parser(s)

            self.prev_hash = content
            print('prev_hash = ', content)

    def init_target(self):
        pass

    def make_block(self, nonce) -> Block:
        trans = self.__get_trans()
        if len(trans) > 1000:
            trans = trans[:1000]
        info = Attachment()
        info.add_data(b'mined by ' + self.name.encode())
        info.ready()

        block = Block(
            0,  # todo: get index
            timestamp=time.time(),
            blockdata=BlockData(trans, info),
            previous_hash=self.prev_hash,
            nonce=nonce)
        return block

    # @staticmethod
    # def make_lightblock(block) -> LightBlock:
    #     trans_txid = []
    #     print('making lightblock.....', time.time())
    #     for t in block.data.trans:
    #         trans_txid.append(t.txid)
    #     info = block.data.attachment
    #
    #     lightblock = LightBlock(0,  # todo: get index
    #                             timestamp=block.timestamp,
    #                             lightblockdata=LightBlockData(trans_txid, info),
    #                             previous_hash=block.previous_hash,
    #                             hash=block.hash,
    #                             nonce=block.nonce)
    #     print('ok', time.time())
    #     return lightblock

    def add_block(self, block: Block) -> bool:
        """
        add the block to the chainbase
        :param block: binary block
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_WRITE, block.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    # def add_lightblock(self, lightblock: LightBlock) -> bool:
    #     """
    #     add the lightblock to the chainbase
    #     :param lightblock: binary lightblock
    #     :return: True | False
    #     """
    #     with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
    #         s.connect(self.chainbase_address)
    #         s.sendall(send_handler(MsgType.TYPE_LIGHTBLOCK_WRITE, lightblock.b))
    #         *_, msgtype, content = recv_parser(s)
    #
    #     return msgtype == MsgType.TYPE_RESPONSE_OK

    def acquire_block(self):
        pass

    @staticmethod
    def __keep_mining() -> bool:
        if MINE_SWITCH.value == 1:
            return True
        else:
            return False

    @staticmethod
    def __set_mine(state: bool):
        if state:
            MINE_SWITCH.value = 1
        else:
            MINE_SWITCH.value = 0

    def __get_trans(self) -> List[Transaction]:
        # self.chainbase_address
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_TRANS_READ, b''))
            *_, msgtype, content = recv_parser(s)

            trans = []

            if msgtype == MsgType.TYPE_RESPONSE_OK:
                trans += batch_parser(content)

            private_key = ec.generate_private_key(ec.SECP256K1,
                                                  default_backend())
            public_key = private_key.public_key()
            public_key = public_key.public_bytes(
                Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
            sha = hashlib.sha256()
            sha.update(public_key)
            public_key_hash = sha.digest()
            ipt = TransInput([(TXID(public_key_hash), OUTPUT_INDEX(0))],
                             public_key_hash)
            fd_ = open('ad1.txt', 'r')
            for index, line in enumerate(fd_.readlines()):
                if index == 0:
                    line = line.rstrip()
                    pr, pu = line.split('ENDDING')
                    temp = bytes(pu[2:-1], encoding='utf-8')
                    temp = temp.replace(b'\r\n', b'\n')
                    public_key = temp.replace(b'\\n', b'\n')
                    sha = hashlib.sha256()
                    sha.update(public_key)
                    public_key_hash = sha.digest()
                    break
            fd_.close()
            opt = TransOutput([(ASSET(20), public_key_hash)])
            tran = Transaction(ipt, opt, 0)
            tran.ready(private_key)
            trans.append(tran.b)
            # result = self.get_miner_credit(public_key_hash, 5)
            # print(result)
            print('len = ', len(trans))
            return [Transaction.unpack(t) for t in trans]

    @staticmethod
    def mine(prev_hash, target):
        """
        find a valid nonce
        :param prev_hash:
        :param target:
        :return: Tuple of (prev_hash, target, nonce)
        """
        seed()
        initial = randrange(0, MINE_TOP)  # [0, 2**32]
        print('mining')

        for nonce in range(initial, MINE_TOP):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return prev_hash, target, -1
            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

        for nonce in range(0, initial):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return prev_hash, target, -1

            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

    @staticmethod
    def __calc_hash(
            prev_hash,
            nonce: int) -> bytes:  # calculate SHA256(SHA256(prev_hash+nonce))
        sha = hashlib.sha256()
        sha.update(prev_hash)
        sha.update(struct.pack('=I', nonce))
        hash_ = sha.digest()
        sha = hashlib.sha256()
        sha.update(hash_)
        hash_ = sha.digest()

        return hash_
Exemple #8
0
class PoWServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def __init__(self, server_name: str, server_address, handler,
                 chainbase_address):
        self.name = server_name
        self.prev_hash = b''
        self.target = (2**234 - 1).to_bytes(32, byteorder='big')
        self.chainbase_address = chainbase_address
        self.peer = PeerManager()
        self.workers = Pool()
        self.block_num = 0
        self.flag = 0

        super().__init__(server_address, handler, bind_and_activate=True)

    def serve_forever(self, poll_interval=0.5):

        self.init_prev_hash()

        self.start_miner()

        super().serve_forever()

    def start_miner(self):
        self.init_target()

        self.workers.apply_async(mine,
                                 args=(
                                     self.prev_hash,
                                     self.target,
                                 ),
                                 callback=partial(self.on_new_block_mined,
                                                  self))

    @staticmethod
    def stop_miner():
        PoWServer.__set_mine(False)

    @staticmethod
    def on_new_block_mined(self: 'PoWServer', result):
        """
        try to add the block that the server itself mines to the chainbase
        :param self: the instance of PoWServer
        :param result: Future object contains mining result
        :return: None
        """
        prev_hash_, target_, nonce = result
        if prev_hash_ == self.prev_hash and target_ == self.target:

            if nonce < 0:  # mining is stopped by stop_miner

                return
            block = self.make_block(
                nonce)  # mining stops because a nonce have been found
            # lightblock = self.make_lightblock(block)
            # print('hash = ', lightblock.hash)
            print('hash = ', block.hash)
            print('block mined:')
            print(block.show_block())
            if self.block_num <= 0:
                self.block_num = 1
            else:
                self.block_num += 1
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK,
                                    content=block.b)
            # self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_LIGHTBLOCK, content=lightblock.b)
            assert self.add_block(block) is True
            print('true')
            self.prev_hash = block.hash
            self.start_miner()  # start a new miner

    def on_new_block_received(self, block):
        print('block received')
        block = Block.unpack(block)

        if self.add_block(block):
            print('try to stop current miner')
            self.stop_miner()  # stop current miner
            self.prev_hash = block.hash
            # self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_BLOCK, content=block.b)
            print('try to start a new miner')
            if self.block_num <= 0:
                self.block_num -= 1
            else:
                self.block_num = -1
            self.start_miner()  # start a new miner

    def on_new_lightblock_received(self, lightblock):
        print('lightblock received')
        lightblock = LightBlock.unpack(lightblock)
        print(lightblock.hash)
        if self.add_lightblock(lightblock):
            print('add light block succeed')
            print('try to stop current miner')
            self.stop_miner()  # stop current miner
            self.prev_hash = lightblock.hash
            self.peer.sendall_block(msgtype=MsgType.TYPE_NEW_LIGHTBLOCK,
                                    content=lightblock.b)
            print('try to start a new miner')
            if self.block_num <= 0:
                self.block_num -= 1
            else:
                self.block_num = -1
            self.start_miner()  # start a new miner

    def init_prev_hash(self):
        """get previous hash from chainbase when initializing"""
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_PREVIOUS_HASH, b''))
            *_, msgtype, content = recv_parser(s)

            self.prev_hash = content
            print('prev_hash = ', content)

    def init_target(self):
        result = self.get_miner_credit(miner_public_key_hash, 0)
        cc = struct.unpack('=d', result[0])[0]
        p = struct.unpack('=d', result[1])[0]

        print('miner_cc =', cc)
        print('miner_time = ', p)
        print(self.block_num)
        if self.block_num > 0:
            a = b = 1
            self.__set_mine(False)
        else:
            miner_time = int(time.time() - p)
            if miner_time == 0:
                miner_time = 1
            if cc > 5:
                miner_cc = cc
            else:
                miner_cc = 1
            a = int(miner_cc * miner_time * 2 * 0.001) + 1
            b = int(miner_time * miner_cc * 10)
            self.__set_mine(True)
        print('a = ', a)
        if self.flag == 0:
            self.target = (2**233 - 1).to_bytes(32, byteorder='big')
            self.flag = 1
        else:
            self.target = (a * 2**233 + b * 30**40).to_bytes(32,
                                                             byteorder='big')
        # self.target = (2 ** 236).to_bytes(32, byteorder='big')
        print('end')

    def make_block(self, nonce) -> Block:
        trans = self.__get_trans()
        info = Attachment()
        info.add_data(b'mined by ' + self.name.encode())
        info.ready()

        block = Block(
            0,  # todo: get index
            timestamp=time.time(),
            blockdata=BlockData(trans, info),
            previous_hash=self.prev_hash,
            nonce=nonce)
        return block

    # @staticmethod
    # def make_lightblock(block) -> LightBlock:
    #     trans_txid = []
    #     print('making lightblock.....')
    #     for t in block.data.trans:
    #         trans_txid.append(t.txid)
    #     info = block.data.attachment
    #
    #     lightblock = LightBlock(0,  # todo: get index
    #                             timestamp=block.timestamp,
    #                             lightblockdata=LightBlockData(trans_txid, info),
    #                             previous_hash=block.previous_hash,
    #                             hash=block.hash,
    #                             nonce=block.nonce)
    #     return lightblock

    def add_block(self, block: Block) -> bool:
        """
        add the block to the chainbase
        :param block: binary block
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_BLOCK_WRITE, block.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    def add_lightblock(self, lightblock: LightBlock) -> bool:
        """
        add the lightblock to the chainbase
        :param lightblock: binary lightblock
        :return: True | False
        """
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_LIGHTBLOCK_WRITE,
                                   lightblock.b))
            *_, msgtype, content = recv_parser(s)

        return msgtype == MsgType.TYPE_RESPONSE_OK

    def acquire_block(self):
        pass

    @staticmethod
    def __keep_mining() -> bool:
        if MINE_SWITCH.value == 1:
            return True
        else:
            return False

    @staticmethod
    def __set_mine(state: bool):
        if state:
            MINE_SWITCH.value = 1
        else:
            MINE_SWITCH.value = 0

    def __get_trans(self) -> List[Transaction]:

        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(send_handler(MsgType.TYPE_TRANS_READ, b''))
            *_, msgtype, content = recv_parser(s)

            trans = []

            if msgtype == MsgType.TYPE_RESPONSE_OK:
                trans += batch_parser(content)

            private_key = ec.generate_private_key(ec.SECP256K1,
                                                  default_backend())
            public_key = private_key.public_key()
            public_key = public_key.public_bytes(
                Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
            sha = hashlib.sha256()
            sha.update(public_key)
            public_key_hash = sha.digest()
            ipt = TransInput([(TXID(public_key_hash), OUTPUT_INDEX(0))],
                             public_key_hash)

            opt = TransOutput([(ASSET(20),
                                PUBLIC_KEY_HASH(miner_public_key_hash))])
            tran = Transaction(ipt, opt, 0)
            tran.ready(private_key)
            trans.append(tran.b)
            content = trans_to_json(tran)
            requests.post('http://127.0.0.1:23390/transaction_post',
                          data=content)
            if self.block_num < -50:
                self.get_miner_credit(miner_public_key_hash,
                                      0.5 / self.block_num)
            else:
                self.get_miner_credit(miner_public_key_hash, 0.5)
            # print(struct.unpack('=d', result[0]), struct.unpack('=d', result[1]))
            return [Transaction.unpack(t) for t in trans]

    @staticmethod
    def mine(prev_hash, target):
        """
        find a valid nonce
        :param prev_hash:
        :param target:
        :return: Tuple of (prev_hash, target, nonce)
        """
        seed()
        print('in')
        initial = randrange(0, MINE_TOP)  # [0, 2**32]
        print('mining')
        start = time.time()
        for nonce in range(initial, MINE_TOP):
            if not PoWServer.__keep_mining():
                print('stop mining')
                end = time.time()

                return prev_hash, target, -1
            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

        for nonce in range(0, initial):
            if not PoWServer.__keep_mining():
                print('stop mining')
                return prev_hash, target, -1
            hash_ = PoWServer.__calc_hash(prev_hash, nonce)

            if hash_ < target:
                return prev_hash, target, nonce

    @staticmethod
    def __calc_hash(
            prev_hash,
            nonce: int) -> bytes:  # calculate SHA256(SHA256(prev_hash+nonce))
        sha = hashlib.sha256()
        sha.update(prev_hash)
        sha.update(struct.pack('=I', nonce))
        hash_ = sha.digest()
        sha = hashlib.sha256()
        sha.update(hash_)
        hash_ = sha.digest()

        return hash_

    def get_miner_credit(self, public_key_hash, num) -> List:
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            p = time.time() * 100000000
            # print(struct.pack('=d', p))
            s.sendall(
                send_handler(
                    MsgType.TYPE_MINER_CREDIT,
                    batch_handler([
                        public_key_hash,
                        struct.pack('=d', num),
                        struct.pack('=d', p)
                    ])))
            *_, msgtype, content = recv_parser(s)
            result = batch_parser(content)
        return result

    def put_miner_use(self, usage, useful) -> List:
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.connect(self.chainbase_address)
            s.sendall(
                send_handler(
                    MsgType.TYPE_MINER_USE,
                    batch_handler(
                        [struct.pack('=d', usage),
                         struct.pack('=d', useful)])))
            *_, msgtype, content = recv_parser(s)
            result = batch_parser(content)
        return result