class ContentContract(object): """Content contract""" def __init__( self, keystore_file=None, keystore_pwd=None, private_key=None, provider=base_config.provider, gas_price=GAS_PRICE, ): """ 合约方法调用类 参数有可能会变化, 所以调用时最好指定参数名 :param keystore_file: user account keystore file, which include user account private key :param keystore_pwd: user account keystore password :param provider: Ulord side provider, such as http://xxxx:yyy, which is a RPC endpoint """ self.web3 = Web3(HTTPProvider(provider)) self.gas_limit = base_config.BLOCK_GAS_LIMIT self.gas_price = gas_price self.last_tx = None self.main_address = None if private_key: self.set_account_from_private_key(private_key) elif keystore_file and keystore_pwd: self.set_account_from_wallet(keystore_file, keystore_pwd) # Rinkeby 测试网络使用的是POA权威证明, 需要使用这个插件才能正常工作 # http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority if provider.startswith("https://rinkeby"): self.web3.middleware_stack.inject(geth_poa_middleware, layer=0) self.eth = Eth(self.web3) # 装载所有合约 self.reloading_contract() @staticmethod def _load_wallet(wallet_file): if not os.path.isfile(wallet_file): wallet_file = os.path.join(base_config.CURR_DIR, 'resources', 'keystore', wallet_file) if not os.path.isfile(wallet_file): raise ValueError("Wallet file not a valid path.") with open(wallet_file) as wf: wallet = json.load(wf) return wallet @staticmethod def _save_wallet(wallet, file_name): wf = os.path.join(base_config.CURR_DIR, 'resources', 'keystore', "{}.json".format(file_name)) print("Wallet file address:\n{}".format(wf)) with open(wf, "w") as f: json.dump(wallet, f) @staticmethod def valid_address(address): """验证是否是一个以太坊地址, 用EIP55校验和返回给定的地址。""" if not Web3.isAddress(address): return None address = Web3.toChecksumAddress(address) return address def _load_abi(self): abi_path = os.path.join(base_config.CURR_DIR, 'abi') file_list = os.listdir(abi_path) self.abi_files = {} for i in file_list: if not i.endswith(".abi"): continue with open(os.path.join(abi_path, i)) as cp: cp_abi = cp.read() self.abi_files[i[:-4]] = json.loads(cp_abi) def _nonce(self, value=0, address=None): if address is None: address = self.account.address nonce = self.eth.getTransactionCount(address) + value return nonce def _build_transaction(self, func, gas_limit=None, gas_price=None, nonce=None): """将合约方法的调用构建为离线交易对象""" return func.buildTransaction({ "nonce": nonce if nonce else self._nonce(), "gas": gas_limit if gas_limit else self.gas_limit, "gasPrice": gas_price if gas_price else self.gas_price, }) def _sign_and_send_rawtransaction(self, transaction): signed = self.account.signTransaction(transaction) tx_hash = Web3.toHex(self.eth.sendRawTransaction( signed.rawTransaction)) return tx_hash def create(self, wallet_password): """创建一个账户, 保存key file到对应目录的json文件中, 且返回私钥和地址""" setattr(self, "account", Account.create()) wallet = self.account.encrypt(wallet_password) self._save_wallet(wallet, self.account.address) return dict( privateKey=Web3.toHex(self.account.privateKey), address=self.account.address, ) def set_account_from_private_key(self, private_key, wallet_password=None): """如果有钱包密码, 则创建对应的keystore文件. 反之 ,则不新建""" setattr(self, "account", Account.privateKeyToAccount(private_key)) if wallet_password: wallet = self.account.encrypt(wallet_password) self._save_wallet(wallet, self.account.address) self.main_address = self.web3.eth.account.privateKeyToAccount( private_key).address # self.web3.eth.defaultAccount = self.web3.eth.accounts[0] return dict( privateKey=Web3.toHex(self.account.privateKey), address=self.account.address, ) def set_account_from_wallet(self, wallet_file, wallet_password): """从密钥文件加载 account 对象""" wallet = self._load_wallet(wallet_file) private_key = Account.decrypt(wallet, wallet_password) setattr(self, "account", Account.privateKeyToAccount(private_key)) self.main_address = self.web3.eth.account.privateKeyToAccount( private_key).address # print(self.web3.eth.defaultAccount, self.web3.eth.accounts,self.web3.eth.account) self.web3.eth.defaultAccount = self.web3.eth.account return dict(privateKey=Web3.toHex(private_key), address=self.account.address) def get_for_receipt(self, tx_hash): """ 获取交易回执 :param tx_hash: 获取交易回执(只有已打包的交易才有数据,否则返回None) :return: """ return self.web3.eth.getTransactionReceipt(tx_hash) def get_gas_balance(self, address): """ 获取侧链余额 """ if address is None: address = self.main_address address = self.valid_address(address) balance = self.web3.eth.getBalance(address, 'latest') return self.web3.fromWei(balance, 'ether') @check_account def transfer_gas(self, to_address, value): """ gas就是侧链的币, :param to_address: 接收地址 :param value: 转账金额(wei) :return: 交易hash """ print("nonce:", self._nonce()) to_address = self.valid_address(to_address) payload = { "to": to_address, "value": value, "gas": self.gas_limit, "gasPrice": self.gas_price, "nonce": self._nonce(), } res = self._sign_and_send_rawtransaction(payload) self.last_tx = res return res def transfer_tokens(self, addresses, qualitys): """ 多地址结算 :param addresses: List, 结算的地址列表 :param qualitys: List, 结算地址列表对应的金额 """ for i, address in enumerate(addresses): addresses[i] = self.valid_address(address) for i, quality in enumerate(qualitys): qualitys[i] = int(quality) publish_tx = self.contract["MulTransfer"].functions.mulPayDiff( addresses, qualitys).buildTransaction({ "nonce": self._nonce(), "gas": self.gas_limit, "gasPrice": self.gas_price }) res = self._sign_and_send_rawtransaction(publish_tx) self.last_tx = res return res # zza write def reloading_contract(self): try: # 所有合约的abi self._load_abi() # 所有合约 self._load_contract() except FileNotFoundError: print( "Please use the deploy_contract command to deploy the contract first, or manually prepare the " "contract address。") def _load_contract(self): # 读取合约地址 with open(os.path.join(base_config.CURR_DIR, "contractAddresses.json")) as wf: contract_addr_list = json.load(wf) self.contract = {} # 装载合约 for contract, addr in contract_addr_list.items(): try: self.contract[contract] = self.web3.eth.contract( address=self.valid_address(addr), abi=self.abi_files[contract]) except: continue view_funcs = [] abi = {} for func in self.abi_files[contract]: if func['type'] == 'function': if func.get('constant') and func.get( "stateMutability") == 'view': view_funcs.append(func["name"]) abi[func['name']] = func self.contract[contract].view_funcs = view_funcs self.contract[contract].abi = abi @check_account def func_call(self, contract_name, function, param): # 找到合约中对应的函数 contract = self.contract[contract_name] try: func = contract.functions.__getattribute__(function) except AttributeError: return "{} there is no {} function".format(contract_name, function) # 准备参数 inputs = contract.abi[function]['inputs'] param = self.format_param(param, inputs) # 增加参数 func = func() if len(param) == 0 else func(*param) # 静态函数 if function in self.contract[contract_name].view_funcs: addr = self.valid_address(base_config.main_address) return func.call({"from": addr}) # 需要上链的函数 if self.last_tx is None or self.get_for_receipt( self.last_tx) is not None: tx = self._build_transaction(func) res = self._sign_and_send_rawtransaction(transaction=tx) self.last_tx = res print("Call successful , transaction hash:") return res else: print( "The last transaction was not confirmed , please call get_for_receipt to view the last transaction" ) return self.last_tx def format_param(self, param, inputs): if isinstance(param, list): res = param else: res = list(param) if len(param) != len(inputs): return "Less parameters" for i in range(len(inputs)): _type = inputs[i].get('type') # if _type.endswith("[]"): # return "Array arguments are not recommended for command-line input, use http://remix.ethereum.org/ " \ # "manually " if _type in ['uint256', 'uint8']: res[i] = int(param[i]) elif 'bool' == _type: res[i] = bool(param[i]) elif 'address' == _type: res[i] = self.valid_address(param[i]) elif 'bytes16' == _type: # res[i] = self.web3.toHex(hexstr=param[i]) res[i] = param[i] else: continue return res def get_last_call_info(self): if self.last_tx is None: return None return self.get_for_receipt(self.last_tx)
class PlatonContractTransaction: """ 合约创建参数list结构:[txType,byteCode,abi] 合约调用参数list结构:[txType,funcName,params01,params02,...] """ def __init__(self, URL): # test address 'http://10.10.8.20:8545' self.w3 = Web3(Web3.HTTPProvider(URL)) self.eth = Eth(self.w3) self.personal = Personal(self.w3) print("if the web3 isConnected : {}".format(self.w3.isConnected())) def get_signed_data(self, fromAddress, toAddress, dataList, privateKey): myNonce = self.eth.getTransactionCount(fromAddress) data = rlp.encode(dataList) transactionDict = { "from": fromAddress, "to": toAddress, "gasPrice": "0x8250de00", "gas": "0x1fffff", # rule of thumb / guess work "nonce": myNonce, "data": data, } signedTransactionDict = self.eth.account.signTransaction( transactionDict, privateKey) return signedTransactionDict.rawTransaction def contract_deploy(self, txType, bytecode, abi, fromAddress): """ 非签名合约部署 :param txType:取值类型 0-主币转账交易 1-合约发布 2-合约调用 3-投票 4-权限 :param bytecode:合约bin(wasm文件),二进制数组 :param abi:abi(json文件),二进制数组 :param fromAddress:钱包地址 :return:合约部署transactionHash """ txType = platon_encoder.encode_type(txType) bytecode = bytecode abi = abi rlpList = [txType, bytecode, abi] data = rlp.encode(rlpList) transactionHash = self.eth.sendTransaction({ "from": fromAddress, "gas": "0x1fffff", "gasPrice": "0x8250de00", "data": data, }) transactionHash = HexBytes(transactionHash).hex().lower() print(transactionHash) return transactionHash def contract_transaction(self, fromAddress, contractAddress, dataList): """ 非签名合约交易 :param fromAddress: 钱包地址 :param contractAddress: 合约地址 :param dataList: 参数list :return:合约交易transactionHash """ data = rlp.encode(dataList) transactionHash = self.eth.sendTransaction({ "from": fromAddress, "to": contractAddress, "gas": "0x1fffff", "gasPrice": "0x8250de00", "data": data, }) transactionHash = HexBytes(transactionHash).hex().lower() print(transactionHash) return transactionHash def contract_call(self, fromAddress, contractAddress, dataList): """ 合约查询交易 :param fromAddress:钱包地址 :param contractAddress:合约地址 :param dataList:参数list :return: """ data = rlp.encode(dataList) recive = self.eth.call({ "from": fromAddress, "to": contractAddress, "data": data }) # recive = HexBytes(recive).decode() print(recive) return recive def signed_contract_deploy(self, txType, bytecode, abi, fromAddress, privateKey): """ 签名部署合约 :param txType:取值类型 0-主币转账交易 1-合约发布 2-合约调用 3-投票 4-权限 :param bytecode:bytecode,二进制数组 :param abi:abi,二进制数组 :param fromAddress:钱包地址 :param privateKey:钱包私钥 :return:transactionHash """ txType = platon_encoder.encode_type(txType) bytecode = bytecode abi = abi rlpList = [txType, bytecode, abi] signedData = self.get_signed_data(rlpList, fromAddress, "", privateKey) transactionHash = self.eth.sendRawTransaction(signedData) transactionHash = HexBytes(transactionHash).hex().lower() print(transactionHash) return transactionHash def signed_contract_transaction(self, fromAddress, contractAddress, dataList, privateKey): """ 签名合约交易 :param fromAddress:钱包地址 :param contractAddress:合约地址 :param dataList:参数list :param privateKey:钱包私钥 :return:transactionHash """ signedData = self.get_signed_data(fromAddress, contractAddress, dataList, privateKey) transactionHash = self.eth.sendRawTransaction(signedData) transactionHash = HexBytes(transactionHash).hex().lower() print(transactionHash) return transactionHash
class PlatonDpos: def __init__(self, url, address, pwd, abi=r"./data/dpos/candidateConstract.json", vote_abi=r'./data/dpos/ticketContract.json', privatekey=conf.PRIVATE_KEY): self.web3 = connect_web3(url) if not self.web3.isConnected(): raise Exception("节点连接失败") self.eth = Eth(self.web3) self.address = address self.abi = abi self.vote_abi = vote_abi self.value = 100 self.privatekey = privatekey def get_result(self, tx_hash, func_name, abi=None): result = self.eth.waitForTransactionReceipt(tx_hash) """查看eventData""" topics = result['logs'][0]['topics'] data = result['logs'][0]['data'] if abi is None: event = Event(json.load(open(self.abi))) else: event = Event(json.load(open(abi))) event_data = event.event_data(topics, data) return event_data, func_name def send_raw_transaction(self, data, from_address, to_address, value): nonce = self.eth.getTransactionCount(from_address) if value > 0: transaction_dict = { "to": to_address, "gasPrice": "0x8250de00", "gas": "0x6fffffff", "nonce": nonce, "data": data, "value": self.web3.toWei(value, "ether"), } else: transaction_dict = { "to": to_address, "gasPrice": "0x8250de00", "gas": "0x6fffffff", "nonce": nonce, "data": data, } signedTransactionDict = self.eth.account.signTransaction( transaction_dict, self.privatekey) data = signedTransactionDict.rawTransaction result = HexBytes(self.eth.sendRawTransaction(data)).hex() return result def CandidateDeposit(self, nodeid, owner, fee, host, port, extra, value=None): ''' @Description: 节点候选人申请/增加质押,质押金额为交易的value值。 @param nodeId: [64]byte 节点ID(公钥) owner: [20]byte 质押金退款地址 fee: uint32 出块奖励佣金比,以10000为基数(eg:5%,则fee=500) host: string 节点IP port: string 节点P2P端口号 Extra: string 附加数据(有长度限制,限制值待定) @return: 出参(事件:CandidateDepositEvent): Ret: bool 操作结果 ErrMsg: string 错误信息 ''' data = rlp.encode([ int(1001).to_bytes(8, 'big'), self.CandidateDeposit.__name__, nodeid, owner, fee, host, str(port), extra ]) if not value: value = self.value to = "0x1000000000000000000000000000000000000001" result = self.send_raw_transaction(data, self.address, to, value) # print('!!!!!', result) return self.get_result(result, self.CandidateDeposit.__name__) def CandidateApplyWithdraw(self, nodeid, withdraw): ''' @Description: 节点质押金退回申请,申请成功后节点将被重新排序,权限校验from==owner @param nodeId: [64]byte 节点ID(公钥) withdraw: uint256 退款金额 (单位:ADP) @return: 出参(事件:CandidateApplyWithdrawEvent): Ret: bool 操作结果 ErrMsg: string 错误信息 ''' # withdraw = np.uint256(withdraw) data = rlp.encode([ int(1002).to_bytes(8, 'big'), self.CandidateApplyWithdraw.__name__, nodeid, self.web3.toWei(withdraw, 'ether') ]) to = "0x1000000000000000000000000000000000000001" result = self.send_raw_transaction(data, self.address, to, value=0) # print('申请退回hx:', result) return self.get_result(result, self.CandidateApplyWithdraw.__name__) def GetCandidateWithdrawInfos(self, nodeid): ''' @Description: 获取节点申请的退款记录列表 @param {type} nodeId: [64]byte 节点ID(公钥) @return: Ret: bool 操作结果 ErrMsg: string 错误信息 []:列表 'Balance': uint256 退款金额 (单位:ADP) LockNumber: uint256 退款申请所在块高 LockBlockCycle: uint256 退款金额锁定周期 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetCandidateWithdrawInfos.__name__, nodeid ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000001", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r'{.*}') recive = re.findall(p, recive)[0] recive = json.loads(recive) return recive def CandidateWithdraw(self, nodeid): ''' @Description: 节点质押金提取,调用成功后会提取所有已申请退回的质押金到owner账户。 @param: nodeId: [64]byte 节点ID(公钥) @return: 出参(事件:CandidateWithdrawEvent): Ret: bool 操作结果 ErrMsg: string 错误信息 ''' data = HexBytes( rlp.encode([ int(1003).to_bytes(8, 'big'), self.CandidateWithdraw.__name__, nodeid ])).hex() to = "0x1000000000000000000000000000000000000001" result = self.send_raw_transaction(data, self.address, to, value=0) # print('退回hx:', result) return self.get_result(result, self.CandidateWithdraw.__name__) def SetCandidateExtra(self): ''' @Description: 设置节点附加信息,供前端扩展使用 @param: nodeId: [64]byte 节点ID(公钥) extra: string 附加信息 @return: 出参(事件:SetCandidateExtraEvent): Ret: bool 操作结果 ErrMsg: string 错误信息 ''' pass def GetCandidateDetails(self, nodeid): ''' @Description: 获取候选人信息。 @param {type} nodeId: [64]byte 节点ID(公钥) @return: Deposit: uint256 质押金额 (单位:ADP) BlockNumber: uint256 质押金更新的最新块高 Owner: [20]byte 质押金退款地址 TxIndex: uint32 所在区块交易索引 CandidateId: [64]byte 节点Id(公钥) From: [20]byte 最新质押交易的发送方 Fee: uint64 出块奖励佣金比,以10000为基数(eg:5%,则fee=500) Host: string 节点IP Port: string 节点P2P端口号 Extra: string 附加数据(有长度限制,限制值待定) ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetCandidateDetails.__name__, nodeid ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000001", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r'{.*}') recive = re.findall(p, recive)[0] recive = re.sub("{{", "{", recive) recive = json.loads(recive) return recive def GetCandidateList(self): ''' @Description: 获取所有入围节点的信息列表 @param {type} @@@@ @return: Ret: bool 操作结果 ErrMsg: string 错误信息 []:列表 Deposit: uint256 质押金额 (单位:ADP) BlockNumber: uint256 质押金更新的最新块高 Owner: [20]byte 质押金退款地址 TxIndex: uint32 所在区块交易索引 CandidateId: [64]byte 节点Id(公钥) From: [20]byte 最新质押交易的发送方 Fee: uint64 出块奖励佣金比,以10000为基数(eg:5%,则fee=500) Host: string 节点IP Port: string 节点P2P端口号 Extra: string 附加数据(有长度限制,限制值待定) ''' data = rlp.encode( [int(10).to_bytes(8, 'big'), self.GetCandidateList.__name__]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000001", "data": data }) recive = str(recive, encoding="ISO-8859-1") recive = re.sub("{\[", "[", recive) p = re.compile(r'{.*?}') recive = re.findall(p, recive) try: recive = [json.loads(i) for i in recive] except Exception as e: raise e return recive def GetVerifiersList(self): ''' @Description: 获取参与当前共识的验证人列表 @param {type} @@@@ @return: Ret: bool 操作结果 ErrMsg: string 错误信息 []:列表 Deposit: uint256 质押金额 (单位:ADP) BlockNumber: uint256 质押金更新的最新块高 Owner: [20]byte 质押金退款地址 TxIndex: uint32 所在区块交易索引 CandidateId: [64]byte 节点Id(公钥) From: [20]byte 最新质押交易的发送方 Fee: uint64 出块奖励佣金比,以10000为基数(eg:5%,则fee=500) Host: string 节点IP Port: string 节点P2P端口号 Extra: string 附加数据(有长度限制,限制值待定) ''' data = rlp.encode( [int(10).to_bytes(8, 'big'), self.GetVerifiersList.__name__]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000001", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r'{.*?}') recive = re.findall(p, recive) recive = [json.loads(i) for i in recive] return recive def VoteTicket(self, count, price, nodeid, voter, value=None): ''' 购买选票,投票给候选人 :param count: uint64 购票数量 :param price:*big.Int 选票单价 :param nodeid:[64]byte 候选人节点Id :param value: 发送交易的value为 购票数量 * 选票单价 :return: 出参(事件:VoteTicketEvent): Ret: bool 操作结果 Data: string 返回数据(成功选票的数量) ErrMsg: string 错误信息 ''' data = rlp.encode([ int(1000).to_bytes(8, 'big'), self.VoteTicket.__name__, int(count).to_bytes(4, 'big'), int(self.web3.toWei(price, 'ether')).to_bytes(8, 'big'), nodeid ]) if not value: value = self.value to = "0x1000000000000000000000000000000000000002" result_hex = self.send_raw_transaction(data, voter, to, value) result, _ = self.get_result(result_hex, self.VoteTicket.__name__, abi=self.vote_abi) return result, result_hex def GetTicketPrice(self): ''' 获取当前的票价 :return: ret: *big.Int 当前票价 error: string 错误信息 ''' data = rlp.encode( [int(10).to_bytes(8, 'big'), self.GetTicketPrice.__name__]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = HexBytes(recive).decode('utf-8') partern = re.compile(r'\d') recive = ''.join(re.findall(partern, recive)) return int(recive) def GetCandidateTicketIds(self, nodeid): ''' 获取指定候选人的选票Id的列表 :param nodeid: [64]byte 节点Id :return: ret: []ticketId 选票的Id列表 error: string 错误信息 ''' data = HexBytes( rlp.encode([ int(10).to_bytes(8, 'big'), self.GetCandidateTicketIds.__name__, nodeid ])).hex() recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile('\"(.+?)\"') recive = re.findall(p, recive) return recive def GetBatchCandidateTicketIds(self, node_ids): ''' 批量获取指定候选人的选票Id的列表 :param node_ids: []nodeId 节点Id列表 :return: ret: []ticketIds 多个节点的选票的Id列表 error: string 错误信息 ''' encode_list = [ int(10).to_bytes(8, 'big'), self.GetBatchCandidateTicketIds.__name__, ':'.join(node_ids) ] data = rlp.encode(encode_list) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r"{.*?}") recive = re.findall(p, recive) recive = [json.loads(i) for i in recive] return recive def GetTicketDetail(self, ticket_id): ''' 获取票详情 :param ticket_id:[32]byte 票Id :return: ret: Ticket 选票信息 error: string 错误信息 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetTicketDetail.__name__, ticket_id ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r"{.*?}") recive = re.findall(p, recive) recive = [json.loads(i) for i in recive] return recive def GetBatchTicketDetail(self, ticket_ids): ''' 批量获取票详情 :param ticket_ids:[]ticketId 票Id列表 :return: ret: []Ticket 选票信息列表 error: string 错误信息 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetBatchTicketDetail.__name__, ':'.join(ticket_ids) ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") p = re.compile(r"{.*?}") recive = re.findall(p, recive) recive = [json.loads(i) for i in recive] return recive def GetCandidateEpoch(self, nodeid): ''' 获取指定候选人的票龄 :param nodeid:[64]byte 节点Id :return: ret: uint64 票龄 error: string 错误信息 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetCandidateEpoch.__name__, nodeid ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") partern = re.compile(r'\d') recive = ''.join(re.findall(partern, recive)) return int(recive) def GetTicketCountByTxHash(self, txHashs): ''' (批量)获取交易的有效选票数量 :param txHashs: string 多个txHashs通过":"拼接的字符串 :return: ret: dict(nodeId)uint32 多个节点的有效选票数量 error: string 错误信息 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetTicketCountByTxHash.__name__, txHashs ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") partern = re.compile(r'{.+?}') recive = re.findall(partern, recive)[0] recive = eval(recive) return recive def GetCandidateTicketCount(self, nodeIds): ''' (批量)获取指定候选人的有效选票数量 :param nodeIds: string 多个nodeId通过":"拼接的字符串 :return: ret: dict(txHash)uint32 多个交易的有效选票数量 error: string 错误信息 ''' data = rlp.encode([ int(10).to_bytes(8, 'big'), self.GetCandidateTicketCount.__name__, nodeIds ]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") partern = re.compile(r'{.+?}') recive = re.findall(partern, recive)[0] recive = eval(recive) return recive def GetPoolRemainder(self): ''' 获取票池剩余票数量 :return: ret: uint64 剩余票数量 error: string 错误信息 ''' data = rlp.encode( [int(10).to_bytes(8, 'big'), self.GetPoolRemainder.__name__]) recive = self.eth.call({ "from": self.address, "to": "0x1000000000000000000000000000000000000002", "data": data }) recive = str(recive, encoding="ISO-8859-1") partern = re.compile(r'\d') recive = ''.join(re.findall(partern, recive)) return int(recive)
class PlatonContractTransaction: """ 合约创建参数list结构:[txType,byteCode,abi] 合约调用参数list结构:[txType,funcName,params01,params02,...] """ def __init__(self, URL): self.URL = URL self.w3 = connect_web3(URL) self.eth = Eth(self.w3) self.personal = Personal(self.w3) for i in range(5): if self.w3.isConnected(): break else: self.w3 = connect_web3(URL) time.sleep(1) else: raise Exception("节点连接失败") def reconnect(self): self.w3 = connect_web3(self.URL) self.eth = Eth(self.w3) self.personal = Personal(self.w3) def get_signed_data(self, fromAddress, toAddress, dataList, privateKey): myNonce = self.eth.getTransactionCount(fromAddress) data = rlp.encode(dataList) transactionDict = { "from": fromAddress, "to": toAddress, "gasPrice": "0x8250de00", "gas": "0x6fffffff", # rule of thumb / guess work "nonce": myNonce, "data": data, } signedTransactionDict = self.eth.account.signTransaction( transactionDict, privateKey) return signedTransactionDict.rawTransaction def contract_deploy(self, bytecode, abi, fromAddress): """ 非签名合约部署 :param txType:取值类型 0-主币转账交易 1-合约发布 2-合约调用 3-投票 4-权限 :param bytecode:合约bin(wasm文件),二进制数组 :param abi:abi(json文件),二进制数组 :param fromAddress:钱包地址 :return:合约部署transactionHash """ txType = encoder.encode_type(1) bytecode = bytecode abi = abi rlpList = [txType, bytecode, abi] data = rlp.encode(rlpList) transactionHash = self.eth.sendTransaction({ "from": fromAddress, "gas": "0x6fffffff", "gasPrice": "0x8250de00", "data": data, }) transactionHash = HexBytes(transactionHash).hex().lower() return transactionHash def contract_transaction(self, fromAddress, contractAddress, dataList): """ 非签名合约交易 :param fromAddress: 钱包地址 :param contractAddress: 合约地址 :param dataList: 参数list :return:合约交易transactionHash """ data = rlp.encode(dataList) transactionHash = self.eth.sendTransaction({ "from": fromAddress, "to": contractAddress, "gas": "0x6fffffff", "gasPrice": "0x8250de00", "data": data, }) transactionHash = HexBytes(transactionHash).hex().lower() return transactionHash def contract_call(self, fromAddress, contractAddress, dataList): """ 合约查询交易 :param fromAddress:钱包地址 :param contractAddress:合约地址 :param dataList:参数list :return: """ data = rlp.encode(dataList) recive = self.eth.call({ "from": fromAddress, "to": contractAddress, "data": data }) return recive def signed_contract_deploy(self, bytecode, abi, fromAddress, privateKey): """ 签名部署合约 :param txType:取值类型 0-主币转账交易 1-合约发布 2-合约调用 3-投票 4-权限 :param bytecode:bytecode,二进制数组 :param abi:abi,二进制数组 :param fromAddress:钱包地址 :param privateKey:钱包私钥 :return:transactionHash """ txType = encoder.encode_type(1) bytecode = bytecode abi = abi rlpList = [txType, bytecode, abi] signedData = self.get_signed_data(fromAddress, "", rlpList, privateKey) transactionHash = self.eth.sendRawTransaction(signedData) transactionHash = HexBytes(transactionHash).hex().lower() return transactionHash def signed_contract_transaction(self, fromAddress, contractAddress, dataList, privateKey): """ 签名合约交易 :param fromAddress:钱包地址 :param contractAddress:合约地址 :param dataList:参数list :param privateKey:钱包私钥 :return:transactionHash """ signedData = self.get_signed_data(fromAddress, contractAddress, dataList, privateKey) transactionHash = self.eth.sendRawTransaction(signedData) transactionHash = HexBytes(transactionHash).hex().lower() return transactionHash def contract_call_string(self, fromAddress, contractAddress, dataList): result = self.contract_call(fromAddress, contractAddress, dataList) length = int(result[66:130], 16) * 2 data = result[130:130 + length] ret = HexBytes(data).decode() return ret.lower()