async def submitblock(block_hex_or_obj, **kwargs): if isinstance(block_hex_or_obj, str): block_bin = unhexlify(block_hex_or_obj.encode()) # Block mined_block = Block(binary=block_bin[:80]) if mined_block.previous_hash != builder.best_block.hash: return 'PreviousHash don\'t match.' previous_block = builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) # tx length storage_flag = int.from_bytes(block_bin[80:81], 'little') if storage_flag < 0xfd: tx_len = storage_flag pos = 81 elif storage_flag == 0xfd: tx_len = int.from_bytes(block_bin[81:83], 'little') pos = 83 elif storage_flag == 0xfe: tx_len = int.from_bytes(block_bin[81:85], 'little') pos = 85 else: # == 0xff tx_len = int.from_bytes(block_bin[81:89], 'little') pos = 89 if F_HEAVY_DEBUG: logging.debug("RpcSubmit block: pos={}, tx_len={}".format( pos, tx_len)) # correct txs while len(block_bin) > pos: tx = TX() tx.b = block_bin tx.deserialize(first_pos=pos, f_raise=False) if tx.version != __chain_version__: return 'tx_ver do not match [{}!={}]'.format( tx.version, __chain_version__) pos += len(tx.b) mined_block.txs.append( tx_builder.get_tx(txhash=tx.hash, default=tx)) # check if tx_len != len(mined_block.txs): return 'Do not match txlen [{}!={}]'.format( tx_len, len(mined_block.txs)) if pos != len(block_bin): return 'Do not match pos [{}!={}]'.format(pos, len(block_bin)) elif isinstance(block_hex_or_obj, Block): mined_block = block_hex_or_obj previous_block = builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) else: return 'Unknown input? -> {}'.format(block_hex_or_obj) mined_block.update_pow() if mined_block.pow_check(): confirmed_generating_block(mined_block) return None # accepted else: return 'not satisfied work.'
def get_bias_by_hash(previous_hash, consensus): N = 30 # target blocks if consensus == V.BLOCK_BASE_CONSENSUS: return 1.0 elif consensus == C.BLOCK_GENESIS: return 1.0 elif (consensus, previous_hash) in cashe: return cashe[(consensus, previous_hash)] elif previous_hash == GENESIS_PREVIOUS_HASH: return 1.0 base_diffs = list() target_diffs = list() target_hash = previous_hash while True: target_block = builder.get_block(target_hash) if target_block is None: return 1.0 target_hash = target_block.previous_hash if target_hash == GENESIS_PREVIOUS_HASH: return 1.0 elif target_block.flag == V.BLOCK_BASE_CONSENSUS and N > len(base_diffs): base_diffs.append(bits2target(target_block.bits) * (N-len(base_diffs))) elif target_block.flag == consensus and N > len(target_diffs): target_diffs.append(bits2target(target_block.bits) * (N-len(target_diffs))) if len(base_diffs) >= N and len(target_diffs) >= N: break bias = sum(base_diffs) / sum(target_diffs) cashe[(consensus, previous_hash)] = bias if Debug.F_SHOW_DIFFICULTY: print("bias", bias, hexlify(previous_hash).decode()) return bias
def start_tx2index(start_hash=None, start_tx=None): if start_hash: start_tx = tx_builder.get_tx(txhash=start_hash) block = builder.get_block(blockhash=builder.get_block_hash(height=start_tx.height)) if block is None: raise BlockChainError('Not found block of start_tx included? {}'.format(start_tx)) if start_tx not in block.txs: raise BlockChainError('Not found start_tx in block? {}'.format(block)) return start_tx.height * 0xffffffff + block.txs.index(start_tx)
def fill_newblock_info(data): new_block = Block(binary=data['block']) logging.debug("Fill newblock={}".format(hexlify(new_block.hash).decode())) proof = TX(binary=data['proof']) new_block.txs.append(proof) new_block.flag = data['block_flag'] proof.signature = data['sign'] # Check the block is correct info if not new_block.pow_check(): raise BlockChainError('Proof of work is not satisfied.') my_block = builder.get_block(new_block.hash) if my_block: raise BlockChainError('Already inserted block {}'.format(my_block)) before_block = builder.get_block(new_block.previous_hash) if before_block is None: logging.debug("Cannot find beforeBlock {}, try to ask outside node." .format(hexlify(new_block.previous_hash).decode())) # not found beforeBlock, need to check other node have the the block new_block.inner_score *= 0.70 # unknown previousBlock, score down before_block = make_block_by_node(blockhash=new_block.previous_hash) if not new_insert_block(before_block, time_check=True): # require time_check, it was generated only a few seconds ago # print([block for block in builder.chain.values()]) raise BlockChainError('Failed insert beforeBlock {}'.format(before_block)) new_height = before_block.height + 1 proof.height = new_height new_block.height = new_height # Append general txs for txhash in data['txs'][1:]: tx = tx_builder.get_tx(txhash) if tx is None: new_block.inner_score *= 0.75 # unknown tx, score down logging.debug("Unknown tx, try to download.") r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) if isinstance(r, str): raise BlockChainError('Failed unknown tx download "{}"'.format(r)) tx = TX(binary=r['tx']) tx.signature = r['sign'] check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) logging.debug("Success unknown tx download {}".format(tx)) tx.height = new_height new_block.txs.append(tx) return new_block
async def get_block_by_height(request): height = int(request.query.get('height', 0)) blockhash = builder.get_block_hash(height) if blockhash is None: return web.Response(text="Not found height.", status=400) block = builder.get_block(blockhash) data = block.getinfo() data['size'] = block.getsize() data['hex'] = hexlify(block.b).decode() return web_base.json_res(data)
def get_bits_by_hash(previous_hash, consensus): if Debug.F_CONSTANT_DIFF: return MAX_BITS, MAX_TARGET elif previous_hash == GENESIS_PREVIOUS_HASH: return MAX_BITS, MAX_TARGET elif (previous_hash, consensus) in cashe: return cashe[(previous_hash, consensus)] # Get best block time block_time = round(V.BLOCK_TIME_SPAN / V.BLOCK_CONSENSUS[consensus] * 100) # Get N, K params N, K = params(block_time) # Loop through N most recent blocks. "< height", not "<=". # height-1 = most recently solved rblock target_hash = previous_hash timestamp = list() target = list() j = 0 while True: target_block = builder.get_block(target_hash) if target_block is None: return MAX_BITS, MAX_TARGET if target_block.flag != consensus: target_hash = target_block.previous_hash continue if j == N + 1: break j += 1 timestamp.insert(0, target_block.time) target.insert(0, bits2target(target_block.bits)) target_hash = target_block.previous_hash if target_hash == GENESIS_PREVIOUS_HASH: return MAX_BITS, MAX_TARGET sum_target = t = j = 0 for i in range(N): solve_time = timestamp[i + 1] - timestamp[i] j += 1 t += solve_time * j sum_target += target[i + 1] # Keep t reasonable in case strange solvetimes occurred. if t < N * K // 3: t = N * K // 3 new_target = t * sum_target // K // N // N # convert new target to bits new_bits = target2bits(new_target) if Debug.F_SHOW_DIFFICULTY: print("ratio", C.consensus2name[consensus], new_bits, hexlify(previous_hash).decode()) cashe[(previous_hash, consensus)] = (new_bits, new_target) return new_bits, new_target
def _big_blocks(height): data = list() for i in range(20): blockhash = builder.get_block_hash(height + i) if blockhash is None: break block = builder.get_block(blockhash) if block is None: break txs = [(tx.b, tx.signature) for tx in block.txs] data.append((block.b, block.height, block.flag, txs)) # TODO:一度に送信できるBytesにチェック return data
async def get_block_by_hash(request): try: blockhash = request.query.get('hash') blockhash = unhexlify(blockhash.encode()) block = builder.get_block(blockhash) if block is None: return web.Response(text="Not found block.", status=400) data = block.getinfo() data['size'] = block.getsize() data['hex'] = hexlify(block.b).decode() return web_base.json_res(data) except Exception as e: return web_base.error_res()
def fill_newblock_info(data): new_block = Block(binary=data['block']) proof = TX(binary=data['proof']) new_block.txs.append(proof) new_block.flag = data['block_flag'] proof.signature = data['sign'] # Check the block is correct info if not new_block.pow_check(): raise BlockChainError('Proof of work is not satisfied.') if builder.get_block(new_block.hash): raise BlockChainError('Already inserted block.') before_block = builder.get_block(new_block.previous_hash) if before_block is None: raise BlockChainError('Not found beforeBlock {}.'.format( hexlify(new_block.previous_hash).decode())) new_height = before_block.height + 1 proof.height = new_height new_block.height = new_height # Append general txs for txhash in data['txs'][1:]: tx = tx_builder.get_tx(txhash) if tx is None: new_block.inner_score *= 0.75 # unknown tx, score down logging.debug("Unknown tx, try to download.") r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) if isinstance(r, str): raise BlockChainError( 'Failed unknown tx download "{}"'.format(r)) tx = TX(binary=r['tx']) tx.signature = r['sign'] check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) logging.debug("Success unknown tx download {}".format(tx)) tx.height = new_height new_block.txs.append(tx) return new_block
async def get_block_by_height(request): f_pickled = request.query.get('pickle', False) height = int(request.query['height']) blockhash = builder.get_block_hash(height) if blockhash is None: return web.Response(text="Not found height.", status=400) block = builder.get_block(blockhash) if f_pickled: block = pickle.dumps(block) return web_base.json_res(b64encode(block).decode()) data = block.getinfo() data['size'] = block.getsize() data['hex'] = hexlify(block.b).decode() return web_base.json_res(data)
def _block_by_hash(blockhash): block = builder.get_block(blockhash) if block is None: return 'Not found blockhash {}.'.format(hexlify(blockhash).decode()) txs = [{'tx': tx.b, 'sign': tx.signature} for tx in block.txs] send_data = { 'block': block.b, 'height': block.height, 'flag': block.flag, 'orphan': bool(block.f_orphan is True), 'next_hash': block.next_hash, 'difficulty': block.difficulty, 'txs': txs } return send_data
def make_block_by_node(blockhash): """ create Block by outside node """ r = ask_node(cmd=DirectCmd.BLOCK_BY_HASH, data={'blockhash': blockhash}) if isinstance(r, str): raise BlockChainError('make_block_by_node() failed, by "{}"'.format(hexlify(blockhash).decode(), r)) block = Block(binary=r['block']) block.flag = r['flag'] before_block = builder.get_block(blockhash=block.previous_hash) if before_block is None: raise BlockChainError('Not found BeforeBeforeBlock {}'.format(hexlify(block.previous_hash).decode())) block.height = before_block.height + 1 for tx in r['txs']: _tx = TX(binary=tx['tx']) _tx.height = block.height _tx.signature = tx['sign'] block.txs.append(_tx) return block
def try_emulate(start_tx, gas_limit=None, out=None): start_time = time.time() cxt = get_context('spawn') que = cxt.Queue() # out = out or io.StringIO() c_address, c_method, c_args, c_redeem = bjson.loads(start_tx.message) c_bin = get_contract_binary(c_address) assert c_bin, 'Not found c_bin of {}'.format(c_address) params = { 'sub_dir': V.SUB_DIR, 'genesis_block': builder.get_block(builder.get_block_hash(0)), 'c_bin': c_bin, 'c_address': c_address, 'c_method': c_method, 'args': c_args} p = cxt.Process(target=_work, args=(params, start_tx, que)) p.start() que_cmd, port = que.get(timeout=10) if que_cmd != CMD_PORT: raise TypeError('Not correct command="{}" data="{}"'.format(que_cmd, port)) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", port)) sock.settimeout(10) # Start emulation line = fee = 0 cmd = EMU_STEP error = None logging.debug("Start contract emulate port={}".format(port)) while True: try: msg_list = sock.recv(8192).decode(errors='ignore').replace("\r", "").split("\n") if len(msg_list) <= 1: sock.close() break elif len(msg_list) < 3: pass elif gas_limit and fee > gas_limit: error = 'reached gas limit. [{}>{}]'.format(fee, gas_limit) break elif cmd in (EMU_STEP, EMU_NEXT, EMU_UNTIL, EMU_RETURN): msg_list, path, words = msg_list[:-3], msg_list[-3][2:], msg_list[-2][3:] file = os.path.split(path)[1] print(file, words, path) if file.startswith('exe.py') and file.endswith('work_field()'): cmd = EMU_STEP # Start! print("start contract {}", file=out) elif file.startswith('contract('): line += 1 fee += 1 cmd = EMU_STEP else: cmd = EMU_NEXT # Add fee for func, gas in __price__.items(): if func in words and words.startswith('def ' + func + '('): fee += gas print("{}:read [{}] {} >> {}".format(line, fee, cmd, words), file=out) else: msg = ', '.join(msg_list) print("msg [{}] >>".format(cmd), msg, file=out) # response to work field sock.send((cmd + "\n").encode()) except ConnectionResetError: break except BaseException: error = str(traceback.format_exc()) break logging.debug("Finish contract {}Sec error:{}".format(round(time.time() - start_time, 3), error)) # Close emulation try: que_cmd, result = que.get_nowait() print(result) try: que.close() except: pass try: p.terminate() except: pass if que_cmd == CMD_ERROR: return False, result, fee, line elif que_cmd == CMD_SUCCESS: return True, result, fee, line except BaseException: return False, error, fee, line
def fast_sync_chain(): assert V.PC_OBJ is not None, "Need PeerClient start before." global f_changed_status back_thread = Thread(target=background_sync_chain, name='BackSync', daemon=True) back_thread.start() start = time() # 外部Nodeに次のBlockを逐一尋ねる failed_num = 0 before_block = builder.best_block index_height = before_block.height + 1 logging.debug("Start sync by {}".format(before_block)) while failed_num < 5: if index_height in block_stack: new_block = block_stack[index_height] with write_protect_lock: del block_stack[index_height] elif backend_processing_lock.locked(): sleep(0.1) continue else: with backend_processing_lock: logging.debug( "Stack blocks on front form {}".format(index_height)) r = ask_node(cmd=DirectCmd.BIG_BLOCKS, data={'height': index_height}) if isinstance(r, str): logging.debug("NewBLockGetError:{}".format(r)) before_block = builder.get_block( before_block.previous_hash) index_height = before_block.height + 1 failed_num += 1 continue elif isinstance(r, list): waiter = Waiter(0) waiter.set() waiter = put_to_block_stack(r, waiter) if waiter is None or len(block_stack) == 0: break else: waiter.wait() continue else: failed_num += 1 logging.debug("Not correct format BIG_BLOCKS.") continue # Base check base_check_failed_msg = None if before_block.hash != new_block.previous_hash: base_check_failed_msg = "Not correct previous hash {}".format( new_block) # proof of work check if not new_block.pow_check(): base_check_failed_msg = "Not correct work hash {}".format( new_block) # rollback if base_check_failed_msg is not None: before_block = builder.get_block(before_block.previous_hash) index_height = before_block.height + 1 failed_num += 1 for height in tuple(block_stack.keys()): if height >= index_height: del block_stack[height] logging.debug(base_check_failed_msg) continue # TX check if len(new_block.txs) > 1: with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() for tx in new_block.txs: if tx.type in (C.TX_POS_REWARD, C.TX_POW_REWARD): continue check_tx(tx=tx, include_block=None) tx_builder.put_unconfirmed(tx=tx, outer_cur=cur) db.commit() # Block check check_block(new_block) for tx in new_block.txs: tx.height = new_block.height check_tx(tx=tx, include_block=new_block) # Chainに挿入 builder.new_block(new_block) for tx in new_block.txs: user_account.affect_new_tx(tx) builder.batch_apply() f_changed_status = True # 次のBlock failed_num = 0 before_block = new_block index_height = before_block.height + 1 # ロギング if index_height % 100 == 0: logging.debug("Update block {} now...".format(index_height + 1)) # Unconfirmed txを取得 logging.info("Finish get block, next get unconfirmed.") r = None while not isinstance(r, dict): r = ask_node(cmd=DirectCmd.UNCONFIRMED_TX, f_continue_asking=True) for txhash in r['txs']: if txhash in tx_builder.unconfirmed: continue try: r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) tx = TX(binary=r['tx']) tx.signature = r['sign'] check_tx_time(tx) check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) except BlockChainError: logging.debug("Failed get unconfirmed {}".format( hexlify(txhash).decode())) # 最終判断 reset_good_node() set_good_node() my_best_height = builder.best_block.height best_height_on_network, best_hash_on_network = get_best_conn_info() if best_height_on_network <= my_best_height: logging.info( "Finish update chain data by network. {}Sec [{}<={}]".format( round(time() - start, 1), best_height_on_network, my_best_height)) return True else: logging.debug("Continue update chain, {}<={}".format( best_height_on_network, my_best_height)) return False