def contract_fill(c: Contract, best_block=None, best_chain=None, stop_txhash=None): # database c_iter = builder.db.read_contract_iter(c_address=c.c_address, start_idx=c.db_index) for index, start_hash, finish_hash, (c_method, c_args, c_storage) in c_iter: if start_hash == stop_txhash or finish_hash == stop_txhash: return c.update(db_index=index, start_hash=start_hash, finish_hash=finish_hash, c_method=c_method, c_args=c_args, c_storage=c_storage) # memory if best_chain: _best_chain = None elif best_block and best_block == builder.best_block: _best_chain = builder.best_chain else: dummy, _best_chain = builder.get_best_chain(best_block=best_block) for block in reversed(best_chain or _best_chain): for tx in block.txs: if tx.hash == stop_txhash: return if tx.type != C.TX_CONCLUDE_CONTRACT: continue c_address, start_hash, c_storage = decode(tx.message) if c_address != c.c_address: continue if start_hash == stop_txhash: return start_tx = tx_builder.get_tx(txhash=start_hash) dummy, c_method, redeem_address, c_args = decode(start_tx.message) index = start_tx2index(start_tx=start_tx) c.update(db_index=index, start_hash=start_hash, finish_hash=tx.hash, c_method=c_method, c_args=c_args, c_storage=c_storage) # unconfirmed (check validator condition satisfied) if best_block is None: unconfirmed = list() for conclude_tx in tuple(tx_builder.unconfirmed.values()): if conclude_tx.hash == stop_txhash: break if conclude_tx.type != C.TX_CONCLUDE_CONTRACT: continue c_address, start_hash, c_storage = decode(conclude_tx.message) if c_address != c.c_address: continue if start_hash == stop_txhash: break start_tx = tx_builder.get_tx(txhash=start_hash) if start_tx.height is None: continue sort_key = start_tx2index(start_tx=start_tx) unconfirmed.append((c_address, start_tx, conclude_tx, c_storage, sort_key)) v = get_validator_object(c_address=c.c_address, best_block=best_block, best_chain=best_chain, stop_txhash=stop_txhash) for c_address, start_tx, conclude_tx, c_storage, sort_key in sorted(unconfirmed, key=lambda x: x[4]): if len(conclude_tx.signature) < v.require: continue # ignore unsatisfied ConcludeTXs dummy, c_method, redeem_address, c_args = decode(start_tx.message) c.update(db_index=sort_key, start_hash=start_tx.hash, finish_hash=conclude_tx.hash, c_method=c_method, c_args=c_args, c_storage=c_storage)
def contract_fill(c: Contract, best_block=None, best_chain=None, stop_txhash=None): assert c.index == -1, 'Already updated' # database c_iter = builder.db.read_contract_iter(c_address=c.c_address) for index, start_hash, finish_hash, (c_method, c_args, c_storage) in c_iter: if finish_hash == stop_txhash: return c.update(start_hash=start_hash, finish_hash=finish_hash, c_method=c_method, c_args=c_args, c_storage=c_storage) # memory if best_chain: _best_chain = None elif best_block and best_block == builder.best_block: _best_chain = builder.best_chain else: dummy, _best_chain = builder.get_best_chain(best_block=best_block) for block in reversed(best_chain or _best_chain): for tx in block.txs: if tx.hash == stop_txhash: return if tx.type != C.TX_CONCLUDE_CONTRACT: continue c_address, start_hash, c_storage = decode(tx.message) if c_address != c.c_address: continue start_tx = tx_builder.get_tx(txhash=start_hash) dummy, c_method, c_args = decode(start_tx.message) c.update(start_hash=start_hash, finish_hash=tx.hash, c_method=c_method, c_args=c_args, c_storage=c_storage) # unconfirmed if best_block is None: for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.time): if tx.hash == stop_txhash: return if tx.type != C.TX_CONCLUDE_CONTRACT: continue c_address, start_hash, c_storage = decode(tx.message) if c_address != c.c_address: continue start_tx = tx_builder.get_tx(txhash=start_hash) dummy, c_method, c_args = decode(start_tx.message) c.update(start_hash=start_hash, finish_hash=tx.hash, c_method=c_method, c_args=c_args, c_storage=c_storage)
def amount_check(tx, payfee_coin_id): # Inputs input_coins = Balance() for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) if input_tx is None: raise BlockChainError('Not found input tx {}'.format(txhash.hex())) address, coin_id, amount = input_tx.outputs[txindex] input_coins[coin_id] += amount # Outputs output_coins = Balance() for address, coin_id, amount in tx.outputs: if amount <= 0: raise BlockChainError('Input amount is more than 0') output_coins[coin_id] += amount # Fee fee_coins = Balance(coin_id=payfee_coin_id, amount=tx.gas_price * tx.gas_amount) # Check all plus amount remain_amount = input_coins - output_coins - fee_coins if not remain_amount.is_empty(): raise BlockChainError( '77 Don\'t match input/output. {}={}-{}-{}'.format( remain_amount, input_coins, output_coins, fee_coins))
async def conclude_contract(request): start = time() post = await web_base.content_type_json_check(request) try: c_address = post['c_address'] start_hash = unhexlify(post['start_hash'].encode()) start_tx = tx_builder.get_tx(txhash=start_hash) if start_tx is None: return web_base.error_res('Not found start_tx {}'.format( post['start_hash'])) send_pairs = post.get('send_pairs', None) c_storage = post.get('storage', None) tx = create_conclude_tx(c_address=c_address, start_tx=start_tx, send_pairs=send_pairs, c_storage=c_storage) if not send_newtx(new_tx=tx): raise Exception('Failed to send new tx.') return web_base.json_res({ 'hash': hexlify(tx.hash).decode(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return web_base.error_res()
def update_work_hash(block): if block.flag == C.BLOCK_GENESIS: block.work_hash = b'\xff' * 32 elif block.flag == C.BLOCK_COIN_POS: proof_tx = block.txs[0] if proof_tx.pos_amount is None: from bc4py.database.builder import tx_builder txhash, txindex = proof_tx.inputs[0] output_tx = tx_builder.get_tx(txhash) if output_tx is None: raise BlockChainError('Not found output {} of {}'.format( proof_tx, block)) address, coin_id, amount = output_tx.outputs[txindex] proof_tx.pos_amount = amount block.work_hash = get_stake_coin_hash( tx=proof_tx, previous_hash=block.previous_hash) elif block.flag == C.BLOCK_CAP_POS: proof_tx = block.txs[0] address, coin_id, amount = proof_tx.outputs[0] scope_hash = poc_hash(address=address, nonce=block.nonce) index = scope_index(block.previous_hash) block.work_hash = poc_work(time=block.time, scope_hash=scope_hash[index * 32:index * 32 + 32], previous_hash=block.previous_hash) elif block.flag == C.BLOCK_FLK_POS: raise BlockChainError("unimplemented") else: # POW_??? hash_fnc = get_workhash_fnc(block.flag) block.work_hash = hash_fnc(block.b)
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 contract_signature_check(extra_tx: TX, v: Validator, include_block: Block): signed_cks = get_signed_cks(extra_tx) accept_cks = signed_cks & set(v.validators) if include_block: # check satisfy require? if len(accept_cks) < v.require: raise BlockChainError('Not satisfied require signature. [signed={}, accepted={}, require={}]' .format(signed_cks, accept_cks, v.require)) else: # check can marge? original_tx = tx_builder.get_tx(txhash=extra_tx.hash) if original_tx is None: # not accept before if 0 < v.require and len(accept_cks) == 0: raise BlockChainError('No acceptable signature. signed={}'.format(signed_cks)) else: # need to marge signature if original_tx.height is not None: raise BlockChainError('Already included tx. height={}'.format(original_tx.height)) if v.require == 0: raise BlockChainError('Don\t need to marge signature.') original_cks = get_signed_cks(original_tx) accept_new_cks = (signed_cks - original_cks) & set(v.validators) if len(accept_new_cks) == 0: raise BlockChainError('No new acceptable cks. ({} - {}) & {}' .format(signed_cks, original_cks, set(v.validators)))
async def conclude_contract(request): start = time() post = await utils.content_type_json_check(request) try: start_hash = a2b_hex(post['start_hash']) start_tx = tx_builder.get_tx(txhash=start_hash) if start_tx is None: return utils.error_res('Not found start_tx {}'.format( post['start_hash'])) c_address, c_method, redeem_address, c_args = start_tx.encoded_message( ) send_pairs = post.get('send_pairs', None) c_storage = post.get('storage', None) tx = create_conclude_tx(c_address=c_address, start_tx=start_tx, redeem_address=redeem_address, send_pairs=send_pairs, c_storage=c_storage) if not send_newtx(new_tx=tx): raise Exception('Failed to send new tx') return utils.json_res({ 'hash': tx.hash.hex(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return utils.error_res()
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 amount_check(tx, payfee_coin_id): # Inputs input_coins = CoinObject() for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) if input_tx is None: raise BlockChainError('Not found input tx {}'.format( hexlify(txhash).decode())) address, coin_id, amount = input_tx.outputs[txindex] input_coins[coin_id] += amount # Outputs output_coins = CoinObject() for address, coin_id, amount in tx.outputs: if amount <= 0: raise BlockChainError('Input amount is more than 0') output_coins[coin_id] += amount # Fee fee_coins = CoinObject(coin_id=payfee_coin_id, amount=tx.gas_price * tx.gas_amount) # Check all plus amount remain_amount = input_coins - output_coins - fee_coins if not remain_amount.is_all_plus_amount(): raise BlockChainError( 'There are minus amount coins. {}={}-{}-{}'.format( remain_amount, input_coins, output_coins, fee_coins))
async def validate_unconfirmed(request): start = time() post = await utils.content_type_json_check(request) try: txhash = a2b_hex(post['hash']) tx = tx_builder.get_tx(txhash=txhash) if tx is None or tx.height is not None: return utils.error_res('You cannot validate tx. {}'.format(tx)) with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() new_tx = create_signed_tx_as_validator(tx=tx) assert tx is not new_tx, 'tx={}, new_tx={}'.format( id(tx), id(new_tx)) if not send_newtx(new_tx=new_tx, outer_cur=cur): raise Exception('Failed to send new tx') db.commit() 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()
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()
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 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 get_contract_storage(c_address, best_block=None, best_chain=None): # DataBaseより cs = ContractStorage() for dummy, index, start_hash, finish_hash in builder.db.read_contract_iter(c_address): if index == 0: start_tx = tx_builder.get_tx(start_hash) dummy, c_bin, c_cs = bjson.loads(start_tx.message) cs.key_value = c_cs or dict() else: finish_tx = tx_builder.get_tx(finish_hash) c_status, dummy, c_diff = bjson.loads(finish_tx.message) cs.marge(c_diff) # Memoryより best_chain = best_chain or _get_best_chain_all(best_block) for block in reversed(best_chain): for tx in block.txs: if tx.type == C.TX_CREATE_CONTRACT: check_address, c_bin, c_cs = bjson.loads(tx.message) if c_address == check_address: cs.key_value = c_cs or dict() elif tx.type == C.TX_START_CONTRACT: pass elif tx.type == C.TX_FINISH_CONTRACT: c_status, start_hash, c_diff = bjson.loads(tx.message) start_tx = tx_builder.get_tx(start_hash) check_address, c_bin, c_cs = bjson.loads(start_tx.message) if c_address == check_address: cs.marge(c_diff) # Unconfirmedより if best_block is None: for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.time): if tx.type == C.TX_CREATE_CONTRACT: check_address, c_bin, c_cs = bjson.loads(tx.message) cs.key_value = c_cs or dict() elif tx.type == C.TX_START_CONTRACT: pass elif tx.type == C.TX_FINISH_CONTRACT: c_status, start_hash, c_diff = bjson.loads(tx.message) start_tx = tx_builder.get_tx(start_hash) check_address, c_bin, c_cs = bjson.loads(start_tx.message) if c_address == check_address: cs.marge(c_diff) return cs
def get_unspents_iter(target_address, best_block=None, best_chain=None): failed = 0 while failed < 20: if best_chain is None: best_chain = _get_best_chain_all(best_block) if best_chain: break failed += 1 sleep(0.05) else: raise BlockChainError('Cannot get best_chain by {}'.format(best_block)) allow_mined_height = best_chain[0].height - C.MATURE_HEIGHT # DataBaseより for address in target_address: for dummy, txhash, txindex, coin_id, amount, f_used in chain_builder.db.read_address_idx_iter( address): if f_used is False: if txindex in get_usedindex(txhash=txhash, best_block=best_block, best_chain=best_chain): continue # Used tx = tx_builder.get_tx(txhash) if tx.type in (C.TX_POW_REWARD, C.TX_POS_REWARD): if tx.height is not None and tx.height < allow_mined_height: yield address, tx.height, txhash, txindex, coin_id, amount else: yield address, tx.height, txhash, txindex, coin_id, amount # Memoryより for block in reversed(best_chain): for tx in block.txs: used_index = get_usedindex(txhash=tx.hash, best_block=best_block, best_chain=best_chain) for index, (address, coin_id, amount) in enumerate(tx.outputs): if index in used_index: continue # Used elif address in target_address: if tx.type in (C.TX_POW_REWARD, C.TX_POS_REWARD): if tx.height is not None and tx.height < allow_mined_height: yield address, tx.height, tx.hash, index, coin_id, amount else: yield address, tx.height, tx.hash, index, coin_id, amount # Unconfirmedより if best_block is None: for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.create_time): used_index = get_usedindex(txhash=tx.hash, best_block=best_block, best_chain=best_chain) for index, (address, coin_id, amount) in enumerate(tx.outputs): if index in used_index: continue # Used elif address in target_address: yield address, None, tx.hash, index, coin_id, amount
def calc_tx_movement(tx, c_address, redeem_address, emulate_gas): """ Calc tx inner movement """ account = Accounting() for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash=txhash) address, coin_id, amount = input_tx.outputs[txindex] account[address][coin_id] -= amount account[redeem_address][0] += (tx.gas_amount+emulate_gas) * tx.gas_price account[c_address][0] -= emulate_gas * tx.gas_price for address, coin_id, amount in tx.outputs: account[address][coin_id] += amount return account
async def get_tx_by_hash(request): try: txhash = request.query.get('hash') txhash = unhexlify(txhash.encode()) tx = tx_builder.get_tx(txhash) if tx is None: return web.Response(text="Not found tx.", status=400) data = tx.getinfo() data['size'] = tx.getsize() data['hex'] = hexlify(tx.b).decode() data['signature'] = [(pubkey, hexlify(sign).decode()) for pubkey, sign in tx.signature] return web_base.json_res(data) except Exception as e: return web_base.error_res()
def validator_tx2index(txhash=None, tx=None): if txhash: tx = tx_builder.get_tx(txhash=txhash) if tx is None: raise BlockChainError('Not found ValidatorTX {}'.format(tx)) if tx.height is None: raise BlockChainError('Not confirmed ValidatorTX {}'.format(tx)) block = chain_builder.get_block(height=tx.height) if block is None: raise BlockChainError( 'Not found block of start_tx included? {}'.format(tx)) if tx not in block.txs: raise BlockChainError('Not found start_tx in block? {}'.format(block)) return tx.height * 0xffffffff + block.txs.index(tx)
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 signature_check(tx): need_cks = set() for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) if input_tx is None: raise BlockChainError('Not found input tx {}'.format(hexlify(txhash).decode())) address, coin_id, amount = input_tx.outputs[txindex] if is_address(address, V.BLOCK_PREFIX): need_cks.add(address) # 通常のアドレスのみ else: raise BlockChainError('Not common address {} {}.'.format(address, tx)) signed_cks = get_signed_cks(tx) if need_cks != signed_cks: raise BlockChainError('Signature verification failed. [{}={}]'.format(need_cks, signed_cks))
def input_output_digest(tx): require_cks = set() coins = CoinObject() for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash=txhash) if input_tx is None: raise BlockChainError('input tx is None. {}:{}'.format( hexlify(txhash).decode(), txindex)) address, coin_id, amount = input_tx.outputs[txindex] require_cks.add(address) coins[coin_id] += amount coins[0] -= tx.gas_amount * tx.gas_price for address, coin_id, amount in tx.outputs: coins[coin_id] -= amount return require_cks, coins
def check_tx_pos_reward(tx, include_block): # POS報酬TXの検査 if not (len(tx.inputs) == len(tx.outputs) == 1): raise BlockChainError('Inputs and outputs is only 1 len.') elif include_block.txs.index(tx) != 0: raise BlockChainError('Proof tx is index 0.') elif not (tx.gas_price == 0 and tx.gas_amount == 0): raise BlockChainError('Pos gas info is wrong. [{}, {}]'.format( tx.gas_price, tx.gas_amount)) elif not (tx.message_type == C.MSG_NONE and tx.message == b''): raise BlockChainError('Pos msg is None type. [{},{}]'.format( tx.message_type, tx.message)) txhash, txindex = tx.inputs[0] base_tx = tx_builder.get_tx(txhash) if base_tx is None: print(list(tx_builder.chained_tx.values())) raise BlockChainError('Not found PosBaseTX:{} of {}.'.format( hexlify(txhash).decode(), tx)) input_address, input_coin_id, input_amount = base_tx.outputs[txindex] tx.pos_amount = input_amount output_address, output_coin_id, output_amount = tx.outputs[0] reward = GompertzCurve.calc_block_reward(include_block.height) include_block.bits2target() if input_address != output_address: raise BlockChainError( 'Input address differ from output address. [{}!={}]'.format( input_address, output_address)) elif not (input_coin_id == output_coin_id == 0): raise BlockChainError('Input and output coinID is zero.') elif input_amount + reward != output_amount: raise BlockChainError('Inout amount wrong [{}+{}!={}]'.format( input_amount, reward, output_amount)) elif tx.version != __chain_version__ or tx.message_type != C.MSG_NONE: raise BlockChainError('Not correct tx version or msg_type.') elif base_tx.height is None: raise BlockChainError('Source TX is unconfirmed. {}'.format(base_tx)) elif not (include_block.height > base_tx.height + C.MATURE_HEIGHT): raise BlockChainError( 'Source TX height is too young. [{}>{}+{}]'.format( include_block.height, base_tx.height, C.MATURE_HEIGHT)) elif not (include_block.time == tx.time == tx.deadline - 10800): raise BlockChainError('TX time is wrong 1. [{}={}={}-10800]'.format( include_block.time, tx.time, tx.deadline)) elif not tx.pos_check(include_block.previous_hash, include_block.target_hash): raise BlockChainError('Proof of stake check is failed.')
def update_work_hash(block): if block.flag == C.BLOCK_GENESIS: block.work_hash = b'\xff' * 32 elif block.flag == C.BLOCK_POS: proof_tx = block.txs[0] if proof_tx.pos_amount is None: from bc4py.database.builder import tx_builder txhash, txindex = proof_tx.inputs[0] output_tx = tx_builder.get_tx(txhash) address, coin_id, amount = output_tx.outputs[txindex] proof_tx.pos_amount = amount block.work_hash = proof_tx.get_pos_hash(block.previous_hash) else: # POW_??? hash_fnc = get_workhash_fnc(block.flag) block.work_hash = hash_fnc(block.b)
async def get_tx_by_hash(request): try: f_pickled = request.query.get('pickle', False) txhash = request.query.get('hash') txhash = a2b_hex(txhash) tx = tx_builder.get_tx(txhash) if tx is None: return web.Response(text="Not found tx", status=400) if f_pickled: tx = pickle.dumps(tx) return utils.json_res(b64encode(tx).decode()) data = tx.getinfo() data['hex'] = tx.b.hex() return utils.json_res(data) except Exception as e: return utils.error_res()
async def get_contract_history(request): try: c_address = request.query['c_address'] data = list() # database for index, start_hash, finish_hash, (c_method, c_args, c_storage) in\ builder.db.read_contract_iter(c_address=c_address): data.append({ 'index': index, 'height': index // 0xffffffff, 'start_hash': hexlify(start_hash).decode(), 'finish_hash': hexlify(finish_hash).decode(), 'c_method': c_method, 'c_args': [decode(a) for a in c_args], 'c_storage': {decode(k): decode(v) for k, v in c_storage.items()} if c_storage else None }) # memory for block in reversed(builder.best_chain): for tx in block.txs: if tx.type != C.TX_CONCLUDE_CONTRACT: continue _c_address, start_hash, c_storage = bjson.loads(tx.message) if _c_address != c_address: continue start_tx = tx_builder.get_tx(txhash=start_hash) dummy, c_method, redeem_address, c_args = bjson.loads( start_tx.message) index = start_tx2index(start_tx=start_tx) data.append({ 'index': index, 'height': tx.height, 'start_hash': hexlify(start_hash).decode(), 'finish_hash': hexlify(tx.hash).decode(), 'c_method': c_method, 'c_args': [decode(a) for a in c_args], 'c_storage': {decode(k): decode(v) for k, v in c_storage.items()} if c_storage else None, # 'redeem_address': redeem_address, }) return web_base.json_res(data) except Exception as e: logging.error(e) return web_base.error_res()
def input_output_digest(tx): require_cks = set() coins = Balance() # inputs for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash=txhash) if input_tx is None: raise BlockChainError('input tx is None. {}:{}'.format(txhash.hex(), txindex)) address, coin_id, amount = input_tx.outputs[txindex] require_cks.add(address) coins[coin_id] += amount # outputs for address, coin_id, amount in tx.outputs: coins[coin_id] -= amount # fee coins[0] -= tx.gas_amount * tx.gas_price return require_cks, coins
def signature_check(tx, include_block): require_cks = set() checked_cks = set() signed_cks = set(tx.verified_list) for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) if input_tx is None: raise BlockChainError('Not found input tx {}'.format(txhash.hex())) if len(input_tx.outputs) <= txindex: raise BlockChainError('txindex is over range {}<={}'.format( len(input_tx.outputs), txindex)) address, coin_id, amount = input_tx.outputs[txindex] if address in checked_cks: continue elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER): require_cks.add(address) elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_VALIDATOR_VER): v_before = get_validator_object(v_address=address, best_block=include_block, stop_txhash=tx.hash) if v_before.version == -1: raise BlockChainError('Not init validator {}'.format(address)) if len(signed_cks & v_before.validators) < v_before.require: raise BlockChainError( 'Don\'t satisfy required signature {}<{}'.format( len(signed_cks & v_before.validators), v_before.require)) require_cks.update(v_before.validators) elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_CONTRACT_VER): raise BlockChainError( 'Not allow ContractAddress include in normal Transfer. {}'. format(address, tx)) else: raise BlockChainError('Not common address {} {}'.format( address, tx)) # success check checked_cks.add(address) if not (0 < len(require_cks) < 256): raise BlockChainError('require signature is over range num={}'.format( len(require_cks))) if require_cks != signed_cks: raise BlockChainError('Signature verification failed. [{}={}]'.format( require_cks, signed_cks))
def inputs_origin_check(tx, include_block): # Blockに取り込まれているなら # TXのInputsも既に取り込まれているはずだ limit_height = builder.best_block.height - C.MATURE_HEIGHT for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) if input_tx is None: # InputのOriginが存在しない raise BlockChainError('Not found input tx. {}:{}'.format( hexlify(txhash).decode(), txindex)) elif input_tx.height is None: # InputのOriginはUnconfirmed if include_block: raise BlockChainError( 'TX {} is include' ', but input origin {} is unconfirmed.'.format( tx, input_tx)) else: # UnconfirmedTXの受け入れなので、txもinput_txもUnconfirmed pass # OK elif input_tx.type in (C.TX_POS_REWARD, C.TX_POW_REWARD) and \ input_tx.height > limit_height: raise BlockChainError('input origin is proof tx, {}>{}'.format( input_tx.height, limit_height)) else: # InputのOriginは既に取り込まれている pass # OK # 使用済みかチェック if txindex in get_usedindex(txhash=txhash, best_block=include_block): sticky_failed_txhash.append(tx.hash) raise BlockChainError( '1 Input of {} is already used! {}:{}'.format( tx, hexlify(txhash).decode(), txindex)) # 同一Block内で使用されていないかチェック if include_block: for input_tx in include_block.txs: if input_tx == tx: break for input_hash, input_index in input_tx.inputs: if input_hash == txhash and input_index == txindex: sticky_failed_txhash.append(tx.hash) raise BlockChainError( '2 Input of {} is already used by {}'.format( tx, input_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