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 node-{} to be a CR on success!\n".format( self.tag, i)) return result
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 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 _check_password(self): ret = keytool.sha256_hash(str.encode(self._password), 3).hex() == self._get_password_hash() if not ret: Logger.error("Invalid password!") return ret
def get_dpos_real_income(self, height: int): response = rpc.get_block_by_height(height) if response is False or not isinstance(response, dict): Logger.error("{} rpc response invalid".format(self.tag)) return None # Logger.debug("{} response: {}".format(self.tag, response)) vout_list = response["tx"][0]["vout"] Logger.debug("{} vout list length: {}".format(self.tag, len(vout_list))) sum = 0.0 income_dict = dict() for vout in vout_list: address = vout["address"] if address == self.foundation_account.address( ) or address == self.node_manager.main_miner_address: continue value = float(vout["value"]) if isinstance(address, str) and address.startswith("8"): income_dict["CRC-I"] = value * constant.TO_SELA else: income_dict[self.node_manager.address_name_dict[ address]] = value * constant.TO_SELA sum += value Logger.debug("{} income dict: {}".format(self.tag, sorted(income_dict.items()))) Logger.debug("{} dpos votes: {}".format(self.tag, self.dpos_votes_dict)) return income_dict
def add_sub_account(self, sub_account: Account): if not self._valid: Logger.error("Invalid keystore!") return self._sub_accounts_list.append(sub_account) sub_account_data = self._create_account_data(sub_account, self.SUB_ACCOUNT) self._accounts_data_list.append(sub_account_data)
def has_dpos_reward(self, height: int): response = rpc.get_block_by_height(height) if response is False: Logger.error("{} rpc response invalid".format(self.tag)) return False # Logger.debug("{} response: {}".format(self.tag, response)) return len(response["tx"][0]["vout"]) > 2
def arbiter_public_keys(key_stores): public_keys = [] if len(key_stores) != 5: Logger.error("[common] Invalid argument, the length of the argument must be equal 5") exit(0) for key_store in key_stores: public_keys.append(key_store.public_key.hex()) return public_keys
def get_dpos_theory_income(self, block_num: int, tx_fee: int, dpos_votes: dict): Logger.debug("{} block num: {}".format(self.tag, block_num)) Logger.debug("{} tx fee: {}".format(self.tag, tx_fee)) dpos_origin_income = math.ceil(self.get_reward_per_block() * 35 / 100) * block_num + (tx_fee * 35 / 100) Logger.debug("{} dpos origin income: {}".format( self.tag, dpos_origin_income)) total_block_confirm_reward = math.floor(dpos_origin_income * 25 / 100) total_top_producers_reward = math.floor(dpos_origin_income * 75 / 100) Logger.debug("{} income1: {}".format(self.tag, total_block_confirm_reward)) Logger.debug("{} income2: {}".format(self.tag, total_top_producers_reward)) current_arbiters = self.get_arbiter_names("arbiters") if not current_arbiters: Logger.error("{} get current arbiters info error".format(self.tag)) return None current_candidates = self.get_arbiter_names("candidates") if not current_candidates: Logger.error("{} get current candidates info error".format( self.tag)) return None individual_first_reward = math.floor(total_block_confirm_reward / len(current_arbiters)) total_votes = dpos_votes["total"] Logger.debug("dpos total votes: {}".format(total_votes)) individual_second_reward = math.floor(total_top_producers_reward / total_votes) Logger.debug("{} income1 each: {}".format(self.tag, individual_first_reward)) Logger.debug("{} income2 each: {}".format(self.tag, individual_second_reward)) theory_incomes = dict() # calculate current arbiter rewards theory_incomes["CRC-I"] = individual_first_reward * 4 for node_name in current_arbiters: if node_name.startswith("CRC"): continue theory_incomes[node_name] = individual_first_reward + dpos_votes[ node_name] * individual_second_reward # calculate current candidates rewards for node_name in current_candidates: if node_name.startswith("CRC"): continue theory_incomes[ node_name] = dpos_votes[node_name] * individual_second_reward Logger.debug("{} theory income: {}".format( self.tag, sorted(theory_incomes.items()))) return theory_incomes
def check_params(self): if self.params.ela_params.number < 3 * self.params.ela_params.crc_number + \ self.params.ela_params.later_start_number: Logger.error( "Ela node number should be >= 3 * crc number + later start number , " "please check your config in the beginning of your test case or config.json, exit..." ) time.sleep(1) exit(-1)
def get_master_key(self): password_twice_hash = keytool.sha256_hash(str.encode(self.password), 2) encrypt_master_key = self.get_encrypt_master_key() if encrypt_master_key is None: Logger.error("encrypt master key is None") return None master_key = keytool.aes_decrypt(bytes.fromhex(encrypt_master_key), password_twice_hash, self.iv) return master_key
def stop(self): if not self.running: Logger.error("{} arbiter{} 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 arbiter{}, error: {}".format(self.tag, self.index, e)) self.running = False
def serialize(self, version: int): if self.candidates is None or len(self.candidates) == 0: Logger.error("candidates list is empty!") return None r = b"" r += struct.pack("<B", self.vote_type) r += serialize.write_var_uint(len(self.candidates)) for candidate in self.candidates: r = serialize.write_var_bytes(r, candidate) return r
def get_encrypt_private_key(self): if "Account" not in self.keystore_dat.keys(): Logger.error("keystore_dat dose not contain key Account") return None at = self.keystore_dat["Account"][0] if "PrivateKeyEncrypted" not in at.keys(): Logger.error("Account dose not contain key PrivateKeyEncrypted") return None return at["PrivateKeyEncrypted"]
def serialize(self): if self.contents is None or len(self.contents) == 0: Logger.error("contents is invalid") return None r = b"" r += struct.pack("<B", self.version) r += serialize.write_var_uint(len(self.contents)) for content in self.contents: r += content.serialize(self.version) return r
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 create_transaction(input_private_key: str, output_addresses: list, amount: int, rpc_port: int, side_chain: bool): account = Account(input_private_key) # check output if output_addresses is None or len(output_addresses) == 0: Logger.error("Invalid output addresses") return None # create outputs outputs, total_amount = create_normal_outputs( output_addresses=output_addresses, amount=amount, fee=util.TX_FEE, output_lock=0 ) # create inputs inputs, change_outputs = create_normal_inputs(account.address(), total_amount, rpc_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 = bytes.fromhex(account.redeem_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) tx = Transaction() if not side_chain : tx.version = Transaction.TX_VERSION_09 else: tx.version = Transaction.TX_VERSION_DEFAULT tx.tx_type = Transaction.TRANSFER_ASSET tx.payload = Payload(Payload.DEFAULT_VERSION) tx.attributes = attributes tx.inputs = inputs tx.outputs = outputs tx.lock_time = 0 tx.programs = programs return tx
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 key_store_dict(self): if not self._valid: Logger.error("Invalid keystore!") return None store_dict = { "Version": "1.0.0", "PasswordHash": self._password_thrice_hash.hex(), "IV": self._iv.hex(), "MasterKey": self._master_key_encrypted.hex(), "Account": self._accounts_data_list } return store_dict
def create_normal_inputs(address: str, total_amount: int, rpc_port: int, utxo_index=-1): global total_amount_global global response total_amount_global = total_amount response = rpc.list_unspent_utxos(address, port=rpc_port) if not response or isinstance(response, dict): Logger.error("get utxos return error: {}".format(response)) return None, None utxos = response if utxo_index >= 0 and len(utxos) >= utxo_index+1: utxo = utxos[utxo_index] utxos = list() utxos.append(utxo) 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"]) * util.TO_SELA) if amount < total_amount_global: # total_amount -= amount total_amount_global -= amount elif amount == total_amount_global: total_amount_global = 0 break elif amount > total_amount_global: 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 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 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 serialize(self, r: bytes, version: int): if len(self.cross_chain_addresses) != len(self.output_indexes) or \ len(self.cross_chain_addresses) != len(self.cross_chain_addresses): Logger.error("Invalid cross chain asset") return None r += serialize.write_var_uint(len(self.cross_chain_addresses)) for i in range(len(self.cross_chain_addresses)): r = serialize.write_var_bytes(r, bytes(self.cross_chain_addresses[i].encode())) r += serialize.write_var_uint(self.output_indexes[i]) r += struct.pack("<q", self.cross_chain_amounts[i]) return r
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 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 pressure_outputs(self): output_addresses = list() for i in range(self.outputs_num): output_addresses.append(self.pressure_account.address()) ret = self.tx_manager.transfer_asset(self.tap_account.private_key(), output_addresses, util.TX_SINGLE_OUTPUT, not self.recharge) if ret: self.wait_block() return True else: Logger.error("{} pressure outputs transfer failed".format( self.tag)) return False
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 get_private_key(self): encrypt_private_key = self.get_encrypt_private_key() if encrypt_private_key is None: Logger.error("encrypt private key is None") return None encrypt_private_key_bytes = bytes.fromhex(encrypt_private_key) if len(encrypt_private_key_bytes) != 96: Logger.error("encrypt private key length is not 96") return None private_key = keytool.aes_decrypt(encrypt_private_key_bytes, self.mater_key, self.iv) return private_key[64:96]
def create_vote_transaction(input_private_key: str, candidates_list: list, amount: int, rpc_port: int, vote_content): # check output if candidates_list is None or len(candidates_list) == 0: Logger.error("Invalid output addresses") return None # candidates_bytes_list = list() # # for candidate in candidates_list: # candidates_bytes_list.append(bytes.fromhex(candidate)) # create outputs account = Account(input_private_key) outputs = create_vote_output(account.address(), amount, vote_content) # create inputs total_amount = amount + util.TX_FEE inputs, change_outputs = create_normal_inputs(account.address(), total_amount, rpc_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 = bytes.fromhex(account.redeem_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) tx = Transaction() tx.version = Transaction.TX_VERSION_09 tx.tx_type = Transaction.TRANSFER_ASSET tx.payload = Payload(Payload.DEFAULT_VERSION) tx.attributes = attributes tx.inputs = inputs tx.outputs = outputs tx.lock_time = 0 tx.programs = programs return tx
def create_register_transaction(keystore: KeyStore, output_addresses: list, amount: int, payload: ProducerInfo, fee=10000, output_lock=0): # check output if output_addresses is None or len(output_addresses) == 0: Logger.error("Invalid output addresses") return None # create outputs outputs, total_amount = create_normal_outputs( output_addresses=output_addresses, amount=amount, fee=fee, output_lock=output_lock ) # create inputs inputs, change_outputs = create_normal_inputs(keystore.address(), total_amount) 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) tx = Transaction() tx.version = Transaction.TX_VERSION_09 tx.tx_type = Transaction.REGISTER_PRODUCER tx.payload_version = 0 tx.payload = payload tx.attributes = attributes tx.inputs = inputs tx.outputs = outputs tx.lock_time = 0 tx.programs = programs return tx