def init(self): try: self.blockLimit = 500 # check chainID common.check_int_range(client_config.groupid, BcosClient.max_group_id) # check group id common.check_int_range(client_config.fiscoChainId, BcosClient.max_chain_id) # check protocol if client_config.client_protocol.lower( ) not in BcosClient.protocol_list: raise BcosException( "invalid configuration, must be: {}".format(''.join( BcosClient.protocol_list))) # check account keyfile self.keystore_file = "{}/{}".format( client_config.account_keyfile_path, client_config.account_keyfile) if os.path.exists(self.keystore_file) is False: raise BcosException( ("keystore file {} doesn't exist, " "please check client_config.py again " "and make sure this account exist").format( self.keystore_file)) self.fiscoChainId = client_config.fiscoChainId self.groupid = client_config.groupid if client_config.client_protocol == client_config.PROTOCOL_RPC \ and client_config.remote_rpcurl is not None: self.rpc = utils.rpc.HTTPProvider(client_config.remote_rpcurl) self.rpc.logger = self.logger if client_config.client_protocol == client_config.PROTOCOL_CHANNEL: if os.path.exists(client_config.channel_node_cert) is False: raise BcosException("{} not found!".format( client_config.channel_node_cert)) if os.path.exists(client_config.channel_node_key) is False: raise BcosException("{} not found!".format( client_config.channel_node_key)) self.channel_handler = ChannelHandler() self.channel_handler.logger = self.logger self.channel_handler.initTLSContext( client_config.channel_ca, client_config.channel_node_cert, client_config.channel_node_key) self.channel_handler.start_channel(client_config.channel_host, client_config.channel_port) blockNumber = self.getBlockNumber() self.channel_handler.setBlockNumber(blockNumber) self.channel_handler.getBlockNumber(self.groupid) self.logger.info("using protocol " + client_config.client_protocol) return self.getinfo() except Exception as e: raise BcosException("init bcosclient failed, reason: {}".format(e))
def init(self): self.fiscoChainId = client_config.fiscoChainId self.groupid = client_config.groupid if client_config.client_protocal == client_config.PROTOCAL_RPC \ and client_config.remote_rpcurl!=None: self.rpc = utils.rpc.HTTPProvider(client_config.remote_rpcurl) self.rpc.logger = self.logger if client_config.client_protocal == client_config.PROTOCAL_CHANNEL: self.channel_handler = ChannelHandler() self.channel_handler.logger = self.logger self.channel_handler.initTLSContext( client_config.channel_ca, client_config.channel_node_cert, client_config.channel_node_key) self.channel_handler.start(client_config.channel_host, client_config.channel_port) self.logger.info("using protocal " + client_config.client_protocal) #print("ip:{},port:{}".format(client_config.channel_host,client_config.channel_port) ) return self.getinfo()
class BcosClient: default_from_account_signer: Signer_Impl = None rpc = None channel_handler = None fiscoChainId = None groupid = None logger = clientlogger.logger # logging.getLogger("BcosClient") request_counter = itertools.count() max_group_id = pow(2, 15) - 1 max_chain_id = pow(2, 63) - 1 protocol_list = ["rpc", "channel"] sysconfig_keys = ["tx_count_limit", "tx_gas_limit"] def __init__(self): self.init() self.lastblocknum = 0 self.lastblocklimittime = 0 def __del__(self): """ release the resources """ self.finish() # load the account from keyfile def load_default_account(self): if self.default_from_account_signer is not None: return # 不需要重复加载 if client_config.crypto_type == CRYPTO_TYPE_GM: # 加载默认国密账号 self.gm_account_file = "{}/{}".format( client_config.account_keyfile_path, client_config.gm_account_keyfile) self.default_from_account_signer = Signer_GM.from_key_file( self.gm_account_file, client_config.gm_account_password) return # 默认的 ecdsa 账号 # check account keyfile if client_config.crypto_type == CRYPTO_TYPE_ECDSA: self.key_file = "{}/{}".format(client_config.account_keyfile_path, client_config.account_keyfile) self.default_from_account_signer = Signer_ECDSA.from_key_file( self.key_file, client_config.account_password) def init(self): try: self.blockLimit = 500 # check chainID common.check_int_range(client_config.groupid, BcosClient.max_group_id) # check group id common.check_int_range(client_config.fiscoChainId, BcosClient.max_chain_id) # check protocol if client_config.client_protocol.lower( ) not in BcosClient.protocol_list: raise BcosException( "invalid configuration, must be: {}".format(''.join( BcosClient.protocol_list))) self.fiscoChainId = client_config.fiscoChainId self.groupid = client_config.groupid if client_config.client_protocol == client_config.PROTOCOL_RPC \ and client_config.remote_rpcurl is not None: self.rpc = utils.rpc.HTTPProvider(client_config.remote_rpcurl) self.rpc.logger = self.logger if client_config.client_protocol == client_config.PROTOCOL_CHANNEL: if os.path.exists(client_config.channel_node_cert) is False: raise BcosException("{} not found!".format( client_config.channel_node_cert)) if os.path.exists(client_config.channel_node_key) is False: raise BcosException("{} not found!".format( client_config.channel_node_key)) self.channel_handler = ChannelHandler() self.channel_handler.setDaemon(True) self.channel_handler.logger = self.logger self.channel_handler.initTLSContext( client_config.ssl_type, client_config.channel_ca, client_config.channel_node_cert, client_config.channel_node_key, client_config.channel_en_crt, client_config.channel_en_key) self.channel_handler.start_channel(client_config.channel_host, client_config.channel_port) blockNumber = self.getBlockNumber() self.channel_handler.setBlockNumber(blockNumber) self.channel_handler.getBlockNumber(self.groupid) self.logger.info("using protocol " + client_config.client_protocol) return self.getinfo() except Exception as e: raise BcosException("init bcosclient failed, reason: {}".format(e)) def finish(self): if client_config.client_protocol == client_config.PROTOCOL_CHANNEL \ and self.channel_handler is not None: self.channel_handler.finish() def getinfo(self): info = "" if client_config.client_protocol == client_config.PROTOCOL_RPC: info = "rpc:{}\n".format(self.rpc) if client_config.client_protocol == client_config.PROTOCOL_CHANNEL: info = "channel {}:{}".format(self.channel_handler.host, self.channel_handler.port) info += ",groupid :{}".format(self.groupid) info += ",crypto type:{}".format(client_config.crypto_type) info += ",ssl type:{}".format(client_config.ssl_type) return info def is_error_response(self, response): if response is None: raise BcosError(-1, None, "response is None") result = response["result"] if isinstance(result, dict) and "error" in result.keys(): msg = result["error"]["message"] code = result["error"]["code"] data = None if ("data" in result["error"]): data = result["error"]["data"] self.logger.error( "is_error_response code: {}, msg:{} ,data:{}".format( code, msg, data)) raise BcosError(code, data, msg) return None def common_request(self, cmd, params, packet_type=ChannelPack.TYPE_RPC): response = None try: next(self.request_counter) stat = StatTool.begin() if client_config.client_protocol == client_config.PROTOCOL_RPC: response = self.rpc.make_request(cmd, params) if client_config.client_protocol == client_config.PROTOCOL_CHANNEL: response = self.channel_handler.make_channel_rpc_request( cmd, params, ChannelPack.TYPE_RPC, packet_type) self.is_error_response(response) memo = "DONE" stat.done() stat.debug("commonrequest:{}:{}".format(cmd, memo)) return response["result"] except Exception as e: # timeout exception exception_str = str(e).lower() if "timeout" in exception_str: raise BcosException( ("{} timeout for without response after 60s, " "please check the status of the node").format(cmd)) else: raise BcosError(-1, None, ( "{} failed," " params: {}, response: {}, error information: {}").format( cmd, params, json.dumps(response), e)) def getNodeVersion(self): """ get node version // Request curl -X POST --data '{"jsonrpc":"2.0","method":"getClientVersion", "params":[],"id":1}' http://127.0.0.1:8545 |jq // Response { "id": 83, "jsonrpc": "2.0", "result": { "Build Time": "20190106 20:49:10", "Build Type": "Linux/g++/RelWithDebInfo", "FISCO-BCOS Version": "2.0.0", "Git Branch": "master", "Git Commit Hash": "693a709ddab39965d9c39da0104836cfb4a72054" } } """ cmd = "getClientVersion" params = [] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblocknumber def getBlockNumber(self): cmd = "getBlockNumber" params = [self.groupid] num_hex = self.common_request(cmd, params) return int(num_hex, 16) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpbftview def getPbftView(self): cmd = "getPbftView" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsealerlist def getSealerList(self): cmd = "getSealerList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getobserverlist def getObserverList(self): cmd = "getObserverList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getconsensusstatus def getConsensusStatus(self): cmd = "getConsensusStatus" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsyncstatus def getSyncStatus(self): cmd = "getSyncStatus" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpeers def getPeers(self): cmd = "getPeers" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getgrouppeers def getGroupPeers(self): cmd = "getGroupPeers" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getnodeidlist def getNodeIDList(self): cmd = "getNodeIDList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getgrouplist def getGroupList(self): cmd = "getGroupList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockbyhash def getBlockByHash(self, block_hash, _includeTransactions=True): cmd = "getBlockByHash" common.check_hash(block_hash) includeTransactions = common.check_and_trans_to_bool( _includeTransactions) params = [self.groupid, block_hash, includeTransactions] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockbynumber def getBlockByNumber(self, num, _includeTransactions=True): """ get block according to number """ cmd = "getBlockByNumber" number = common.check_int_range(num) includeTransactions = common.check_and_trans_to_bool( _includeTransactions) params = [self.groupid, hex(number), includeTransactions] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockhashbynumber def getBlockHashByNumber(self, num): cmd = "getBlockHashByNumber" common.check_int_range(num) params = [self.groupid, hex(num)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_cn/release-2.0/docs/api.html#gettransactionbyhash def getTransactionByHash(self, hash): cmd = "getTransactionByHash" common.check_hash(hash) params = [self.groupid, hash] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionbyblockhashandindex def getTransactionByBlockHashAndIndex(self, hash, index): cmd = "getTransactionByBlockHashAndIndex" common.check_hash(hash) common.check_int_range(index) params = [self.groupid, hash, hex(index)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionbyblocknumberandindex def getTransactionByBlockNumberAndIndex(self, num, index): cmd = "getTransactionByBlockNumberAndIndex" common.check_int_range(num) common.check_int_range(index) params = [self.groupid, hex(num), hex(index)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionreceipt def getTransactionReceipt(self, hash): cmd = "getTransactionReceipt" common.check_hash(hash) params = [self.groupid, hash] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions def getPendingTransactions(self): cmd = "getPendingTransactions" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtxsize def getPendingTxSize(self): cmd = "getPendingTxSize" params = [self.groupid] tx_size = self.common_request(cmd, params) return int(tx_size, 16) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getcode def getCode(self, address): cmd = "getCode" fmt_addr = common.check_and_format_address(address) params = [self.groupid, fmt_addr] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettotaltransactioncount def getTotalTransactionCount(self): cmd = "getTotalTransactionCount" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsystemconfigbykey def getSystemConfigByKey(self, key): if key not in BcosClient.sysconfig_keys: raise ArgumentsError("invalid system key, must be {}".format( BcosClient.sysconfig_keys)) cmd = "getSystemConfigByKey" params = [self.groupid, key] return self.common_request(cmd, params) def channel_getBlockLimit(self): """ get blockNumber from _block_notify directly when use channelHandler """ return self.channel_handler.blockNumber + self.blockLimit def RPC_getBlocklimit(self): tick = time.time() deltablocklimit = 500 tickstamp = tick - self.lastblocklimittime self.logger.debug("blocklimit tick stamp {}".format(tickstamp)) if tickstamp < 100: # get blocklimit every 100sec return self.lastblocknum + deltablocklimit for i in range(0, 5): # try n times try: blocknum = self.getBlockNumber() oldblocknum = self.lastblocknum # print("last {},now {}".format(self.lastblocknum,blocknum)) if blocknum >= self.lastblocknum: self.lastblocknum = blocknum self.logger.info( "getBlocklimit:{},blocknum:{},old:{}".format( self.lastblocknum, blocknum, oldblocknum)) return self.lastblocknum + deltablocklimit except BcosError as e: self.logger.error("getBlocklimit error {}, {}".format( e.code, e.message)) time.sleep(0.2) continue return self.lastblocknum def getBlockLimit(self): """ get block limit """ if self.channel_handler is not None: return self.channel_getBlockLimit() return self.RPC_getBlocklimit() # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions def call(self, to_address, contract_abi, fn_name, args=None): cmd = "call" if to_address != "": common.check_and_format_address(to_address) self.load_default_account() functiondata = encode_transaction_data(fn_name, contract_abi, None, args) callmap = dict() callmap["data"] = functiondata callmap["from"] = self.default_from_account_signer.get_keypair( ).address callmap["to"] = to_address callmap["value"] = 0 # send transaction to the given group params = [client_config.groupid, callmap] # 发送 response = self.common_request(cmd, params) #print("response : ",response) # check status if "status" in response.keys(): status = int(response["status"], 16) error_message = transaction_status_code.TransactionStatusCode.get_error_message( status) if error_message is not None: raise BcosException( "call error, error message: {}".format(error_message)) if "output" in response.keys(): outputdata = response["output"] # 取得方法的abi,签名,参数 和返回类型,进行call返回值的解析 fn_abi, fn_selector, fn_arguments = get_function_info( fn_name, contract_abi, None, args, None, ) # print("fn_selector",fn_selector) # print("fn_arguments",fn_arguments) #fn_output_types = get_fn_abi_types_single(fn_abi, "outputs") fn_output_types = get_abi_output_types(fn_abi) try: #decoderesult = decode_single(fn_output_types, decode_hex(outputdata)) #print("fn_output_types",fn_output_types) decoderesult = decode_abi(fn_output_types, decode_hex(outputdata)) return decoderesult except BaseException as e: return response return response # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions ''' 可用于所有已知abi的合约,传入abi定义,方法名,正确的参数列表,即可发送交易。交易由BcosClient里加载的账号进行签名。 如果显式传入from_accont,要注意根据国密( CRYPTO_TYPE_GM) 或非国密选择类型(CRYPTO_TYPE_ECDSA),构建对应的signer ''' def sendRawTransaction(self, to_address, contract_abi, fn_name, args=None, bin_data=None, gasPrice=30000000, packet_type=ChannelPack.TYPE_RPC, from_account_signer=None): cmd = "sendRawTransaction" if to_address != "": common.check_and_format_address(to_address) # 第三个参数是方法的abi,可以传入None,encode_transaction_data做了修改,支持通过方法+参数在整个abi里找到对应的方法abi来编码 if bin_data is None: functiondata = encode_transaction_data(fn_name, contract_abi, None, args) # the args is None elif args is None: functiondata = bin_data # deploy with params else: print(contract_abi) fn_data = get_aligned_function_data(contract_abi, None, args) functiondata = bin_data + fn_data[2:] if to_address is not None and len(to_address) > 0: from eth_utils import to_checksum_address to_address = to_checksum_address(to_address) # 填写一个bcos transaction 的 mapping import random txmap = dict() txmap["randomid"] = random.randint(0, 1000000000) txmap["gasPrice"] = gasPrice txmap["gasLimit"] = gasPrice txmap["blockLimit"] = self.getBlockLimit() txmap["to"] = to_address txmap["value"] = 0 txmap["data"] = functiondata txmap["fiscoChainId"] = self.fiscoChainId txmap["groupId"] = self.groupid txmap["extraData"] = "" #print("\n>>>>functiondata ",functiondata) signtx = SignTx() # 关键流程:对交易签名,重构后全部统一到 SignTx 类(client/signtransaction.py)完成 # 如果本方法未传入 from_account, 则用默认的账户代替,要区分国密或非国密 # load default account if not set .notice: account only use for # sign transaction for sendRawTransa# if self.client_account is None: if from_account_signer is None: self.load_default_account() from_account_signer = self.default_from_account_signer # print(signtx.signer) signtx.signer = from_account_signer signed_result = signtx.sign_transaction(txmap) # print("@@@@@rawTransaction : ",encode_hex(signedTxResult.rawTransaction)) # signedTxResult.rawTransaction是二进制的,要放到rpc接口里要encode下 params = [self.groupid, encode_hex(signed_result.rawTransaction)] result = self.common_request(cmd, params, packet_type) return result # 发送交易后等待共识完成,检索receipt def channel_sendRawTransactionGetReceipt(self, to_address, contract_abi, fn_name, args=None, bin_data=None, gasPrice=30000000, from_account_signer=None): return self.sendRawTransaction(to_address, contract_abi, fn_name, args, bin_data, gasPrice, ChannelPack.TYPE_TX_COMMITTED, from_account_signer=from_account_signer) def rpc_sendRawTransactionGetReceipt(self, to_address, contract_abi, fn_name, args=None, bin_data=None, gasPrice=30000000, timeout=15, from_account_signer=None): # print("sendRawTransactionGetReceipt",args) stat = StatTool.begin() txid = self.sendRawTransaction(to_address, contract_abi, fn_name, args, bin_data, gasPrice, from_account_signer=from_account_signer) result = None for i in range(0, timeout): result = self.getTransactionReceipt(txid) # print("getTransactionReceipt : ", result) if result is None: time.sleep(1) self.logger.info( "sendRawTransactionGetReceipt,retrying getTransactionReceipt : {}" .format(i)) continue else: break # get the result break stat.done() memo = "DONE" if result is None: memo = "ERROR:TIMEOUT" stat.debug("sendRawTransactionGetReceipt,{}".format(memo)) if result is None: raise BcosError(-1, None, "sendRawTransactionGetReceipt,{}".format(memo)) return result def sendRawTransactionGetReceipt(self, to_address, contract_abi, fn_name, args=None, bin_data=None, gasPrice=30000000, timeout=15, from_account_signer=None): if self.channel_handler is not None: return self.channel_sendRawTransactionGetReceipt( to_address, contract_abi, fn_name, args, bin_data, gasPrice, from_account_signer=from_account_signer) return self.rpc_sendRawTransactionGetReceipt( to_address, contract_abi, fn_name, args, bin_data, gasPrice, timeout, from_account_signer=from_account_signer) ''' newaddr = result['contractAddress'] blocknum = result['blockNumber'] ''' def deploy(self, contract_bin, contract_abi=None, fn_args=None, from_account_signer=None): result = self.sendRawTransactionGetReceipt( to_address="", contract_abi=contract_abi, fn_name=None, args=fn_args, bin_data=contract_bin, from_account_signer=from_account_signer) # newaddr = result['contractAddress'] # blocknum = result['blockNumber'] # print("onblock : %d newaddr : %s "%(int(blocknum,16),newaddr)) return result def deployFromFile(self, contractbinfile, contract_abi=None, fn_args=None, from_account_signer=None): with open(contractbinfile, "r") as f: contractbin = f.read() f.close() result = self.deploy(contractbin, contract_abi, fn_args, from_account_signer=from_account_signer) return result
class BcosClient: client_account = None rpc = None channel_handler = None fiscoChainId = None groupid = None logger = clientlogger.logger #logging.getLogger("BcosClient") request_counter = itertools.count() def __init__(self): self.init() #load the account from keyfile def load_default_account(self): if (client_config.account_keyfile != None): keystorefile = client_config.account_keyfile_path + "/" + client_config.account_keyfile with open(keystorefile, "r") as dump_f: keytext = json.load(dump_f) privkey = Account.decrypt(keytext, client_config.account_password) self.client_account = Account.from_key(privkey) def init(self): self.fiscoChainId = client_config.fiscoChainId self.groupid = client_config.groupid if client_config.client_protocal == client_config.PROTOCAL_RPC \ and client_config.remote_rpcurl!=None: self.rpc = utils.rpc.HTTPProvider(client_config.remote_rpcurl) self.rpc.logger = self.logger if client_config.client_protocal == client_config.PROTOCAL_CHANNEL: self.channel_handler = ChannelHandler() self.channel_handler.logger = self.logger self.channel_handler.initTLSContext( client_config.channel_ca, client_config.channel_node_cert, client_config.channel_node_key) self.channel_handler.start(client_config.channel_host, client_config.channel_port) self.logger.info("using protocal " + client_config.client_protocal) #print("ip:{},port:{}".format(client_config.channel_host,client_config.channel_port) ) return self.getinfo() def finish(self): if client_config.client_protocal == client_config.PROTOCAL_CHANNEL and self.channel_handler != None: self.channel_handler.finish() def getinfo(self): info = "" if client_config.client_protocal == client_config.PROTOCAL_RPC: info = "rpc:{}\n".format(self.rpc) if client_config.client_protocal == client_config.PROTOCAL_CHANNEL: info = "channel {}:{}".format(self.channel_handler.host, self.channel_handler.port) info += ",groupid :{}\n".format(self.groupid) if self.client_account != None: info += "account address: {}\n".format(self.client_account.address) return info def is_error_reponse(self, response): if response == None: e = BcosError(-1, None, "response is None") return e if ("error" in response): msg = response["error"]["message"] code = response["error"]["code"] data = None if ("data" in response["error"]): data = response["error"]["data"] self.logger.error( "is_error_reponse code: {}, msg:{} ,data:{}".format( code, msg, data)) e = BcosError(code, data, msg) return e return None def common_request(self, cmd, params): next(self.request_counter) stat = StatTool.begin() if client_config.client_protocal == client_config.PROTOCAL_RPC: response = self.rpc.make_request(cmd, params) if client_config.client_protocal == client_config.PROTOCAL_CHANNEL: response = self.channel_handler.make_request( cmd, params, ChannelPack.TYPE_RPC) error = self.is_error_reponse(response) memo = "DONE" if (error != None): memo = "ERROR {}:{}".format(error.code, error.message) stat.done() stat.debug("commonrequest:{}:{}".format(cmd, memo)) if (error != None): raise error return response["result"] ''' // Request curl -X POST --data '{"jsonrpc":"2.0","method":"getClientVersion","params":[],"id":1}' http://127.0.0.1:8545 |jq // Result { "id": 83, "jsonrpc": "2.0", "result": { "Build Time": "20190106 20:49:10", "Build Type": "Linux/g++/RelWithDebInfo", "FISCO-BCOS Version": "2.0.0", "Git Branch": "master", "Git Commit Hash": "693a709ddab39965d9c39da0104836cfb4a72054" } } ''' def getNodeVersion(self): cmd = "getClientVersion" params = [] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblocknumber def getBlockNumber(self): cmd = "getBlockNumber" params = [self.groupid] num_hex = self.common_request(cmd, params) #print("getBlockNumber hex:",num_hex) return int(num_hex, 16) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpbftview def getPbftView(self): cmd = "getPbftView" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsealerlist def getSealerList(self): cmd = "getSealerList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getobserverlist def getObserverList(self): cmd = "getObserverList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getconsensusstatus def getConsensusStatus(self): cmd = "getConsensusStatus" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsyncstatus def getSyncStatus(self): cmd = "getSyncStatus" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpeers def getPeers(self): cmd = "getPeers" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getgrouppeers def getGroupPeers(self): cmd = "getGroupPeers" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getnodeidlist def getNodeIDList(self): cmd = "getNodeIDList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getgrouplist def getGroupList(self): cmd = "getGroupList" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockbyhash def getBlockByHash(self, hash, includeTransactions=False): cmd = "getBlockByHash" params = [self.groupid, hash, includeTransactions] return self.common_request(cmd, params) #https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockbynumber def getBlockByNumber(self, num, includeTransactions=False): cmd = "getBlockByNumber" params = [self.groupid, hex(num), includeTransactions] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getblockhashbynumber def getBlockHashByNumber(self, num): cmd = "getBlockHashByNumber" params = [self.groupid, hex(num)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_cn/release-2.0/docs/api.html#gettransactionbyhash def getTransactionByHash(self, hash): cmd = "getTransactionByHash" params = [self.groupid, hash] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionbyblockhashandindex def getTransactionByBlockHashAndIndex(self, hash, index): cmd = "getTransactionByBlockHashAndIndex" params = [self.groupid, hash, hex(index)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionbyblocknumberandindex def getTransactionByBlockNumberAndIndex(self, num, index): cmd = "getTransactionByBlockNumberAndIndex" params = [self.groupid, hex(num), hex(index)] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettransactionreceipt def getTransactionReceipt(self, hash): cmd = "getTransactionReceipt" params = [self.groupid, hash] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions def getPendingTransactions(self): cmd = "getPendingTransactions" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtxsize def getPendingTxSize(self): cmd = "getPendingTxSize" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getcode def getCode(self, address): cmd = "getCode" params = [self.groupid, address] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#gettotaltransactioncount def getTotalTransactionCount(self): cmd = "getTotalTransactionCount" params = [self.groupid] return self.common_request(cmd, params) # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getsystemconfigbykey def getSystemConfigByKey(self, key): cmd = "getSystemConfigByKey" params = [self.groupid, key] return self.common_request(cmd, params) lastblocknum = 0 lastblocklimittime = 0 def getBlocklimit(self): tick = time.time() deltablocklimit = 500 tickstamp = tick - self.lastblocklimittime self.logger.debug("blocklimit tick stamp {}".format(tickstamp)) if tickstamp < 100: #get blocklimit every 100sec return self.lastblocknum + deltablocklimit for i in range(0, 5): #try n times try: blocknum = self.getBlockNumber() oldblocknum = self.lastblocknum #print("last {},now {}".format(self.lastblocknum,blocknum)) if blocknum >= self.lastblocknum: self.lastblocknum = blocknum self.logger.info( "getBlocklimit:{},blocknum:{},old:{}".format( self.lastblocknum, blocknum, oldblocknum)) return self.lastblocknum + deltablocklimit except BcosError as e: self.logger.error("getBlocklimit error {}, {}".format( e.code, e.message)) time.sleep(0.2) continue return self.lastblocklimit # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions def call(self, to_address, contract_abi, fn_name, args=None): cmd = "call" if self.client_account == None: self.load_default_account() functiondata = encode_transaction_data(fn_name, contract_abi, None, args) callmap = dict() callmap["data"] = functiondata callmap["from"] = self.client_account.address callmap["to"] = to_address callmap["value"] = 0 params = [1, callmap] # 发送 response = self.common_request(cmd, params) outputdata = response["output"] #取得方法的abi,签名,参数 和返回类型,进行call返回值的解析 fn_abi, fn_selector, fn_arguments = get_function_info( fn_name, contract_abi, None, args, None, ) #print("fn_selector",fn_selector) #print("fn_arguments",fn_arguments) fn_output_types = get_fn_abi_types_single(fn_abi, "outputs") #print("output types str:", fn_output_types) decoderesult = decode_single(fn_output_types, decode_hex(outputdata)) return decoderesult # https://fisco-bcos-documentation.readthedocs.io/zh_CN/release-2.0/docs/api.html#getpendingtransactions ''' 可用于所有已知abi的合约,传入abi定义,方法名,正确的参数列表,即可发送交易。交易由BcosClient里加载的账号进行签名。 ''' def sendRawTransaction(self, to_address, contract_abi, fn_name, args=None, bin_data=None): cmd = "sendRawTransaction" # 第三个参数是方法的abi,可以传入None,encode_transaction_data做了修改,支持通过方法+参数在整个abi里找到对应的方法abi来编码 if (bin_data == None): functiondata = encode_transaction_data(fn_name, contract_abi, None, args) else: functiondata = bin_data if (to_address != None and len(to_address) > 0): from eth_utils import to_checksum_address to_address = to_checksum_address(to_address) #load default account if not set .notice: account only use for sign transaction for sendRawTransaction if self.client_account == None: self.load_default_account() # 填写一个bcos transaction 的 mapping import random txmap = dict() txmap["randomid"] = random.randint(0, 1000000000) # 测试用 todo:改为随机数 txmap["gasPrice"] = 30000000 txmap["gasLimit"] = 30000000 txmap["blockLimit"] = self.getBlocklimit() #501 # 测试用,todo:从链上查一下 txmap["to"] = to_address txmap["value"] = 0 txmap["data"] = functiondata txmap["fiscoChainId"] = self.fiscoChainId txmap["groupId"] = self.groupid txmap["extraData"] = "" # txmap["chainId"]=None #chainId没用了,fiscoChainId有用 #print(txmap) ''' from datatypes.bcostransactions import ( serializable_unsigned_transaction_from_dict, ) # 将mapping构建一个transaction对象,非必要,用来对照的 transaction = serializable_unsigned_transaction_from_dict(txmap) # 感受下transaction encode的原始数据 print(encode_hex(rlp.encode(transaction))) ''' # 实际上只需要用sign_transaction就可以获得rawTransaction的编码数据了,input :txmap,私钥 signedTxResult = Account.sign_transaction( txmap, self.client_account.privateKey) # signedTxResult.rawTransaction是二进制的,要放到rpc接口里要encode下 params = [self.groupid, encode_hex(signedTxResult.rawTransaction)] result = self.common_request(cmd, params) return result #发送交易后等待共识完成,检索receipt def sendRawTransactionGetReceipt(self, to_address, contract_abi, fn_name, args=None, bin_data=None, timeout=15): #print("sendRawTransactionGetReceipt",args) stat = StatTool.begin() txid = self.sendRawTransaction(to_address, contract_abi, fn_name, args, bin_data) result = None for i in range(0, timeout): result = self.getTransactionReceipt(txid) #print("getTransactionReceipt : ", result) if result == None: time.sleep(1) self.logger.info( "sendRawTransactionGetReceipt,retrying getTransactionReceipt : {}" .format(i)) continue else: break #get the result break stat.done() memo = "DONE" if result == None: memo = "ERROR:TIMEOUT" stat.debug("sendRawTransactionGetReceipt,{}".format(memo)) if result == None: raise BcosError(-1, None, "sendRawTransactionGetReceipt,{}".format(memo)) return result ''' newaddr = result['contractAddress'] blocknum = result['blockNumber'] ''' def deploy(self, contract_bin): result = self.sendRawTransactionGetReceipt\ (to_address="",contract_abi=None,fn_name=None,bin_data=contract_bin) newaddr = result['contractAddress'] blocknum = result['blockNumber'] #print("onblock : %d newaddr : %s "%(int(blocknum,16),newaddr)) return result