def transfer_multi_cross_chain_asset(self, input_private_key: str, lock_address: str, cross_address: str, tx_count: int, amount: int, recharge: bool, port: int): account = Account(input_private_key) response = rpc.list_unspent_utxos(account.address(), port=port) if not response or isinstance(response, dict): Logger.error("get utxos return error: {}".format(response)) return False utxos = response if len(utxos) < tx_count: Logger.error("utxo is not enough") return False for i in range(tx_count): current_utxos = list() utxo = utxos[i] current_utxos.append(utxo) Logger.info("current cross chain index: {}".format(i)) tx = txbuild.create_cross_chain_transaction_by_utxo( input_private_key=input_private_key, lock_address=lock_address, cross_chain_address=cross_address, amount=amount, recharge=recharge, utxos=current_utxos) if tx is None: return False tx = txbuild.single_sign_transaction(input_private_key, tx) Logger.debug("cross chain asset transaction: \n{}".format(tx)) ret = self.handle_tx_result(tx, port) return ret
def pressure_inputs(self, inputs_num: int): output_addresses = list() for i in range(inputs_num): output_addresses.append(self.pressure_account.address()) ret = self.tx_manager.transfer_asset(self.tap_account.private_key(), output_addresses, 1 * util.TO_SELA) if ret: self.wait_block() value = rpc.get_balance_by_address(self.pressure_account.address()) Logger.debug("{} account {} wallet balance: {}".format( self.tag, self.pressure_account.address(), value)) ret = self.tx_manager.transfer_asset( self.pressure_account.private_key(), [self.pressure_account.address()], int(Decimal(value) * util.TO_SELA - util.TX_FEE)) if ret: self.wait_block() return True else: Logger.error("{} pressure inputs transfer failed".format( self.tag)) return False else: Logger.error("{} pressure outupts transfer failed".format( self.tag)) return ret
def create_side_info(self, node_type: str): self.params.arbiter_params.side_info[node_type] = dict() global side_chain_genesis_hash if node_type is not "geth": side_port = util.reset_port(1, node_type, "json_port") Logger.warn("{} side port: {}".format(self.tag, side_port)) side_chain_genesis_hash = rpc.get_block_hash_by_height( 0, side_port) Logger.debug("{} {} genesis hash: {}".format( self.tag, node_type, self.params.arbiter_params.side_chain_genesis_hash)) else: side_chain_genesis_hash = self.params.eth_params.genesis_hash recharge_address = keytool.gen_cross_chain_address( bytes.fromhex(side_chain_genesis_hash)) self.params.arbiter_params.recharge_address = recharge_address self.params.arbiter_params.withdraw_address = "0000000000000000000000000000000000" self.params.arbiter_params.side_chain_genesis_hash = side_chain_genesis_hash Logger.info("{} side genesis hash:{}".format(self.tag, side_chain_genesis_hash)) Logger.info("{} recharge address: {}".format( self.tag, self.params.arbiter_params.recharge_address)) Logger.info("{} withdraw address: {}".format( self.tag, self.params.arbiter_params.withdraw_address)) self.params.arbiter_params.side_info[node_type][ constant.SIDE_GENESIS_ADDRESS] = side_chain_genesis_hash self.params.arbiter_params.side_info[node_type][ constant.SIDE_RECHARGE_ADDRESS] = recharge_address self.params.arbiter_params.side_info[node_type][constant.SIDE_WITHDRAW_ADDRESS] = \ self.params.arbiter_params.withdraw_address
def ready_for_cr(self): ret = self.register_cr_candidates() self.get_current_height() self.check_result("register cr", ret) Logger.info("{} register cr on success!".format(self.tag)) ret = self.tx_manager.vote_cr_candidates() self.get_current_height() self.check_result("vote cr", ret) Logger.info("{} vote cr on success!".format(self.tag)) # transfer to CRFoundation # self.tx_manager.transfer_asset(self.tap_account.private_key(), [self.CR_Foundation_TEMP], 5000 * util.TO_SELA) # if ret: # rpc.discrete_mining(1) # value = rpc.get_balance_by_address(self.CR_Foundation_TEMP) # Logger.debug("{} CRFoundation {} wallet balance: {}".format(self.tag, self.CR_Foundation_TEMP, value)) # else: # Logger.error("{} CRFoundation transfer failed".format(self.tag)) self.tx_manager.transfer_asset(self.tap_account.private_key(), [self.CRC_COMMITTEE_ADDRESS], 5000 * util.TO_SELA) if ret: rpc.discrete_mining(1) value = rpc.get_balance_by_address(self.CRC_COMMITTEE_ADDRESS) Logger.debug("{} CRFoundation {} wallet balance: {}".format( self.tag, self.CRC_COMMITTEE_ADDRESS, value)) else: Logger.error("{} CRFoundation transfer failed".format(self.tag))
def gen_signature(self): r = b"" r = self.serialize_unsigned(r, self.version) signature = keytool.ecdsa_sign(self.private_key, r) Logger.debug("{} len signature: {}".format(self.tag, len(signature))) self.signature = signature return signature
def get_dpos_votes(self): list_producers = rpc.list_producers(0, 200) if list_producers is False: return 0 total_votes = float(list_producers["totalvotes"]) # candidates = rpc.get_arbiters_info()["candidates"] dpos_votes = dict() producers = list_producers["producers"] for producer in producers: node_pubkey = producer["nodepublickey"] dpos_votes[ self.node_manager.node_pubkey_name_dict[node_pubkey]] = float( producer["votes"]) # if after_h2_transactions is not None: # for tx_income in after_h2_transactions: # if tx_income.node_pubkey != "" and tx_income.node_pubkey in candidates: # total_votes -= tx_income.votes # node_name = self.node_manager.node_pubkey_name_dict[tx_income.node_pubkey] # origin_votes = dpos_votes[node_name] # origin_votes -= tx_income.votes # dpos_votes[node_name] = origin_votes Logger.debug("{} total votes: {}".format(self.tag, total_votes)) dpos_votes["total"] = total_votes self.dpos_votes_dict = dpos_votes return dpos_votes
def start(self): if self.running: return if self.arbiter_enable: self.process = subprocess.Popen( "./ela{} -p {}".format(self.index, self.password), stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir ) if self.index in range(1, self.params.crc_number + 1): Logger.debug("{} crc{} started on success.".format(self.tag, self.index)) elif self.index in range(self.params.crc_number + 1, self.params.crc_number * 3 + 1): Logger.debug("{} producer{} started on success.".format(self.tag, self.index)) else: Logger.debug("{} candidate{} started on success.".format(self.tag, self.index)) else: self.process = subprocess.Popen( "./ela{}".format(self.index), stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir ) if self.index == 0: Logger.debug("{} miner started on success.".format(self.tag)) else: Logger.debug("{} normal ela{} started on success.".format(self.tag, self.index)) self.running = True return True
def register_producers_candidates(self): global result result = False for i in range( self.params.ela_params.crc_number + 1, self.params.ela_params.number - round(self.params.ela_params.later_start_number / 2) + 1 ): ela_node = self.node_manager.ela_nodes[i] public_key = ela_node.node_account.public_key() ret = self.register_producer(ela_node) if not ret: return False rpc.discrete_mining(7) status = rpc.producer_status(public_key) Logger.debug("After mining 7 blocks, register status: {}".format(status)) result = status == "Active" if not result: Logger.error("{} register producer {} failed".format(self.tag, ela_node.name)) break Logger.info("{} register node-{} to be a producer on success!\n".format(self.tag, i)) return result
def register_cr_candidates(self): global result result = True for i in range(1, self.params.ela_params.crc_number + 1): ela_node = self.node_manager.ela_nodes[i] cid = ela_node.cr_account.cid_address() cr_info = self.create_cr_info( register_private_key=ela_node.cr_account.private_key(), nickname="CR-00{}".format(i), url="www.00{}.com".format(i), location=0) ret = self.tx_manager.register_cr( input_private_key=self.tap_account.private_key(), amount=5000 * constant.TO_SELA, cr_info=cr_info) if not ret: return False self.discrete_miner(7) status = self.get_cr_status(cid) Logger.debug( "After mining 7 blocks, register status: {}".format(status)) result = status == "Active" if not result: Logger.error("{} register CR {} failed".format( self.tag, ela_node.name)) break Logger.info("{} register CR-{} to be a CR on success!\n".format( self.tag, i)) return result
def create_cross_chain_asset(keystore: KeyStore, lock_address: str, cross_chain_address: str, amount: int, port: int): if lock_address is None or lock_address is "": Logger.error("Invalid lock address") return None if cross_chain_address is None or cross_chain_address is "": Logger.error("Invalid cross chain address") return None # create outputs: outputs, total_amount = create_normal_outputs( output_addresses=[lock_address], amount=amount, fee=10000, output_lock=0 ) # create inputs: inputs, change_outputs = create_normal_inputs(keystore.address(), total_amount, port) if inputs is None or change_outputs is None: Logger.error("Create normal inputs failed") return None outputs.extend(change_outputs) # create program programs = list() redeem_script = keystore.sign_script program = Program(code=redeem_script, params=None) programs.append(program) # create attributes attributes = list() attribute = Attribute( usage=Attribute.NONCE, data=bytes("attributes".encode()) ) attributes.append(attribute) cross_chain_asset = TransferCrossChainAsset() cross_chain_asset.cross_chain_addresses = [cross_chain_address] cross_chain_asset.output_indexes = [0] cross_chain_asset.cross_chain_amounts = [amount - 10000] tx = Transaction() if port == rpc.DEFAULT_PORT: tx.version = Transaction.TX_VERSION_09 else: tx.version = Transaction.TX_VERSION_DEFAULT Logger.debug("transaction version {}".format(tx.version)) tx.tx_type = Transaction.TRANSFER_CROSS_CHAIN_ASSET tx.payload = cross_chain_asset tx.attributes = attributes tx.inputs = inputs tx.outputs = outputs tx.lock_time = 0 tx.programs = programs return tx
def test_content(): test_case = "update producer after pre offset but before h2" controller = Controller(config) controller.ready_for_dpos() h1 = controller.params.ela_params.crc_dpos_height h2 = controller.params.ela_params.public_dpos_height pre_offset = config["ela"]["pre_connect_offset"] update_node_prikey = "aa8ad6d1ac6953f7b9a68b5b13fe79d7217fb687651c1c30be12e5e0328b667f" # update_producer_beforeh1 = controller.tx_manager.register_producers_list[0] update_producer = controller.tx_manager.register_producers_list[0] current_height = controller.get_current_height() if current_height < h1 - 5: controller.discrete_mining_blocks(h1 - 5 - current_height) height_times = dict() height_times[current_height] = 1 global result global update_height update_height = 0 while True: current_height = controller.get_current_height() times = controller.get_height_times(height_times, current_height) Logger.debug("current height: {}, times: {}".format( current_height, times)) if times >= 100: result = False break if current_height >= h1: controller.show_arbiter_info() if update_height == 0 and current_height > h1 + 35: producer_payload = update_producer.producer_info() producer_payload.nickname = "^_^ HAHA" producer_payload.node_account = Account(update_node_prikey) producer_payload.url = "127.0.0.1" result = controller.tx_manager.update_producer( update_producer, producer_payload) controller.check_result(test_case, result) if result: controller.node_manager.node_pubkey_name_dict[ update_node_prikey] = producer_payload.nickname update_height = current_height if current_height > h2 + 100: break controller.discrete_mining_blocks(1) time.sleep(1) controller.check_result(test_case, result) controller.terminate_all_process(result)
def test_content(): test_case = "update producer after pre offset but before h2" controller = Controller(config) controller.ready_for_dpos() h1 = controller.params.ela_params.crc_dpos_height h2 = controller.params.ela_params.public_dpos_height pre_offset = config["ela"]["pre_connect_offset"] update_node_pubkey = "0303710a960f04893281fe016ec7563a9c17fb8c7f0bea555b3a9349a6a1646479" # update_producer_beforeh1 = controller.tx_manager.register_producers_list[0] update_producer = controller.tx_manager.register_producers_list[0] current_height = controller.get_current_height() if current_height < h1 - 5: controller.discrete_mining_blocks(h1 - 5 - current_height) height_times = dict() height_times[current_height] = 1 global result global update_height update_height = 0 while True: current_height = controller.get_current_height() times = controller.get_height_times(height_times, current_height) Logger.debug("current height: {}, times: {}".format(current_height, times)) if times >= 100: result = False break if current_height >= h1: controller.show_current_next_info() if update_height == 0 and current_height > h1 + 35: producer_payload = update_producer.info producer_payload.nickname = "^_^ HAHA" producer_payload.node_public_key = bytes.fromhex(update_node_pubkey) producer_payload.url = "127.0.0.1" result = controller.tx_manager.update_producer(update_producer, producer_payload) controller.check_result(test_case, result) if result: controller.rpc_manager.node_info_dict[update_node_pubkey] = producer_payload.nickname update_height = current_height if current_height > h2 + 100: break controller.discrete_mining_blocks(1) time.sleep(1) controller.check_result(test_case, result) controller.terminate_all_process()
def start(self): self.process = subprocess.Popen("./did{}".format(self.index), stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir) self.running = True Logger.debug("{} ./did{} started on success.".format( self.tag, self.index)) return True
def ready_for_dpos(self): producers = rpc.list_producers(0, 100, state="active") if producers is None: Logger.info('{} list producers is null'.format(self.tag)) return Logger.debug('{} list producers:{}'.format(self.tag, producers)) self.producers_list = producers["producers"] ret = self.vote_producers_candidates() self.check_result("vote producers", ret) Logger.info("{} vote producer on success!".format(self.tag))
def get_total_tx_fee(self, transactions: list): Logger.debug("{} transactions length: {}".format( self.tag, len(transactions))) if len(transactions) == 0: return 0 tx_fee = 0 for tx in transactions: if tx.valid: tx_fee += tx.tx_fee return tx_fee
def ready_for_cr(self): cr = rpc.list_current_crs() # cr = rpc.list_cr_candidates(0, 100, state="active") if cr is None: Logger.info('{} list cr is null'.format(self.tag)) return Logger.debug('{} list cr:{}'.format(self.tag, cr)) # self.cr_list = cr["crcandidatesinfo"] self.cr_list = cr["crmembersinfo"] ret = self.vote_cr_candidates() self.check_result("vote cr", ret) Logger.info("{} vote cr on success!".format(self.tag))
def get_request(url): try: Logger.debug("{} get request url: {}".format(tag, url)) response = requests.get(url) resp = response.json() if resp["Desc"] == "Success": return resp["Result"] else: return None except requests.exceptions.RequestException as e: Logger.error("{} get request error: {}".format(tag, e)) return False
def start(self): self.process = subprocess.Popen( "./arbiter{} -p {}".format(self.index, self.params.password), stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir ) time.sleep(0.2) self.running = True Logger.debug("{} ./arbiter{} started on success.".format(self.tag, self.index)) return True
def stop(self): if not self.running: Logger.error("{} ela{} has already stopped".format(self.tag, self.index)) return try: self.process.terminate() # self.dev_null.close() # self.err_output.close() except subprocess.SubprocessError as e: Logger.error("{} Unable to stop ela{}, error: {}".format(self.tag, self.index, e)) self.running = False Logger.debug("{} ela{} has stopped on success!".format(self.tag, self.index))
def handle_tx_result(self, tx: Transaction, port=rpc.DEFAULT_PORT): r = tx.serialize() response = rpc.send_raw_transaction(r.hex(), port) if isinstance(response, dict): Logger.error("{} rpc response: {}".format(self.tag, response)) Logger.error("rpc send raw transaction failed") return False tx_hash = util.bytes_reverse(bytes.fromhex(tx.hash())).hex() Logger.debug("{} tx hash : {}".format(self.tag, tx_hash)) Logger.debug("{} response: {}".format(self.tag, response)) return tx_hash
def create_normal_inputs(address: str, total_amount: int, port=rpc.DEFAULT_PORT): global total_amount_global global response total_amount_global = total_amount total_amount_format = str(Decimal(str(total_amount_global)) / Decimal(constant.TO_SELA)) if port != rpc.DEFAULT_PORT: response = rpc.list_unspent_utxos(address, port=port) else: response = rpc.get_utxos_by_amount(address, total_amount_format, port) if not response or isinstance(response, dict): Logger.debug("get utxos return error: {}".format(response)) return None, None utxos = response Logger.debug("utxos: {}".format(utxos)) inputs = list() change_outputs = list() program_hash = keytool.address_to_program_hash(address) for utxo in utxos: txid = util.bytes_reverse(bytes.fromhex(utxo["txid"])) index = utxo["vout"] input = Input(txid, index) inputs.append(input) amount = int(Decimal(utxo["amount"]) * constant.TO_SELA) if amount < total_amount_global: total_amount -= amount elif amount == total_amount: total_amount_global = 0 break elif amount > total_amount: change = Output( value=amount - total_amount_global, output_lock=0, program_hash=program_hash, output_type=Output.OT_NONE, output_payload=OutputPayload() ) change_outputs.append(change) total_amount_global = 0 break if total_amount_global > 0: Logger.error("Available token is not enough!") return None, None return inputs, change_outputs
def post_request(url, method, params): try: Logger.debug("{} method: {}".format(tag, method)) Logger.debug("{} params: {}".format(tag, params)) response = requests.post(url, json={"method": method, "params": params}, headers={"content-type": "application/json"}) resp = response.json() if resp["error"] == None: return resp["result"] else: return resp["error"] except requests.exceptions.RequestException as e: Logger.error("{} post request error: {}".format(tag, e)) return False
def mining_side_blocks(self, side_port: int, num=1): side_height_begin = rpc.get_block_count(side_port) while True: main_height = rpc.get_block_count() side_height = rpc.get_block_count(side_port) Logger.debug("{} main height: {}, side height: {}".format( self.tag, main_height, side_height)) if side_height - side_height_begin > num: break rpc.discrete_mining(1) time.sleep(3)
def start(self): Logger.info("remove old geth.") # rm old geth gethfile = os.path.join(self.config["gopath"], "bin/geth") bootnodefile = os.path.join(self.config["gopath"], "bin/bootnode") oraclefold = os.path.join( self.config["gopath"], "src/github.com/elastos/Elastos.ELA.SideChain.ETH/oracle") if os.path.exists(gethfile): os.remove(gethfile) Logger.info("start complie geth.") subprocess.Popen("sh ./replace_compile.sh", stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir) timecount = 60 while True: timecount -= 1 if timecount <= 0: break if os.path.exists(gethfile): shutil.copy(gethfile, os.path.join(self.cwd_dir, "ethnode1")) shutil.copy(gethfile, os.path.join(self.cwd_dir, "ethnode2")) shutil.copy(gethfile, os.path.join(self.cwd_dir, "ethnode3")) shutil.copy(bootnodefile, os.path.join(self.cwd_dir, "v4")) shutil.copy(bootnodefile, os.path.join(self.cwd_dir, "v5")) shutil.copytree(oraclefold, os.path.join(self.cwd_dir, "oracle")) break time.sleep(1) if timecount == 0: Logger.error("compile geth fail.") subprocess.Popen("sh ./killall.sh", stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir) exit(1) Logger.info("start geth.") self.process = subprocess.Popen("sh ./gethstart.sh", stdout=self.dev_null, stderr=self.err_output, shell=True, cwd=self.cwd_dir) time.sleep(0.5) self.running = True Logger.debug("eth nodes started on success.") return True
def save_to_file(self, dest_dir_path: str): if not self._valid: Logger.error("Invalid keystore!") return False if dest_dir_path is "": Logger.debug("Invalid parameter!") return False if not os.path.exists(dest_dir_path): os.makedirs(dest_dir_path) file_path = os.path.join(dest_dir_path, "keystore.dat") util.write_config_file(self.key_store_dict(), file_path) return True
def pressure_inputs(self): value = self.inputs_num * util.TX_FEE Logger.debug("{} account {} wallet balance: {}".format( self.tag, self.pressure_account.address(), value)) ret = self.tx_manager.transfer_asset( self.pressure_account.private_key(), [self.tap_account.address()], value - util.TX_FEE) if ret: self.wait_block() return True else: Logger.error("{} pressure inputs transfer failed".format(self.tag)) return False
def handle_tx_result(self, tx: Transaction, port=rpc.DEFAULT_PORT): # Logger.debug("{} {}".format(self.tag, tx)) r = tx.serialize() response = rpc.send_raw_transaction(r.hex(), port) if isinstance(response, dict): Logger.error("{} rpc response: {}".format(self.tag, response)) Logger.error("rpc send raw transaction failed") return False # response return on success, response is tx hash, but we should reverse it at first reverse_res = util.bytes_reverse(bytes.fromhex(response)).hex() Logger.debug("{} tx hash : {}".format(self.tag, tx.hash())) Logger.debug("{} response: {}".format(self.tag, reverse_res)) return tx.hash() == reverse_res
def recharge_necessary_keystore(self, input_private_key: str, accounts: list, amount: int): addresses = list() for a in accounts: addresses.append(a.address()) ret = self.transfer_asset(input_private_key, addresses, amount) if ret: rpc.discrete_mining(1) else: Logger.error("{} recharge necessary keystore failed".format(self.tag)) return False for i in range(len(addresses)): value = rpc.get_balance_by_address(addresses[i]) Logger.debug("{} arbiter {} wallet balance: {}".format(self.tag, i, value)) return ret
def __deploy_eth_nodes(self): src_path = os.path.join(self.params.root_path, "datas/stables/eth_nodes") killfile = os.path.join(self.params.root_path, "shell/killall.sh") dest_path = os.path.join(self.env_manager.test_path, self.env_manager.current_date_time, "eth_nodes") if not os.path.exists(src_path): return False Logger.debug("{} src_path: {}".format(self.tag, src_path)) shutil.copytree(src_path, dest_path) shutil.copy(killfile, dest_path) node = self._init_nodes("geth", "", 0, dest_path, "normal") node.reset_config() # util.write_config_file(node.config, os.path.join(dest_path, "config.json")) self.nodes_dict["geth"].append(node)
def _read_key_stores(self, category: str, num: int): dest_path = os.path.join(self.stables_path, category + ".json") if not os.path.exists(dest_path): Logger.error( "{} read key stores failed, dest path {} is not found, exit..." .format(self.tag, dest_path)) time.sleep(1) exit(-1) content_dict = util.read_config_file(dest_path) for i in range(num): private_key_str = content_dict[category + "_" + str(i)]["private_key"] a = Account(private_key_str) self.category_dict[category].append(a) Logger.debug("{} load {} keystore on success !".format( self.tag, category))