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
def create_contract_transfer_tx(c_address, cur, c_method, c_args=None, send_pairs=None, sender=C.ANT_UNKNOWN, gas_price=None, retention=10800): assert isinstance(c_method, str) if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED): raise BlockChainError('Not allowed inner account.') if c_args is None: c_args = tuple() else: c_args = tuple(c_args) redeem_address = create_new_user_keypair(read_user2name(sender, cur), cur) msg_body = bjson.dumps((c_address, c_method, redeem_address, c_args), compress=False) send_pairs = send_pairs_format_check(c_address=c_address, send_pairs=send_pairs) tx = send_many(sender=sender, send_pairs=send_pairs, cur=cur, fee_coin_id=0, gas_price=gas_price, msg_type=C.MSG_BYTE, msg_body=msg_body, retention=retention) return tx
def create_conclude_tx(c_address, start_tx, send_pairs=None, c_storage=None): assert isinstance(start_tx, TX) assert send_pairs is None or isinstance(send_pairs, list) assert c_storage is None or isinstance(c_storage, dict) message = bjson.dumps((c_address, start_tx.hash, c_storage), compress=False) v = get_validator_object(c_address=c_address) send_pairs = send_pairs or list() tx = TX( tx={ 'type': C.TX_CONCLUDE_CONTRACT, 'time': start_tx.time, 'deadline': start_tx.deadline, 'gas_price': start_tx.gas_price, 'gas_amount': 0, 'outputs': [tuple(s) for s in send_pairs], 'message_type': C.MSG_BYTE, 'message': message }) extra_gas = C.SIGNATURE_GAS * v.require tx.gas_amount = tx.size + extra_gas # fill unspents fill_contract_inputs_outputs(tx=tx, c_address=c_address, additional_gas=extra_gas) # replace dummy address replace_redeem_dummy_address(tx=tx, replace_by=c_address) tx.serialize() if v.index == -1: raise BlockChainError( 'Not init validator address. {}'.format(c_address)) if setup_contract_signature(tx, v.validators) == 0: raise BlockChainError('Cannot sign, you are not validator.') return tx
def test(): import time import json with open("sample.json") as f: t = json.load(f) t.append("namuyan" * 1024) s = time.time() pr_dump = cProfile.Profile() pr_dump.enable() bj = dumps(t) pr_dump.disable() print("encode", time.time() - s, "Sec") stats_dump = pstats.Stats(pr_dump) stats_dump.sort_stats('ncalls') stats_dump.print_stats() pr_load = cProfile.Profile() pr_load.enable() decoded = loads(bj) pr_load.disable() print("decode", time.time() - s, "Sec") stats_load = pstats.Stats(pr_load) stats_load.sort_stats('ncalls') stats_load.print_stats() print(len(bj) // 1000, "kB, match=", t == decoded)
def create_contract_update_tx(c_address, cur, c_bin=None, c_extra_imports=None, c_settings=None, send_pairs=None, sender=C.ANT_UNKNOWN, gas_price=None, retention=10800): assert c_bin or c_extra_imports or c_settings if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED): raise BlockChainError('Not allowed inner account.') c_method = contract.M_UPDATE c_args = (c_bin, c_extra_imports, c_settings) redeem_address = create_new_user_keypair(read_user2name(sender, cur), cur) msg_body = bjson.dumps((c_address, c_method, redeem_address, c_args), compress=False) send_pairs = send_pairs_format_check(c_address=c_address, send_pairs=send_pairs) tx = send_many(sender=sender, send_pairs=send_pairs, cur=cur, fee_coin_id=0, gas_price=gas_price, msg_type=C.MSG_BYTE, msg_body=msg_body, retention=retention) return tx
def write_coins(self, coin_id, txhash, params, setting): assert self.is_batch_thread(), 'Not created batch.' index = -1 for index, *dummy in self.read_coins_iter(coin_id=coin_id): pass index += 1 k = coin_id.to_bytes(4, ITER_ORDER) + index.to_bytes(4, ITER_ORDER) v = txhash + bjson.dumps((params, setting), compress=False) self.batch['_coins'][k] = v logging.debug("Insert new coins id={}".format(coin_id))
def write_contract(self, c_address, start_hash, finish_hash, message): assert self.is_batch_thread(), 'Not created batch.' assert len(message) == 3 index = -1 for index, *dummy in self.read_contract_iter(c_address=c_address): pass index += 1 k = c_address.encode() + index.to_bytes(4, ITER_ORDER) v = start_hash + finish_hash + bjson.dumps(message, compress=False) self.batch['_contract'][k] = v logging.debug("Insert new contract {} {}".format(c_address, index))
def create_cert(master_sk, signer_pk, cert_start, cert_stop): assert int(time.time()) < cert_start < cert_stop, 'wrong time setting of cert.' master_ecc = Encryption() master_ecc.sk = master_sk cert_raw = bjson.dumps((master_ecc.pk, signer_pk, cert_start, cert_stop), compress=False) cert = { 'master': master_ecc.pk, 'start': cert_start, 'stop': cert_stop, 'sign': master_ecc.sign(cert_raw, encode='hex')} return cert
def create_contract_tx(c_bin, cur, sender=C.ANT_UNKNOWN, c_cs=None, gas_price=None, retention=10800): assert isinstance(c_bin, bytes), 'contract is bytes code.' assert isinstance(sender, int), 'Sender is id.' if c_cs: for k, v in c_cs.items(): assert isinstance(k, bytes), 'Key is bytes.' assert isinstance(v, bytes), 'Value is bytes.' # TXを作成 now = int(time.time()) - V.BLOCK_GENESIS_TIME ck = create_new_user_keypair(C.ANT_NAME_CONTRACT, cur) c_address = convert_address(ck, V.BLOCK_CONTRACT_PREFIX) message = bjson.dumps((c_address, c_bin, c_cs), compress=False) tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_CREATE_CONTRACT, 'time': now, 'deadline': now + retention, 'inputs': list(), 'outputs': list(), 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 1, 'message_type': C.MSG_BYTE, 'message': message }) tx.gas_amount = tx.getsize() + C.CONTRACT_CREATE_FEE + 96 # fill unspents fee_coin_id = 0 input_address = fill_inputs_outputs(tx, cur, fee_coin_id, C.CONTRACT_CREATE_FEE) fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount) movements = UserCoins() movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins # account check send_coins = CoinObject() check_enough_amount(sender, send_coins, fee_coins) if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED): raise BlockChainError('Not allowed inner account.') # replace dummy address replace_redeem_dummy_address(tx, cur) # setup signature tx.serialize() setup_signature(tx, input_address) movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins insert_log(movements, cur, tx.type, tx.time, tx.hash) return c_address, tx
def start_contract_tx(c_address, c_method, cur, c_args=None, outputs=None, sender=C.ANT_UNKNOWN, gas_price=None, additional_gas_amount=None, retention=10800): # TXを作成 now = int(time.time()) - V.BLOCK_GENESIS_TIME c_redeem = create_new_user_keypair(C.ANT_NAME_UNKNOWN, cur) message = bjson.dumps((c_address, c_method, c_args or tuple(), c_redeem), compress=False) tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_START_CONTRACT, 'time': now, 'deadline': now + retention, 'inputs': list(), 'outputs': outputs or list(), 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 1, 'message_type': C.MSG_BYTE, 'message': message }) check_output_format(tx.outputs) tx.gas_amount = tx.getsize() + 96 tx.serialize() # fill unspents fee_coin_id = 0 input_address = fill_inputs_outputs( tx, cur, fee_coin_id, additional_gas_amount or V.CONTRACT_MINIMUM_AMOUNT) fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount) movements = UserCoins() movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins # account check send_coins = CoinObject() check_enough_amount(sender, send_coins, fee_coins) if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED): raise BlockChainError('Not allowed inner account.') # replace dummy address replace_redeem_dummy_address(tx, cur) # setup signature tx.serialize() setup_signature(tx, input_address) movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins insert_log(movements, cur, tx.type, tx.time, tx.hash) return tx
def generate_sign(self, sk): assert self.version is not None, 'mint version error.' d = { 'version': self.version, 'coin_id': self.coin_id, 'name': self.name, 'unit': self.unit, 'digit': self.digit, 'amount': self.amount, 'additional_issue': self.additional_issue, 'owner': self.owner } binary = bjson.dumps(d, compress=False) self.sign = sign(msg=binary, sk=sk, pk=self.owner)
def write_contract(self, c_address, start_tx, finish_hash, message): assert self.is_batch_thread(), 'Not created batch.' assert len(message) == 3 include_block = self.read_block(blockhash=self.read_block_hash(height=start_tx.height)) index = start_tx.height * 0xffffffff + include_block.txs.index(start_tx) # check newer index already inserted last_index = None for last_index, *dummy in self.read_contract_iter(c_address=c_address, start_idx=index): pass assert last_index is None, 'Not allow older ConcludeTX insert. my={} last={}'.format(index, last_index) k = c_address.encode() + index.to_bytes(8, ITER_ORDER) v = start_tx.hash + finish_hash + bjson.dumps(message, compress=False) self.batch['_contract'][k] = v logging.debug("Insert new contract {} {}".format(c_address, index))
def create_validator_edit_tx(c_address, 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(c_address=c_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 = bjson.dumps((c_address, new_address, flag, sig_diff), compress=False) tx = TX( tx={ 'type': C.TX_VALIDATOR_EDIT, 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': message }) extra_gas = C.VALIDATOR_EDIT_GAS + C.SIGNATURE_GAS * v.require tx.gas_amount = tx.size + extra_gas tx.update_time(retention) # fill unspents fill_contract_inputs_outputs(tx=tx, c_address=c_address, additional_gas=extra_gas) # replace dummy address replace_redeem_dummy_address(tx=tx, replace_by=c_address) tx.serialize() if len(v.validators) > 0 and setup_contract_signature(tx, v.validators) == 0: raise BlockChainError('Cannot sign, you are not validator.') return tx
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 })
def _send_msg(self, item, allows=None, denys=None): msg_body = bjson.dumps(item) if allows is None: allows = self.p2p.user if denys is None: denys = list() c = 0 for user in allows: if user not in denys: try: self.p2p.send_msg_body(msg_body=msg_body, user=user) except Exception as e: logging.debug("Failed send msg to {}, {}".format(user.name, e)) c += 1 return c # 送った送信先
def check_sign(self): d = { 'version': self.version, 'coin_id': self.coin_id, 'name': self.name, 'unit': self.unit, 'digit': self.digit, 'amount': self.amount, 'additional_issue': self.additional_issue, 'owner': self.owner } binary = bjson.dumps(d, compress=False) try: verify(msg=binary, sign=self.sign, pk=self.owner) except ValueError: raise MintCoinError('signature verification failed.')
def serialize(self): d = { 'version': self.version, 'coin_id': self.coin_id, 'amount': self.amount, 'owner': self.owner, 'sign': self.sign } if self.name is not None: d['name'] = self.name if self.unit is not None: d['unit'] = self.unit if self.digit is not None: d['digit'] = self.digit if self.additional_issue is not None: d['additional_issue'] = self.additional_issue if self.image is not None: d['image'] = self.image if self.message is not None: d['message'] = self.message self.binary = bjson.dumps(d, compress=False)
def create_boot_file(genesis_block, network_ver=None, connections=None): network_ver = network_ver or random.randint(1000000, 0xffffffff) assert isinstance(network_ver, int) and abs( network_ver) <= 0xffffffff, 'network_ver is int <=0xffffffff.' data = { 'block': genesis_block.b, 'txs': [tx.b for tx in genesis_block.txs], 'connections': connections or list(), 'network_ver': network_ver } boot_path = os.path.join(V.DB_HOME_DIR, 'boot.dat') data = b64encode(bjson.dumps(data)) with open(boot_path, mode='bw') as fp: while len(data) > 0: write, data = data[:60], data[60:] fp.write(write + b'\n') logging.info("create new boot.dat!")
def remove_file_by_master(self, signer_sk, cert, file_hash): file_hash = file_hash.lower() file_path = os.path.join(V.DATA_PATH, 'file.' + file_hash + '.dat') uuid = random.randint(10, 99999999) signer_ecc = Encryption() signer_ecc.sk = signer_sk try: os.remove(file_path) sign_raw = bjson.dumps((file_hash, uuid), compress=False) send_data = { 'signer': signer_ecc.pk, 'sign': signer_ecc.sign(msg=sign_raw, encode='hex'), 'cert': cert} dummy, result = self.send_command(ClientCmd.FILE_DELETE, send_data, uuid=uuid) logging.debug("File delete success.") except: logging.debug("Failed delete file.") pass
def send_msg_body(self, msg_body, user=None, status=200, f_udp=False, f_pro_force=False): # StatusCode: https://ja.wikipedia.org/wiki/HTTPステータスコード assert type(msg_body) == bytes, 'msg_body is bytes' assert 200 <= status < 600, 'Not found status code {}'.format(status) # get client if len(self.user) == 0: raise ConnectionError('client connection is zero.') elif len(msg_body) > C.MAX_RECEIVE_SIZE + 5000: error = 'Max message size is {}kb (You try {}Kb)'.format( round(C.MAX_RECEIVE_SIZE / 1000000, 3), round(len(msg_body) / 1000000, 3)) self.send_msg_body(msg_body=bjson.dumps(error), user=user, status=500) raise ConnectionRefusedError(error) elif user is None: user = random.choice(self.user) # send message if f_udp and f_pro_force: self._udp_body(msg_body, user) elif f_udp and user.p2p_udp_accept and len(msg_body) < 1400: self._udp_body(msg_body, user) else: msg_body = zlib.compress(msg_body) msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) msg_len = len(msg_body).to_bytes(4, 'big') send_data = msg_len + msg_body user.send(send_data) self.traffic.put_traffic_up(send_data) # logging.debug("Send {}Kb to '{}'".format(len(msg_len+msg_body) / 1000, user.name)) return user
def issue_mintcoin(name, unit, digit, amount, cur, description=None, image=None, additional_issue=True, change_address=True, gas_price=None, sender=C.ANT_UNKNOWN, retention=10800): mint_id = get_new_coin_id() sender_name = read_user2name(user=sender, cur=cur) mint_address = create_new_user_keypair(name=sender_name, cur=cur) params = { "name": name, "unit": unit, "digit": digit, "address": mint_address, "description": description, "image": image } setting = { "additional_issue": additional_issue, "change_address": change_address } m_before = get_mintcoin_object(coin_id=mint_id) result = check_mintcoin_new_format(m_before=m_before, new_params=params, new_setting=setting) if isinstance(result, str): raise BlockChainError('check_mintcoin_new_format(): {}'.format(result)) msg_body = bjson.dumps((mint_id, params, setting), compress=False) tx = TX( tx={ 'type': C.TX_MINT_COIN, 'inputs': list(), 'outputs': [(MINTCOIN_DUMMY_ADDRESS, 0, amount)], 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 1, 'message_type': C.MSG_BYTE, 'message': msg_body }) tx.update_time(retention) additional_gas = C.MINTCOIN_GAS tx.gas_amount = tx.size + C.SIGNATURE_GAS + additional_gas tx.serialize() # fill unspents fee_coin_id = 0 input_address = fill_inputs_outputs(tx=tx, cur=cur, fee_coin_id=fee_coin_id, additional_gas=additional_gas) # input_address.add(mint_address) fee_coins = Balance(coin_id=fee_coin_id, amount=tx.gas_price * tx.gas_amount) # check amount check_enough_amount(sender=sender, send_coins=Balance(0, amount), fee_coins=fee_coins) # replace dummy address replace_redeem_dummy_address(tx=tx, cur=cur) # replace dummy mint_id replace_mint_dummy_address(tx=tx, mint_address=mint_address, mint_id=mint_id, f_raise=True) # setup signature tx.serialize() setup_signature(tx=tx, input_address=input_address) # movement movements = Accounting() minting_coins = Balance(mint_id, amount) movements[sender] += minting_coins movements[C.ANT_OUTSIDE] -= minting_coins movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins insert_log(movements, cur, tx.type, tx.time, tx.hash) return mint_id, tx
def create_genesis_block(all_supply, block_span, prefix=b'\x98', contract_prefix=b'\x12', digit_number=8, minimum_price=100, consensus=None, premine=None): """ Height0のGenesisBlockを作成する :param all_supply: PoW/POS合わせた全採掘量、プリマインを除く :param block_span: Blockの採掘間隔(Sec) :param prefix: 一般アドレスの頭文字、b'\x98'=N :param contract_prefix: コントラクトの頭文字、b'\x98'=C :param digit_number: コインの分解能 :param minimum_price: 最小gas_price :param consensus: 採掘アルゴ {consensus: ratio(0~100), ..} :param premine: プリマイン [(address, coin_id, amount), ...] """ # default: Yescript9割, Stake1割の分配 consensus = consensus or {C.BLOCK_YES_POW: 90, C.BLOCK_POS: 10} if sum(consensus.values()) != 100: raise BlockChainError('sum of consensus values is 100 [!={}]'.format( sum(consensus.values()))) elif not isinstance(sum(consensus.values()), int): raise BlockChainError('value is int only.') elif not (0 < min(consensus.values()) <= 100): raise BlockChainError('out of range {}'.format(min( consensus.values()))) elif not (0 < max(consensus.values()) <= 100): raise BlockChainError('out of range {}'.format(min( consensus.values()))) all_consensus = { C.BLOCK_POS, C.BLOCK_YES_POW, C.BLOCK_X11_POW, C.BLOCK_HMQ_POW, C.BLOCK_LTC_POW, C.BLOCK_X16_POW } if len(set(consensus.keys()) - all_consensus) > 0: raise BlockChainError('Not found all_consensus number {}'.format( set(consensus.keys()) - all_consensus)) elif len(set(consensus.keys()) & all_consensus) == 0: raise BlockChainError('No usable consensus found {}'.format( set(consensus.keys()) & all_consensus)) # params assert isinstance(minimum_price, int), 'minimum_price is INT' genesis_time = int(time.time()) # premine premine_txs = list() for index, chunk in enumerate(chunked(premine or list(), 256)): tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_TRANSFER, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': chunk, 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_PLAIN, 'message': 'Premine {}'.format(index).encode() }) tx.height = 0 premine_txs.append(tx) # validator V.BLOCK_GENESIS_TIME = int(time.time()) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: ck = create_new_user_keypair(C.ANT_CONTRACT, db.cursor()) db.commit() c_address = convert_address(ck, contract_prefix) c_bin = contract2binary(Contract) c_cs = { ck.encode(): b'\x00\x00\x00\x00', b'\x00' + b'\x00\x00\x00\x00': b'\x01' } # TODO: 初期値どうする? validator_tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_CREATE_CONTRACT, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': list(), 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': bjson.dumps((c_address, c_bin, c_cs), compress=False) }) validator_tx.height = 0 params = { 'prefix': prefix, # CompressedKey prefix 'contract_prefix': contract_prefix, # ContractKey prefix 'validator_address': c_address, 'genesis_time': genesis_time, # GenesisBlockの採掘時間 'all_supply': all_supply, # 全採掘量 'block_span': block_span, # ブロックの採掘間隔 'digit_number': digit_number, # 小数点以下の桁数 'minimum_price': minimum_price, 'contract_minimum_amount': pow(10, digit_number), 'consensus': consensus } # Block承認のアルゴリズム # BLockChainの設定TX setting_tx = TX( tx={ 'version': __chain_version__, 'type': C.TX_GENESIS, 'time': 0, 'deadline': 10800, 'inputs': list(), 'outputs': list(), 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_BYTE, 'message': bjson.dumps(params, compress=False) }) setting_tx.height = 0 # height0のBlock生成 genesis_block = Block( block={ 'merkleroot': b'\x00' * 32, 'time': 0, 'previous_hash': b'\xff' * 32, 'bits': MAX_BITS, 'nonce': b'\xff' * 4 }) # block params genesis_block.height = 0 genesis_block.flag = C.BLOCK_GENESIS # block body genesis_block.txs.append(setting_tx) genesis_block.txs.append(validator_tx) genesis_block.txs.extend(premine_txs) genesis_block.bits2target() genesis_block.target2diff() genesis_block.update_merkleroot() genesis_block.serialize() return genesis_block
def create_conclude_tx(c_address, start_tx, redeem_address, send_pairs=None, c_storage=None, emulate_gas=0): assert isinstance(start_tx, TX) assert send_pairs is None or isinstance(send_pairs, list) assert c_storage is None or isinstance(c_storage, dict) assert isinstance(emulate_gas, int) message = bjson.dumps((c_address, start_tx.hash, c_storage), compress=False) v = get_validator_object(c_address=c_address) send_pairs = send_pairs or list() tx = TX( tx={ 'type': C.TX_CONCLUDE_CONTRACT, 'time': start_tx.time, 'deadline': start_tx.deadline, 'gas_price': start_tx.gas_price, 'gas_amount': 0, 'outputs': [tuple(s) for s in send_pairs], 'message_type': C.MSG_BYTE, 'message': message }) extra_gas = C.SIGNATURE_GAS * v.require tx.gas_amount = tx.size + extra_gas # fill unspents fill_contract_inputs_outputs(tx=tx, c_address=c_address, additional_gas=extra_gas) # replace dummy address replace_redeem_dummy_address(tx=tx, replace_by=c_address) # fix redeem fees if send_pairs: # conclude_txで使用したGasを、ユーザーから引いてコントラクトに戻す処理 conclude_fee = (emulate_gas + tx.gas_amount) * tx.gas_price fee_coin_id = 0 f_finish_add = f_finish_sub = False for index, (address, coin_id, amount) in enumerate(tx.outputs): if coin_id != fee_coin_id: continue elif not f_finish_add and address == c_address: f_finish_add = True tx.outputs[index] = (address, coin_id, amount + conclude_fee) elif not f_finish_sub and address == redeem_address: f_finish_sub = True tx.outputs[index] = (address, coin_id, amount - conclude_fee) else: pass if not (f_finish_add and f_finish_sub): raise BlockChainError( 'Cannot move conclude fee, add={} sub={}'.format( f_finish_add, f_finish_sub)) logging.debug("Move conclude fee {}:{}".format(fee_coin_id, conclude_fee)) tx.serialize() if v.version == -1: raise BlockChainError( 'Not init validator address. {}'.format(c_address)) if setup_contract_signature(tx, v.validators) == 0: raise BlockChainError('Cannot sign, you are not validator.') return tx
def obj2bjson(o): return bjson.dumps(o)
def type_request(self, user, item): temperate = { 'type': T_RESPONSE, 'cmd': item['cmd'], 'data': None, 'time': time.time(), 'uuid': item['uuid']} allow_list = list() deny_list = list() ack_list = list() if item['cmd'] == ClientCmd.PING_PONG: temperate['data'] = { 'ping': item['data'], 'pong': time.time()} allow_list.append(user) elif item['cmd'] == ClientCmd.BROADCAST: if item['uuid'] in self.__broadcast_uuid: return # already get broadcast data elif self.__waiting_result.include(item['uuid']): return # I'm broadcaster, get from ack elif not self.broadcast_check(item['data']): user.warn += 1 self.__broadcast_uuid.append(item['uuid']) return # not allowed broadcast data else: self.__broadcast_uuid.append(item['uuid']) self.broadcast_que.broadcast(item['data']) deny_list.append(user) allow_list = None # send ACK ack_list.append(user) # send Response temperate['type'] = T_REQUEST temperate['data'] = item['data'] elif item['cmd'] == ClientCmd.GET_PEER_INFO: # [[(host,port), header],..] temperate['data'] = self.peers.data allow_list.append(user) elif item['cmd'] == ClientCmd.GET_NEARS: temperate['data'] = {user.get_host_port(): user.serialize() for user in self.p2p.user} allow_list.append(user) elif item['cmd'] == ClientCmd.CHECK_REACHABLE: try: port = item['data']['port'] except: port = user.p2p_port temperate['data'] = is_reachable(host=user.host_port[0], port=port) allow_list.append(user) elif item['cmd'] == ClientCmd.FILE_CHECK: # {'hash': hash, 'uuid': uuid} file_hash = item['data']['hash'] file_path = os.path.join(V.TMP_PATH, 'file.' + file_hash + '.dat') f_existence = os.path.exists(file_path) if 'uuid' in item['data']: f_asked = self.__user2user_route.include(item['data']['uuid']) else: f_asked = False temperate['data'] = {'have': f_existence, 'asked': f_asked} allow_list.append(user) elif item['cmd'] == ClientCmd.FILE_GET: def asking(): # ファイル要求元のNodeに近いNode群を無視する nears_name = set(user_.name for user_ in self.p2p.user) best_name = list(nears_name - already_asked_user) random.shuffle(best_name) nears_name = list(nears_name) random.shuffle(nears_name) # nearを最後に探索するように並び替え try_to_ask_name = best_name + nears_name # ファイル所持Nodeを見つけたら即コマンド送る、それ以外は候補をリスト化 candidates = list() for ask_name in try_to_ask_name: try: ask_user = self.p2p.name2user(ask_name) if ask_user is None: continue send_data = {'hash': file_hash, 'uuid': item['uuid']} dummy, data = self.send_command(cmd=ClientCmd.FILE_CHECK, user=ask_user, data=send_data, timeout=2) except Exception as e: logging.debug("Check file existence one by one, %s", e) continue if data['have']: # ファイル所持Nodeを発見したのでGETを即送信 hopeful = ask_user break elif not data['asked']: candidates.append(ask_user) else: pass else: # 候補がいなければここで探索終了 if len(candidates) == 0: temperate['type'] = T_RESPONSE self._send_msg(item=temperate, allows=[user], denys=list()) logging.debug("Asking, stop asking file.") return else: hopeful = random.choice(candidates) # 一番新しいのを候補 logging.debug("Asking, Candidate={}, ask=>{}".format(len(candidates), hopeful.name)) try: data = {'hash': file_hash, 'asked': nears_name} self.__user2user_route.put(uuid=item['uuid'], item=(user, hopeful)) from_client, data = self.send_command(ClientCmd.FILE_GET, data, item['uuid'], user=hopeful, timeout=5) temperate['data'] = data if data is None: logging.debug("Asking failed from {} {}".format(hopeful.name, file_hash)) else: logging.debug("Asking success {} {}".format(hopeful.name, file_hash)) except Exception as e: logging.debug("Asking raised {} {} {}".format(hopeful.name, file_hash, e)) temperate['data'] = None temperate['type'] = T_RESPONSE count = self._send_msg(item=temperate, allows=[user], denys=list()) logging.debug("Response file to {} {}({})".format(user.name, count, file_hash)) return def sending(): with open(file_path, mode='br') as f: raw = f.read() temperate['type'] = T_RESPONSE temperate['data'] = raw self.__user2user_route.put(uuid=item['uuid'], item=(user, user)) if 0 < self._send_msg(item=temperate, allows=[user], denys=list()): logging.debug("Send file to {} {}".format(user.name, file_hash)) else: logging.debug("Failed send file to {} {}".format(user.name, file_hash)) if self.__user2user_route.include(item['uuid']): return logging.debug("Asked file get by {}".format(user.name)) file_hash = item['data']['hash'] already_asked_user = set(item['data']['asked']) file_path = os.path.join(V.TMP_PATH, 'file.' + file_hash + '.dat') # When you have file, sending. When you don't have file, asking if os.path.exists(file_path): Thread(target=sending, name='Sending', daemon=True).start() elif V.F_FILE_CONTINUE_ASKING: # Default disable Thread(target=asking, name='Asking', daemon=True).start() elif item['cmd'] == ClientCmd.FILE_DELETE: item_ = item['data'] file_hash = item_['hash'] signer_pk = item_['signer'] sign = item_['sign'] cert_sign = item_['cert']['sign'] master_pk = item_['cert']['master'] cert_start = item_['cert']['start'] cert_stop = item_['cert']['stop'] if not(cert_start < int(time.time()) < cert_stop): return # old signature elif master_pk not in C.MASTER_KEYS: return elif item['uuid'] in self.__broadcast_uuid: return # already get broadcast data elif self.__waiting_result.include(item['uuid']): return # I'm broadcaster, get from ack self.__broadcast_uuid.append(item['uuid']) cert_raw = bjson.dumps((master_pk, signer_pk, cert_start, cert_stop), compress=False) sign_raw = bjson.dumps((file_hash, item['uuid']), compress=False) deny_list.append(user) allow_list = None # send ACK ack_list.append(user) # send Response temperate['type'] = T_REQUEST temperate['data'] = item['data'] # delete file check try: logging.debug("1:Delete request {}".format(file_hash)) ecc = Encryption() ecc.pk = master_pk # 署名者の署名者チェック ecc.verify(msg=cert_raw, signature=cert_sign) ecc.pk = signer_pk # 署名者チェック ecc.verify(msg=sign_raw, signature=sign) if self.remove_file(file_hash): logging.info("2:Delete request accepted!") except ValueError: allow_list = list() # No sending elif item['cmd'] == ClientCmd.DIRECT_CMD: def direct_cmd(): data = item['data'] temperate['data'] = self.event.work(cmd=data['cmd'], data=data['data']) self._send_msg(item=temperate, allows=[user]) if 'cmd' in item['data'] and item['data']['cmd'] in self.event: Thread(target=direct_cmd, name='DirectCmd', daemon=True).start() else: pass # send message send_count = self._send_msg(item=temperate, allows=allow_list, denys=deny_list) # send ack ack_count = 0 if len(ack_list) > 0: temperate['type'] = T_ACK temperate['data'] = send_count ack_count = self._send_msg(item=temperate, allows=ack_list) # debug if Debug.P_RECEIVE_MSG_INFO: logging.debug("Reply to request {} All={}, Send={}, Ack={}" .format(temperate['cmd'], len(self.p2p.user), send_count, ack_count))
def encode(*args): assert len(args) == 3 return bjson.dumps(args, compress=False)
def change_mintcoin(mint_id, cur, amount=None, description=None, image=None, setting=None, new_address=None, gas_price=None, sender=C.ANT_UNKNOWN, retention=10800): assert amount or description or image or setting or new_address params = dict() if description: params['description'] = description if image: params['image'] = image if new_address: params['address'] = new_address if len(params) == 0: params = None if not params and not setting and not amount: raise BlockChainError('No update found.') m_before = get_mintcoin_object(coin_id=mint_id) if m_before.version == -1: raise BlockChainError('Not init mintcoin. {}'.format(m_before)) result = check_mintcoin_new_format(m_before=m_before, new_params=params, new_setting=setting) if isinstance(result, str): raise BlockChainError('check_mintcoin_new_format(): {}'.format(result)) msg_body = bjson.dumps((mint_id, params, setting), compress=False) tx = TX( tx={ 'type': C.TX_MINT_COIN, 'gas_price': gas_price or V.COIN_MINIMUM_PRICE, 'gas_amount': 1, 'message_type': C.MSG_BYTE, 'message': msg_body }) if amount: tx.outputs.append((MINTCOIN_DUMMY_ADDRESS, 0, amount)) send_coins = Balance(0, amount) minting_coins = Balance(mint_id, amount) else: send_coins = Balance(0, 0) minting_coins = Balance(0, 0) tx.update_time(retention) additional_gas = C.MINTCOIN_GAS + C.SIGNATURE_GAS # for mint_coin user signature tx.gas_amount = tx.size + C.SIGNATURE_GAS + additional_gas tx.serialize() # fill unspents fee_coin_id = 0 input_address = fill_inputs_outputs(tx=tx, cur=cur, fee_coin_id=fee_coin_id, additional_gas=additional_gas) input_address.add(m_before.address) fee_coins = Balance(coin_id=fee_coin_id, amount=tx.gas_price * tx.gas_amount) # check amount check_enough_amount(sender=sender, send_coins=send_coins, fee_coins=fee_coins) # replace dummy address replace_redeem_dummy_address(tx=tx, cur=cur) # replace dummy mint_id replace_mint_dummy_address(tx=tx, mint_address=m_before.address, mint_id=mint_id, f_raise=False) # setup signature tx.serialize() setup_signature(tx=tx, input_address=input_address) # movement movements = Accounting() movements[sender] += minting_coins movements[C.ANT_OUTSIDE] -= minting_coins movements[sender] -= fee_coins movements[C.ANT_OUTSIDE] += fee_coins insert_log(movements, cur, tx.type, tx.time, tx.hash) return tx