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)
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 __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_
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_
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