def subtract_fee_from_user_balance(tx: TX): """subtract fee from user's sending outputs""" subtract_fee = tx.gas_amount * tx.gas_price f_subtracted = False f_added = False for index, (address, coin_id, amount) in enumerate(tx.outputs): if coin_id != 0: continue elif amount < subtract_fee: continue elif not f_added and address == DUMMY_REDEEM_ADDRESS: # add used fee to redeem output tx.outputs[index] = (address, coin_id, amount + subtract_fee) f_added = True elif not f_subtracted and address != DUMMY_REDEEM_ADDRESS: # subtract used fee from sending output tx.outputs[index] = (address, coin_id, amount - subtract_fee) f_subtracted = True else: continue # check if f_subtracted is False or f_added is False: raise BlockChainError('failed to subtract fee sub={} add={} fee={}' .format(f_subtracted, f_added, subtract_fee)) return subtract_fee
def new_tx(data): try: new_tx = TX(binary=data['tx']) new_tx.signature = data['sign'] check_tx_time(new_tx) check_tx(tx=new_tx, include_block=None) if new_tx.type in (C.TX_VALIDATOR_EDIT, C.TX_CONCLUDE_CONTRACT) and new_tx.hash in tx_builder.unconfirmed: # marge contract signature original_tx = tx_builder.unconfirmed[new_tx.hash] new_signature = list(set(new_tx.signature) | set(original_tx.signature)) original_tx.signature = new_signature logging.info("Marge contract tx {}".format(new_tx)) else: # normal tx tx_builder.put_unconfirmed(new_tx) update_mining_staking_all_info() logging.info("Accept new tx {}".format(new_tx)) return True except BlockChainError as e: error = 'Failed accept new tx "{}"'.format(e) logging.error(error) return False except Exception: error = "Failed accept new tx" logging.error(error, exc_info=True) return False
async def sign_raw_tx(request): post = await web_base.content_type_json_check(request) try: binary = unhexlify(post['hex'].encode()) other_pairs = dict() for sk in post.get('pairs', list()): pk = public_key(sk=sk) ck = get_address(pk=pk, prefix=V.BLOCK_PREFIX) other_pairs[ck] = (pk, sign(msg=binary, sk=sk, pk=pk)) tx = TX(binary=binary) for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] try: tx.signature.append( message2signature(raw=tx.b, address=address)) except BlockChainError: if address not in other_pairs: raise BlockChainError( 'Not found secret key "{}"'.format(address)) tx.signature.append(other_pairs[address]) data = tx.getinfo() return web_base.json_res({ 'hash': data['hash'], 'signature': data['signature'], 'hex': hexlify(tx.b).decode() }) except BaseException: return web_base.error_res()
def check_new_tx(tx: TX): if tx.height is not None: log.error('New tx, but is already confirmed, {}'.format(tx)) return elif tx.message_type != C.MSG_MSGPACK: return elif tx.type == C.TX_CONCLUDE_CONTRACT: # 十分な署名が集まったら消す c_address, start_hash, c_storage = tx.encoded_message() v = get_validator_by_contract_info(c_address=c_address, start_hash=start_hash, stop_txhash=tx.hash) related_list = check_related_address(v.validators) if related_list: data = (time(), tx, related_list, c_address, start_hash, c_storage) watching_tx[tx.hash] = data if not stream.is_disposed: stream.on_next((C_Conclude, False, data)) elif tx.type == C.TX_VALIDATOR_EDIT: # 十分な署名が集まったら消す v_address, new_address, flag, sig_diff = tx.encoded_message() v = get_validator_object(v_address=v_address, stop_txhash=tx.hash) related_list = check_related_address(v.validators) if related_list: data = (time(), tx, related_list, v_address, new_address, flag, sig_diff) watching_tx[tx.hash] = data if not stream.is_disposed: stream.on_next((C_Validator, False, data)) else: pass
def load_boot_file(): normal_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'boot.dat') extra_path = os.path.join(V.DB_HOME_DIR, 'boot.dat') if os.path.exists(normal_path): with open(normal_path, mode='br') as fp: data = bjson.loads( b64decode(fp.read().replace(b'\n', b'').replace(b'\r', b''))) elif os.path.exists(extra_path): with open(extra_path, mode='br') as fp: data = bjson.loads( b64decode(fp.read().replace(b'\n', b'').replace(b'\r', b''))) else: raise FileNotFoundError('Cannot find boot.dat "{}" or "{}" ?'.format( normal_path, extra_path)) genesis_block = Block(binary=data['block']) genesis_block.flag = C.BLOCK_GENESIS genesis_block.height = 0 for b_tx in data['txs']: tx = TX(binary=b_tx) tx.height = 0 genesis_block.txs.append(tx) connections = data.get('connections', list()) network_ver = data['network_ver'] return genesis_block, network_ver, connections
def put_to_block_stack(r, before_waiter): block_tmp = dict() batch_txs = list() for block_b, block_height, block_flag, txs in r: block = Block(binary=block_b) block.height = block_height block.flag = block_flag for tx_b, tx_signature in txs: tx = TX(binary=tx_b) tx.height = None tx.signature = tx_signature tx_from_database = tx_builder.get_tx(txhash=tx.hash) if tx_from_database: block.txs.append(tx_from_database) else: block.txs.append(tx) block_tmp[block_height] = block batch_txs.extend(block.txs) # check if len(block_tmp) == 0: return None batch_sign_cashe(batch_txs) before_waiter.wait() with write_protect_lock: block_stack.update(block_tmp) return batch_workhash(tuple(block_tmp.values()))
def finish_contract_tx(start_tx, f_limit=True): assert start_tx.height is None, 'StartTX height is None.' assert P.VALIDATOR_OBJ, 'You are not a validator.' c_address, c_data, c_args, c_redeem = bjson.loads(start_tx.message) if f_limit: gas_limit = start_tx.gas_amount - start_tx.getsize() else: gas_limit = None status, result, estimate_gas, line = try_emulate(start_tx, gas_limit) if status: # 成功時 outputs, cs_result = result if cs_result is None: message = bjson.dumps((True, start_tx.hash, None), compress=False) else: cs = get_contract_storage(c_address) cs_diff = cs.diff_dev(new_key_value=cs_result.key_value) message = bjson.dumps((True, start_tx.hash, cs_diff), compress=False) try: check_output_format(outputs) except BlockChainError as e: logging.debug("Contract failed `emulate success` {}".format(e)) return failed_finish_tx(start_tx), estimate_gas else: # 失敗時 outputs = None logging.debug("Contract failed `emulate failed` {}".format(result)) return failed_finish_tx(start_tx), 0 # finish tx 作成 finish_tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_FINISH_CONTRACT, 'time': start_tx.time, 'deadline': start_tx.deadline, 'inputs': list(), 'outputs': outputs or list(), 'gas_price': start_tx.gas_price, 'gas_amount': 1, 'message_type': C.MSG_BYTE, 'message': message }) to_user_redeem = (c_redeem, 0, 0) finish_tx.outputs.append(to_user_redeem) # gas_amountを返す # fill input/output # TODO: c_redeemを用いてFeeを還元 redeem_gas = start_tx.gas_amount - start_tx.getsize() - estimate_gas if not fill_inputs_outputs(finish_tx, c_address, start_tx.hash, redeem_gas): return failed_finish_tx(start_tx), estimate_gas redeem_idx = finish_tx.outputs.index(to_user_redeem) redeem_amount = -1 * finish_tx.gas_amount * finish_tx.gas_price finish_tx.outputs[redeem_idx] = (c_redeem, 0, redeem_amount) replace_redeem_dummy_address(finish_tx, c_address) finish_tx.serialize() return finish_tx, estimate_gas
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.'
async def broadcast_tx(request): post = await web_base.content_type_json_check(request) try: binary = unhexlify(post['hex'].encode()) new_tx = TX(binary=binary) new_tx.signature = [(pk, unhexlify(sign.encode())) for pk, sign in post['signature']] if not send_newtx(new_tx=new_tx): raise BaseException('Failed to send new tx.') return web_base.json_res({'txhash': hexlify(new_tx.hash).decode()}) except BaseException: return web_base.error_res()
async def sign_raw_tx(request): post = await utils.content_type_json_check(request) try: binary = a2b_hex(post['hex']) other_pairs = dict() for sk in post.get('pairs', list()): sk = a2b_hex(sk) keypair: PyKeyPair = PyKeyPair.from_secret_key(sk) r, s = keypair.get_single_sign(binary) pk = keypair.get_public_key() ck = get_address(pk=pk, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER) other_pairs[ck] = (pk, r, s) tx = TX.from_binary(binary=binary) for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] try: tx.signature.append( sign_message_by_address(raw=tx.b, address=address)) except BlockChainError: if address not in other_pairs: raise BlockChainError( 'Not found secret key "{}"'.format(address)) tx.signature.append(other_pairs[address]) data = tx.getinfo() return utils.json_res({ 'hash': data['hash'], 'signature': data['signature'], 'hex': tx.b.hex() }) except Exception: return utils.error_res()
def create_signed_tx_as_validator(tx: TX): tx = deepcopy(tx) if tx.type == C.TX_VALIDATOR_EDIT: v_address, new_address, flag, sig_diff = tx.encoded_message() v = get_validator_object(v_address=v_address, stop_txhash=tx.hash) elif tx.type == C.TX_CONCLUDE_CONTRACT: c_address, start_hash, c_storage = tx.encoded_message() v = get_validator_by_contract_info(c_address=c_address, start_hash=start_hash) else: raise BlockChainError('Not found tx type {}'.format(tx)) # sign and check how many add signs if setup_contract_signature(tx, v.validators) == 0: raise BlockChainError( 'Cannot sign, you are not validator or already signed') return tx
def check_tx_create_contract(tx: TX, include_block: Block): if len(tx.inputs) == 0 or len(tx.outputs) == 0: raise BlockChainError('No inputs or outputs.') elif tx.message_type != C.MSG_BYTE: raise BlockChainError('create contract tx is bytes msg.') elif V.BLOCK_CONTRACT_PREFIX is None: raise BlockChainError('Not set contract prefix ?') elif V.BLOCK_CONTRACT_PREFIX == V.BLOCK_PREFIX: raise BlockChainError('normal prefix same with contract prefix.') # GAS量チェック estimate_gas = tx.getsize() + C.CONTRACT_CREATE_FEE if estimate_gas > tx.gas_amount: raise BlockChainError('Insufficient gas [{}>{}]'.format( estimate_gas, tx.gas_amount)) # Contractをデコードできるか c_address, c_bin, c_cs = bjson.loads(tx.message) binary2contract(c_bin) # ContractStorageの初期値チェック if c_cs: for k, v in c_cs.items(): if not isinstance(k, bytes) or not isinstance(v, bytes): raise BlockChainError('cs format is wrong. {}'.format(c_cs)) if not is_address(c_address, V.BLOCK_CONTRACT_PREFIX): raise BlockChainError('Is not contract address. {}'.format(c_address)) # 既に登録されていないかチェック cs = get_contract_storage(c_address, include_block) if cs.version != 0: raise BlockChainError('Already created contract. {}'.format(tx))
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
async def broadcast_tx(request): start = time() post = await web_base.content_type_json_check(request) try: binary = unhexlify(post['hex'].encode()) new_tx = TX(binary=binary) new_tx.signature = [(pk, unhexlify(_sign.encode())) for pk, _sign in post['signature']] if not send_newtx(new_tx=new_tx): raise BaseException('Failed to send new tx.') return web_base.json_res({ 'hash': hexlify(new_tx.hash).decode(), 'gas_amount': new_tx.gas_amount, 'gas_price': new_tx.gas_price, 'fee': new_tx.gas_amount * new_tx.gas_price, 'time': round(time() - start, 3) }) except BaseException: return web_base.error_res()
def read_tx(self, txhash): if self.is_batch_thread() and txhash in self.batch['_tx']: b = self.batch['_tx'][txhash] elif is_plyvel: b = self._tx.get(txhash, default=None) else: b = self._tx.Get(txhash, default=None) if b is None: return None b = bytes(b) height, _time, bin_len, sign_len = struct_tx.unpack_from(b) b_tx = b[16:16+bin_len] b_sign = b[16+bin_len:16+bin_len+sign_len] assert len(b) == 16+bin_len+sign_len, 'Wrong len [{}={}]'\ .format(len(b), 16+bin_len+sign_len) tx = TX(binary=b_tx) tx.height = height tx.signature = bin2signature(b_sign) return tx
def new_tx(data): try: new_tx = TX(binary=data['tx']) new_tx.signature = data['sign'] check_tx(tx=new_tx, include_block=None) check_tx_time(new_tx) tx_builder.put_unconfirmed(new_tx) update_mining_staking_all_info() logging.info("Accept new tx {}".format(new_tx)) return True except BlockChainError as e: error = 'Failed accept new tx "{}"'.format(e) logging.error(error) add_failed_mark(error) return False except BaseException: error = "Failed accept new tx" logging.error(error, exc_info=True) add_failed_mark(error) return False
def update_unspents_txs(): global unspents_txs c = 50 while previous_block is None and 0 < c: sleep(0.2) c -= 1 previous_height = previous_block.height proof_txs = list() all_num = 0 for address, height, txhash, txindex, coin_id, amount in get_unspents_iter( ): if height is None: continue if coin_id != 0: continue if not (previous_height + 1 > height + C.MATURE_HEIGHT): continue if not is_address(address, prefix=V.BLOCK_PREFIX): continue if amount < 100000000: continue if staking_limit < all_num: logging.debug("Unspents limit reached, skip by {} limits.".format( staking_limit)) break all_num += 1 proof_tx = TX( tx={ 'type': C.TX_POS_REWARD, 'inputs': [(txhash, txindex)], 'outputs': [(address, 0, 0)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_NONE, 'message': b'' }) proof_tx.height = previous_height + 1 proof_tx.pos_amount = amount proof_txs.append(proof_tx) unspents_txs = proof_txs return all_num, len(proof_txs)
def create_mining_block(consensus): global mining_address # setup mining address for PoW with mining_address_lock: if mining_address is None: if V.MINING_ADDRESS is None: with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() mining_address = generate_new_address_by_userid( C.ANT_UNKNOWN, cur) db.commit() else: mining_address = V.MINING_ADDRESS if unconfirmed_txs is None: raise FailedGenerateWarning('unconfirmed_txs is None') if previous_block is None: raise FailedGenerateWarning('previous_block is None') # create proof_tx reward = GompertzCurve.calc_block_reward(previous_block.height + 1) fees = sum(tx.gas_amount * tx.gas_price for tx in unconfirmed_txs) proof_tx = TX.from_dict( tx={ 'type': C.TX_POW_REWARD, 'inputs': list(), 'outputs': [(mining_address, 0, reward + fees)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_NONE, 'message': b'' }) proof_tx.update_time() # create mining block bits, target = get_bits_by_hash(previous_hash=previous_block.hash, consensus=consensus) mining_block = Block.from_dict( block={ 'merkleroot': b'\xff' * 32, 'time': 0, 'previous_hash': previous_block.hash, 'bits': bits, 'nonce': b'\xff\xff\xff\xff' }) proof_tx.height = previous_block.height + 1 mining_block.height = proof_tx.height mining_block.flag = consensus mining_block.bits2target() mining_block.txs.append(proof_tx) if unconfirmed_txs is None: raise FailedGenerateWarning('unconfirmed_txs is None') mining_block.txs.extend(unconfirmed_txs) mining_block.update_merkleroot() mining_block.update_time(proof_tx.time) return mining_block
def check_unconfirmed_order(best_block, ordered_unconfirmed_txs): if len(ordered_unconfirmed_txs) == 0: return None s = time() dummy_proof_tx = TX() dummy_proof_tx.type = C.TX_POW_REWARD, dummy_block = Block() dummy_block.height = best_block.height + 1 dummy_block.previous_hash = best_block.hash dummy_block.txs.append(dummy_proof_tx) # dummy for proof tx dummy_block.txs.extend(ordered_unconfirmed_txs) tx = None try: for tx in ordered_unconfirmed_txs: if tx.type == C.TX_GENESIS: pass elif tx.type == C.TX_POS_REWARD: pass elif tx.type == C.TX_POW_REWARD: pass elif tx.type == C.TX_TRANSFER: pass elif tx.type == C.TX_MINT_COIN: check_tx_mint_coin(tx=tx, include_block=dummy_block) elif tx.type == C.TX_VALIDATOR_EDIT: check_tx_validator_edit(tx=tx, include_block=dummy_block) elif tx.type == C.TX_CONCLUDE_CONTRACT: check_tx_contract_conclude(tx=tx, include_block=dummy_block) else: raise BlockChainError('Unknown tx type "{}"'.format(tx.type)) else: log.debug('Finish unconfirmed order check {}mSec'.format( int((time() - s) * 1000))) return None except Exception as e: log.warning(e, exc_info=True) # return errored tx return tx
def create_validator_edit_tx(v_address, cur, new_address=None, flag=F_NOP, sig_diff=0, gas_price=None, retention=10800): assert not (flag == F_NOP and sig_diff == 0), 'No edit info' if new_address is None and flag != F_NOP: raise BlockChainError('No cosigner edit, but flag is not NOP') # validator object v = get_validator_object(v_address=v_address) if v.version == -1: if new_address is None or flag != F_ADD or sig_diff != 1: raise BlockChainError('Not correct info') else: next_require = v.require + sig_diff next_validator_num = len(v.validators) if flag == F_ADD: next_validator_num += 1 elif flag == F_REMOVE: next_validator_num -= 1 if not (0 < next_require <= next_validator_num): raise BlockChainError('ReqError, 0 < {} <= {}'.format( next_require, next_validator_num)) # tx create message = msgpack.packb((v_address, new_address, flag, sig_diff), use_bin_type=True) tx = TX.from_dict( tx={ 'type': C.TX_VALIDATOR_EDIT, 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 0, 'message_type': C.MSG_MSGPACK, 'message': message }) tx.gas_amount = tx.size tx.update_time(retention) # fill unspents additional_gas = C.VALIDATOR_EDIT_GAS + v.require * C.SIGNATURE_GAS input_address = fill_inputs_outputs(tx=tx, cur=cur, additional_gas=additional_gas) assert len(input_address & set(v.validators)) == 0, 'Not implemented?' # replace dummy address replace_redeem_dummy_address(tx=tx, cur=cur) setup_signature(tx, input_address) if v.version > -1 and setup_contract_signature(tx, v.validators) == 0: raise BlockChainError('Cannot sign, you are not validator') return tx
def create_mining_block(consensus): global mining_address # create proof_tx mining_address = mining_address or V.MINING_ADDRESS or new_key() reward = GompertzCurve.calc_block_reward(previous_block.height + 1) fees = sum(tx.gas_amount * tx.gas_price for tx in unconfirmed_txs) proof_tx = TX( tx={ 'type': C.TX_POW_REWARD, 'inputs': list(), 'outputs': [(mining_address, 0, reward + fees)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_PLAIN if V.MINING_MESSAGE else C.MSG_NONE, 'message': V.MINING_MESSAGE if V.MINING_MESSAGE else b'' }) proof_tx.update_time() # create mining block bits, target = get_bits_by_hash(previous_hash=previous_block.hash, consensus=consensus) mining_block = Block( block={ 'merkleroot': b'\xff' * 32, 'time': 0, 'previous_hash': previous_block.hash, 'bits': bits, 'nonce': b'\xff\xff\xff\xff' }) proof_tx.height = previous_block.height + 1 mining_block.height = proof_tx.height mining_block.flag = consensus mining_block.bits2target() mining_block.txs.append(proof_tx) mining_block.txs.extend(unconfirmed_txs) mining_block.update_merkleroot() mining_block.update_time(proof_tx.time) return mining_block
def update_unspents_txs(time_limit=0.2): global unspents_txs c = 100 while previous_block is None: if c < 0: raise Exception('Timeout on update unspents') sleep(0.1) c -= 1 s = time() previous_height = previous_block.height proof_txs = list() all_num = 0 with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() for address, height, txhash, txindex, coin_id, amount in get_my_unspents_iter( cur): if time() - s > time_limit: break if height is None: continue if coin_id != 0: continue if not (previous_height + 1 > height + C.MATURE_HEIGHT): continue if not is_address( ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER): continue if amount < 100000000: continue if staking_limit < all_num: log.debug("Unspents limit reached, skip by {} limits".format( staking_limit)) break all_num += 1 proof_tx = TX.from_dict( tx={ 'type': C.TX_POS_REWARD, 'inputs': [(txhash, txindex)], 'outputs': [(address, 0, 0)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_NONE, 'message': b'' }) proof_tx.height = previous_height + 1 proof_tx.pos_amount = amount proof_txs.append(proof_tx) unspents_txs = proof_txs return all_num, len(proof_txs)
def failed_finish_tx(start_tx): message = bjson.dumps((False, start_tx.hash, None), compress=False) return TX( tx={ 'version': __chain_version__, 'type': C.TX_FINISH_CONTRACT, 'time': start_tx.time, 'deadline': start_tx.deadline, 'inputs': list(), 'outputs': list(), 'gas_price': start_tx.gas_price, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': message })
async def create_raw_tx(request): # [version=1] [type=TRANSFER] [time=now] [deadline=now+10800] # [inputs:list()] [outputs:list()] # [gas_price=MINIMUM_PRICE] [gas_amount=MINIMUM_AMOUNT] # [message_type=None] [message=None] post = await web_base.content_type_json_check(request) try: publish_time = post.get('time', int(time.time() - V.BLOCK_GENESIS_TIME)) deadline_time = post.get('deadline', publish_time + 10800) message_type = post.get('message_type', C.MSG_NONE) if message_type == C.MSG_NONE: message = b'' elif message_type == C.MSG_BYTE: message = unhexlify(post['message'].encode()) elif message_type == C.MSG_PLAIN: message = post['message'].encode() else: message_type = C.MSG_NONE message = b'' inputs = list() input_address = set() for txhash, txindex in post.get('inputs', list()): txhash = unhexlify(txhash.encode()) inputs.append((txhash, txindex)) input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] input_address.add(address) tx = TX( tx={ 'version': post.get('version', __chain_version__), 'type': post.get('type', C.TX_TRANSFER), 'time': publish_time, 'deadline': deadline_time, 'inputs': inputs, 'outputs': post.get('outputs', list()), 'gas_price': post.get('gas_price', V.COIN_MINIMUM_PRICE), 'gas_amount': 0, 'message_type': message_type, 'message': message }) tx_size = tx.getsize() + len(input_address) * 96 tx.gas_amount = post.get('gas_amount', tx_size) tx.serialize() return web_base.json_res({ 'tx': tx.getinfo(), 'hex': hexlify(tx.b).decode() }) except BaseException: return web_base.error_res()
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
def load_boot_file(url=None): normal_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'boot.json') extra_path = os.path.join(V.DB_HOME_DIR, 'boot.json') if url: data = requests.get(url=url).json() elif os.path.exists(normal_path): with open(normal_path, mode='r') as fp: data = json.load(fp) elif os.path.exists(extra_path): with open(extra_path, mode='r') as fp: data = json.load(fp) else: raise FileNotFoundError('Cannot find boot.json "{}" or "{}" ?'.format( normal_path, extra_path)) # load from exist boot.json genesis_block = Block.from_binary(binary=a2b_hex(data['genesis_binary'])) assert genesis_block.hash == a2b_hex(data['genesis_hash']) genesis_block.flag = C.BLOCK_GENESIS genesis_block.height = 0 for tx_dct in data['txs']: tx = TX.from_binary(binary=a2b_hex(tx_dct['binary'])) assert tx.hash == a2b_hex(tx_dct['hash']) tx.height = 0 genesis_block.txs.append(tx) connections = data['connections'] network_ver = data['network_ver'] if isinstance(data['params'], dict): # new type boot.json params = data['params'] params['consensus'] = { int(k): v for k, v in params['consensus'].items() } elif isinstance(data['params'], str): # old type boot.json params = original_mpk.unpackb(a2b_hex(data['params']), raw=True, encoding='utf8') else: raise Exception('Unknown type params') return genesis_block, params, network_ver, connections
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 broadcast_tx(request): start = time() post = await utils.content_type_json_check(request) try: binary = a2b_hex(post['hex']) new_tx = TX.from_binary(binary=binary) new_tx.signature = [(a2b_hex(pk), a2b_hex(sig)) for pk, sig in post['signature']] if 'R' in post: new_tx.R = a2b_hex(post['R']) if not send_newtx(new_tx=new_tx): raise BlockChainError('Failed to send new tx') return utils.json_res({ 'hash': new_tx.hash.hex(), 'gas_amount': new_tx.gas_amount, 'gas_price': new_tx.gas_price, 'fee': new_tx.gas_amount * new_tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return utils.error_res()
def object_hook(dct): if isinstance(dct, dict) and '_bc4py_class_' in dct: if dct['_bc4py_class_'] == 'Block': block = Block.from_binary(binary=dct['binary']) block.height = dct['height'] block.flag = dct['flag'] block.txs.extend(object_hook(tx) for tx in dct['txs']) for tx in block.txs: tx.height = block.height return block elif dct['_bc4py_class_'] == 'TX': tx = TX.from_binary(binary=dct['binary']) tx.height = dct['height'] tx.signature.extend(tuple(sig) for sig in dct['signature']) tx.R = dct['R'] return tx else: raise Exception('Not found class name "{}"'.format( dct['_bc4py_class_'])) else: return dct
async def create_raw_tx(request): # [version=1] [type=TRANSFER] [time=now] [deadline=now+10800] # [inputs:list()] [outputs:list()] # [gas_price=MINIMUM_PRICE] [gas_amount=MINIMUM_AMOUNT] # [message_type=None] [message=None] post = await utils.content_type_json_check(request) try: publish_time = post.get('time', int(time() - V.BLOCK_GENESIS_TIME)) deadline_time = post.get('deadline', publish_time + 10800) message_type = post.get('message_type', C.MSG_NONE) message = type2message(message_type, post.get('message')) inputs = list() input_address = set() for txhash, txindex in post.get('inputs', list()): txhash = a2b_hex(txhash) inputs.append((txhash, txindex)) input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] input_address.add(address) tx = TX.from_dict( tx={ 'version': post.get('version', __chain_version__), 'type': post.get('type', C.TX_TRANSFER), 'time': publish_time, 'deadline': deadline_time, 'inputs': inputs, 'outputs': post.get('outputs', list()), 'gas_price': post.get('gas_price', V.COIN_MINIMUM_PRICE), 'gas_amount': 0, 'message_type': message_type, 'message': message }) require_gas = tx.size + len(input_address) * C.SIGNATURE_GAS tx.gas_amount = post.get('gas_amount', require_gas) tx.serialize() return utils.json_res({'tx': tx.getinfo(), 'hex': tx.b.hex()}) except Exception: return utils.error_res()