diff --git a/Dockerfile b/Dockerfile index 6fecbdd..eb87d36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ WORKDIR /usr/src/app # Install Linux dependencies RUN apt-get update && apt-get install -y libssl-dev -COPY client_sdk_python ./web3/ +COPY client_sdk_python ./client_sdk_python/ COPY tests ./tests/ COPY ens ./ens/ diff --git a/README.md b/README.md index 120bf89..581e638 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Read more in the [documentation on ReadTheDocs](http://web3py.readthedocs.io/). ```python -from web3 import Web3, HTTPProvider -from web3.eth import PlatON +from client_sdk_python import Web3, HTTPProvider +from client_sdk_python.eth import PlatON from hexbytes import HexBytes # get blockNumber diff --git a/client_sdk_python/__init__.py b/client_sdk_python/__init__.py index 6cc61c4..9869999 100644 --- a/client_sdk_python/__init__.py +++ b/client_sdk_python/__init__.py @@ -4,7 +4,7 @@ if sys.version_info < (3, 5): raise EnvironmentError("Python 3.5 or above is required") -from eth_account import Account # noqa: E402 +from platon_account import Account # noqa: E402 from client_sdk_python.main import Web3 # noqa: E402 from client_sdk_python.providers.rpc import ( # noqa: E402 HTTPProvider, diff --git a/client_sdk_python/debug.py b/client_sdk_python/debug.py index ed6cf1e..33ccc1c 100644 --- a/client_sdk_python/debug.py +++ b/client_sdk_python/debug.py @@ -1,14 +1,29 @@ from client_sdk_python.module import ( Module, ) +import json +import rlp +from hexbytes import HexBytes +from client_sdk_python.utils.transactions import send_obj_transaction class Debug(Module): + + need_analyze = True + def economicConfig(self): - return self.web3.manager.request_blocking("debug_economicConfig", []) + return json.loads(self.web3.manager.request_blocking("debug_economicConfig", [])) + + def setValidatorList(self, node_list, pri_key, transaction_cfg={"gas": 210000}): + data_list = [] + for node_id in node_list: + data_list.append(bytes.fromhex(node_id)) + data = HexBytes(rlp.encode([rlp.encode(int(1900)), rlp.encode(data_list)])).hex() + return send_obj_transaction(self, data, self.web3.stakingAddress, pri_key, transaction_cfg) - def getBuildMsg(self): - return self.web3.manager.request_blocking("debug_getBuildMsg", []) + def getWaitSlashingNodeList(self): + result = self.web3.manager.request_blocking("debug_getWaitSlashingNodeList", []) + if not result: + return [] + return json.loads(result) - def getReceiveMsg(self): - return self.web3.manager.request_blocking("debug_getReceiveMsg", []) \ No newline at end of file diff --git a/client_sdk_python/eth.py b/client_sdk_python/eth.py index 12cc823..f48735e 100644 --- a/client_sdk_python/eth.py +++ b/client_sdk_python/eth.py @@ -1,7 +1,11 @@ import json -from eth_account import ( +import sha3 +import rlp +from eth_utils.hexadecimal import remove_0x_prefix +from platon_account import ( Account, ) +from platon_account.internal.transactions import bech32_address_bytes from eth_utils import ( apply_to_return_value, is_checksum_address, @@ -51,6 +55,9 @@ wait_for_transaction_receipt, ) +from platon_account.internal.signing import ( + to_standard_signature_bytes, +) class Eth(Module): account = Account() @@ -109,8 +116,7 @@ def evidences(self): @property def consensusStatus(self): - data = self.web3.manager.request_blocking("platon_consensusStatus", []) - return json.loads(data) + return self.web3.manager.request_blocking("platon_consensusStatus", []) def getPrepareQC(self, block_number): return self.web3.manager.request_blocking("platon_getPrepareQC", [block_number]) @@ -417,6 +423,31 @@ def analyzeReceiptByHash(self, tx_hash): def analyzeReceipt(self, transaction_receipt): return self.web3.analyzeReceipt(transaction_receipt) + def ecrecover(self, block_identifier): + block = self.getBlock(block_identifier) + extra = block.proofOfAuthorityData[0:32] + sign = block.proofOfAuthorityData[32:] + miner = bech32_address_bytes(remove_0x_prefix(block.miner)) + raw_data = [bytes.fromhex(remove_0x_prefix(block.parentHash.hex())), + miner, + bytes.fromhex(remove_0x_prefix(block.stateRoot.hex())), + bytes.fromhex(remove_0x_prefix(block.transactionsRoot.hex())), + bytes.fromhex(remove_0x_prefix(block.receiptsRoot.hex())), + bytes.fromhex(remove_0x_prefix(block.logsBloom.hex())), + block.number, + block.gasLimit, + block.gasUsed, + block.timestamp, + extra, + bytes.fromhex(remove_0x_prefix(block.nonce)) + ] + message_hash = sha3.keccak_256(rlp.encode(raw_data)).digest() + hash_bytes = HexBytes(message_hash) + signature_bytes = HexBytes(sign) + signature_bytes_standard = to_standard_signature_bytes(signature_bytes) + signature_obj = self.account._keys.Signature(signature_bytes=signature_bytes_standard) + return remove_0x_prefix(signature_obj.recover_public_key_from_msg_hash(hash_bytes).to_hex()) + class PlatON(Eth): pass diff --git a/client_sdk_python/main.py b/client_sdk_python/main.py index fa30e0f..8eadcb5 100644 --- a/client_sdk_python/main.py +++ b/client_sdk_python/main.py @@ -3,12 +3,13 @@ add_0x_prefix, from_wei, is_address, - is_checksum_address, + # is_checksum_address, keccak, remove_0x_prefix, - to_checksum_address, + # to_checksum_address, to_wei, ) +from platon_keys.utils.address import MIANNETHRP, TESTNETHRP from ens import ENS @@ -87,6 +88,23 @@ def get_default_modules(): } +def default_address(mainnet, testnet): + return {MIANNETHRP: mainnet, TESTNETHRP: testnet} + + +restricting = default_address("lat1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp7pn3ep","lax1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqp3yp7hw") +staking = default_address("lat1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzsjx8h7", "lax1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzlh5ge3") +penalty = default_address("lat1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyva9ztf", "lax1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyrchd9x") +pipAddr = default_address("lat1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq93t3hkm", "lax1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq97wrcc5") +delegateReward = default_address("lat1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxlcypcy", "lax1zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxsakwkt") + + +def to_checksum_address(val): + return val + +def is_checksum_address(val): + return True + class Web3: # Providers HTTPProvider = HTTPProvider @@ -118,13 +136,7 @@ class Web3: isChecksumAddress = staticmethod(is_checksum_address) toChecksumAddress = staticmethod(to_checksum_address) - # platon contract address - restrictingAddress = "0x1000000000000000000000000000000000000001" - stakingAddress = "0x1000000000000000000000000000000000000002" - penaltyAddress = "0x1000000000000000000000000000000000000004" - pipAddress = "0x1000000000000000000000000000000000000005" - - def __init__(self, providers=empty, middlewares=None, modules=None, ens=empty, chain_id=101): + def __init__(self, providers=empty, middlewares=None, modules=None, ens=empty, chain_id=100): self.manager = RequestManager(self, providers, middlewares) if modules is None: @@ -133,17 +145,29 @@ def __init__(self, providers=empty, middlewares=None, modules=None, ens=empty, c for module_name, module_class in modules.items(): module_class.attach(self, module_name) - self.ens = ens + if chain_id == 100: + self.net_type = MIANNETHRP + else: + self.net_type = TESTNETHRP + # platon contract address + self.restrictingAddress = restricting[self.net_type] + self.stakingAddress = staking[self.net_type] + self.penaltyAddress = penalty[self.net_type] + self.pipAddress = pipAddr[self.net_type] + self.delegateRewardAddress = delegateReward[self.net_type] - self.chain_id = chain_id + self.ens = ens - def setChainId(self, chain_id): self.chain_id = chain_id @property def chainId(self): return self.chain_id + @chainId.setter + def chainId(self, chain_id): + self.chain_id = chain_id + @property def middleware_stack(self): return self.manager.middleware_stack @@ -215,3 +239,8 @@ def ens(self): @ens.setter def ens(self, new_ens): self._ens = new_ens + + def pubkey_to_address(self, pubkey): + addr_dict = {MIANNETHRP: pubkey.to_bech32_address(), + TESTNETHRP: pubkey.to_bech32_test_address()} + return addr_dict[self.net_type] diff --git a/client_sdk_python/middleware/names.py b/client_sdk_python/middleware/names.py index be88f94..52f5ebb 100644 --- a/client_sdk_python/middleware/names.py +++ b/client_sdk_python/middleware/names.py @@ -16,5 +16,5 @@ def name_to_address_middleware(w3): abi_ens_resolver(w3), ] return construct_formatting_middleware( - request_formatters=abi_request_formatters(normalizers, RPC_ABIS) + # request_formatters=abi_request_formatters(normalizers, RPC_ABIS) ) diff --git a/client_sdk_python/middleware/pythonic.py b/client_sdk_python/middleware/pythonic.py index 492c9f0..dba5348 100644 --- a/client_sdk_python/middleware/pythonic.py +++ b/client_sdk_python/middleware/pythonic.py @@ -10,7 +10,7 @@ is_string, remove_0x_prefix, text_if_str, - to_checksum_address, + # to_checksum_address, ) from hexbytes import ( HexBytes, @@ -63,6 +63,10 @@ def bytes_to_ascii(value): is_not_null = complement(is_null) +def to_checksum_address(val): + return val + + @curry def to_hexbytes(num_bytes, val, variable_length=False): if isinstance(val, (str, int, bytes)): diff --git a/client_sdk_python/middleware/signing.py b/client_sdk_python/middleware/signing.py index 86d6c5e..48e9ef8 100644 --- a/client_sdk_python/middleware/signing.py +++ b/client_sdk_python/middleware/signing.py @@ -3,13 +3,13 @@ ) import operator -from eth_account import ( +from platon_account import ( Account, ) -from eth_account.local import ( +from platon_account.local import ( LocalAccount, ) -from eth_keys.datatypes import ( +from platon_keys.datatypes import ( PrivateKey, ) from eth_utils import ( @@ -63,7 +63,7 @@ def gen_normalized_accounts(val): def to_account(val): raise TypeError( "key must be one of the types: " - "eth_keys.datatype.PrivateKey, eth_account.local.LocalAccount, " + "platon_keys.datatype.PrivateKey, platon_account.local.LocalAccount, " "or raw private key as a hex string or byte string. " "Was of type {0}".format(type(val))) @@ -99,8 +99,8 @@ def construct_sign_and_send_raw_middleware(private_key_or_account): Keyword arguments: private_key_or_account -- A single private key or a tuple, list or set of private keys. Keys can be any of the following formats: - - An eth_account.LocalAccount object - - An eth_keys.PrivateKey object + - An platon_account.LocalAccount object + - An platon_keys.PrivateKey object - A raw private key as a hex string or byte string """ diff --git a/client_sdk_python/pip.py b/client_sdk_python/pip.py index 121dfa9..2c5fb55 100644 --- a/client_sdk_python/pip.py +++ b/client_sdk_python/pip.py @@ -66,16 +66,14 @@ def submitVersion(self, verifier, pip_id, new_version, end_voting_rounds, pri_ke rlp.encode(int(new_version)), rlp.encode(int(end_voting_rounds))]) return send_obj_transaction(self, data, self.web3.pipAddress, pri_key, transaction_cfg) - def submitParam(self, verifier, url, end_voting_block, param_name, current_value, new_value, - pri_key, transaction_cfg=None): + def submitParam(self, verifier, pip_id, module, name, new_value, pri_key, transaction_cfg=None): """ - todo fill - :param verifier: - :param url: - :param end_voting_block: - :param param_name: - :param current_value: - :param new_value: + Submit an param proposal + :param verifier: The certified submitting the proposal + :param pip_id: PIPID + :param module: parameter module + :param name: parameter name + :param new_value: New parameter value :param pri_key: Private key for transaction :param transaction_cfg: Transaction basic configuration type: dict @@ -87,8 +85,8 @@ def submitParam(self, verifier, url, end_voting_block, param_name, current_value :return: if is need analyze return transaction result dict if is not need analyze return transaction hash """ - data = rlp.encode([rlp.encode(int(2002)), rlp.encode(bytes.fromhex(verifier)), rlp.encode(url), rlp.encode(int(end_voting_block)), - rlp.encode(param_name), rlp.encode(str(current_value)), rlp.encode(str(new_value))]) + data = rlp.encode([rlp.encode(int(2002)), rlp.encode(bytes.fromhex(verifier)), rlp.encode(pip_id), rlp.encode(module), + rlp.encode(name), rlp.encode(new_value)]) return send_obj_transaction(self, data, self.web3.pipAddress, pri_key, transaction_cfg) def submitCancel(self, verifier, pip_id, end_voting_rounds, tobe_canceled_proposal_id, pri_key, transaction_cfg=None): @@ -232,16 +230,26 @@ def getActiveVersion(self, from_address=None): data = rlp.encode([rlp.encode(int(2103))]) return parse_data(call_obj(self, from_address, self.web3.pipAddress, data)) - # def getProgramVersion(self, from_address=None): - # data = rlp.encode([rlp.encode(int(2104))]) - # return parse_data(call_obj(self, from_address, self.web3.pipAddress, data)) + def getGovernParamValue(self, module, name, from_address=None): + """ + Query the current block height governance parameter value + :param module: Parameter module + :param name: parameter name + :param from_address: + :return: + """ + data = rlp.encode([rlp.encode(int(2104)), rlp.encode(module), rlp.encode(name)]) + return parse_data(call_obj(self, from_address, self.web3.pipAddress, data)) - def listParam(self, from_address=None): + def listGovernParam(self, module=None, from_address=None): """ - todo fill + Query governance parameter list + :param module :param from_address: Used to call the rpc call method :return: todo fill """ - data = rlp.encode([rlp.encode(int(2105))]) + if module is None: + module = "" + data = rlp.encode([rlp.encode(int(2106)), rlp.encode(module)]) return parse_data(call_obj(self, from_address, self.web3.pipAddress, data)) diff --git a/client_sdk_python/ppos.py b/client_sdk_python/ppos.py index 111c033..bd05497 100644 --- a/client_sdk_python/ppos.py +++ b/client_sdk_python/ppos.py @@ -5,8 +5,10 @@ from client_sdk_python.module import ( Module, ) +from eth_utils.hexadecimal import remove_0x_prefix from client_sdk_python.utils.encoding import parse_str from client_sdk_python.utils.transactions import send_obj_transaction, call_obj +from platon_account.internal.transactions import bech32_address_bytes class Ppos(Module): @@ -15,7 +17,7 @@ class Ppos(Module): need_analyze = True def createStaking(self, typ, benifit_address, node_id, external_id, node_name, website, details, amount, - program_version, program_version_sign, bls_pubkey, bls_proof, pri_key, transaction_cfg=None): + program_version, program_version_sign, bls_pubkey, bls_proof, pri_key, reward_per, transaction_cfg=None): """ Initiate Staking :param typ: Indicates whether the account free amount or the account's lock amount is used for staking, 0: free amount; 1: lock amount @@ -31,6 +33,7 @@ def createStaking(self, typ, benifit_address, node_id, external_id, node_name, w :param bls_pubkey: Bls public key :param bls_proof: Proof of bls, obtained by pulling the proof interface, admin_getSchnorrNIZKProve :param pri_key: Private key for transaction + :param reward_per: Proportion of the reward share obtained from the commission, using BasePoint 1BP = 0.01% :param transaction_cfg: Transaction basic configuration type: dict example:cfg = { @@ -41,18 +44,18 @@ def createStaking(self, typ, benifit_address, node_id, external_id, node_name, w :return: if is need analyze return transaction result dict if is not need analyze return transaction hash """ - if benifit_address[:2] == '0x': - benifit_address = benifit_address[2:] + benifit_address = bech32_address_bytes(benifit_address) if program_version_sign[:2] == '0x': program_version_sign = program_version_sign[2:] - data = HexBytes(rlp.encode([rlp.encode(int(1000)), rlp.encode(typ), rlp.encode(bytes.fromhex(benifit_address)), + data = HexBytes(rlp.encode([rlp.encode(int(1000)), rlp.encode(typ), rlp.encode(benifit_address), rlp.encode(bytes.fromhex(node_id)), rlp.encode(external_id), rlp.encode(node_name), - rlp.encode(website), rlp.encode(details), rlp.encode(amount), rlp.encode(program_version), + rlp.encode(website), rlp.encode(details), + rlp.encode(amount), rlp.encode(reward_per), rlp.encode(program_version), rlp.encode(bytes.fromhex(program_version_sign)), rlp.encode(bytes.fromhex(bls_pubkey)), rlp.encode(bytes.fromhex(bls_proof))])).hex() return send_obj_transaction(self, data, self.web3.stakingAddress, pri_key, transaction_cfg) - def editCandidate(self, benifit_address, node_id, external_id, node_name, website, details, pri_key, transaction_cfg=None): + def editCandidate(self, benifit_address, node_id, external_id, node_name, website, details, pri_key, reward_per, transaction_cfg=None): """ Modify staking information :param benifit_address: Income account for accepting block rewards and staking rewards @@ -62,6 +65,7 @@ def editCandidate(self, benifit_address, node_id, external_id, node_name, websit :param website: The third-party home page of the node (with a length limit indicating the home page of the node) :param details: Description of the node (with a length limit indicating the description of the node) :param pri_key: Private key for transaction + :param reward_per: Proportion of the reward share obtained from the commission, using BasePoint 1BP = 0.01% :param transaction_cfg: Transaction basic configuration type: dict example:cfg = { @@ -72,9 +76,11 @@ def editCandidate(self, benifit_address, node_id, external_id, node_name, websit :return: if is need analyze return transaction result dict if is not need analyze return transaction hash """ - if benifit_address[:2] == '0x': - benifit_address = benifit_address[2:] - data = HexBytes(rlp.encode([rlp.encode(int(1001)), rlp.encode(bytes.fromhex(benifit_address)), rlp.encode(bytes.fromhex(node_id)), + # if benifit_address[:2] == '0x': + # benifit_address = benifit_address[2:] + benifit_address = bech32_address_bytes(benifit_address) + data = HexBytes(rlp.encode([rlp.encode(int(1001)), rlp.encode(benifit_address), rlp.encode(bytes.fromhex(node_id)), + rlp.encode(reward_per), rlp.encode(external_id), rlp.encode(node_name), rlp.encode(website), rlp.encode(details)])).hex() return send_obj_transaction(self, data, self.web3.stakingAddress, pri_key, transaction_cfg) @@ -166,8 +172,11 @@ def getVerifierList(self, from_address=None): data = rlp.encode([rlp.encode(int(1100))]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) parse = parse_str(raw_data) - for i in parse['Data']: - i["Shares"] = int(i["Shares"], 16) + try: + raw_data = parse["Ret"] + for i in raw_data: + i["Shares"] = int(i["Shares"], 16) + except:... return parse def getValidatorList(self, from_address=None): @@ -180,8 +189,11 @@ def getValidatorList(self, from_address=None): data = rlp.encode([rlp.encode(int(1101))]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) parse = parse_str(raw_data) - for i in parse['Data']: - i["Shares"] = int(i["Shares"], 16) + try: + raw_data = parse["Ret"] + for i in raw_data: + i["Shares"] = int(i["Shares"], 16) + except:... return parse def getCandidateList(self, from_address=None): @@ -194,12 +206,15 @@ def getCandidateList(self, from_address=None): data = rlp.encode([rlp.encode(int(1102))]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) parse = parse_str(raw_data) - for i in parse['Data']: - i["Shares"] = int(i["Shares"], 16) - i["Released"] = int(i["Released"], 16) - i["ReleasedHes"] = int(i["ReleasedHes"], 16) - i["RestrictingPlan"] = int(i["RestrictingPlan"], 16) - i["RestrictingPlanHes"] = int(i["RestrictingPlanHes"], 16) + try: + raw_data = parse["Ret"] + for i in raw_data: + i["Shares"] = int(i["Shares"], 16) + i["Released"] = int(i["Released"], 16) + i["ReleasedHes"] = int(i["ReleasedHes"], 16) + i["RestrictingPlan"] = int(i["RestrictingPlan"], 16) + i["RestrictingPlanHes"] = int(i["RestrictingPlanHes"], 16) + except:... return parse def getRelatedListByDelAddr(self, del_addr, from_address=None): @@ -210,9 +225,8 @@ def getRelatedListByDelAddr(self, del_addr, from_address=None): :return: todo fill """ - if del_addr[:2] == '0x': - del_addr = del_addr[2:] - data = rlp.encode([rlp.encode(int(1103)), rlp.encode(bytes.fromhex(del_addr))]) + del_addr = bech32_address_bytes(del_addr) + data = rlp.encode([rlp.encode(int(1103)), rlp.encode(del_addr)]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) return parse_str(raw_data) @@ -226,21 +240,21 @@ def getDelegateInfo(self, staking_blocknum, del_address, node_id, from_address=N :return: todo fill """ - if del_address[:2] == '0x': - del_address = del_address[2:] - data = rlp.encode([rlp.encode(int(1104)), rlp.encode(staking_blocknum), rlp.encode(bytes.fromhex(del_address)), rlp.encode(bytes.fromhex(node_id))]) + del_address = bech32_address_bytes(del_address) + data = rlp.encode([rlp.encode(int(1104)), rlp.encode(staking_blocknum), rlp.encode(del_address), rlp.encode(bytes.fromhex(node_id))]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) - parse = json.loads(str(raw_data, encoding="utf8")) - raw_data_dict = parse["Data"] - if raw_data_dict != "": - data = json.loads(raw_data_dict) - data["Released"] = int(data["Released"], 16) - data["ReleasedHes"] = int(data["ReleasedHes"], 16) - data["RestrictingPlan"] = int(data["RestrictingPlan"], 16) - data["RestrictingPlanHes"] = int(data["RestrictingPlanHes"], 16) - data["Reduction"] = int(data["Reduction"], 16) - parse["Data"] = data - return parse + receive = json.loads(str(raw_data, encoding="utf8")) + try: + raw_data_dict = receive["Ret"] + raw_data_dict["Released"] = int(raw_data_dict["Released"], 16) + raw_data_dict["ReleasedHes"] = int(raw_data_dict["ReleasedHes"], 16) + raw_data_dict["RestrictingPlan"] = int(raw_data_dict["RestrictingPlan"], 16) + raw_data_dict["RestrictingPlanHes"] = int(raw_data_dict["RestrictingPlanHes"], 16) + raw_data_dict["CumulativeIncome"] = int(raw_data_dict["CumulativeIncome"], 16) + # raw_data_dict["Reduction"] = int(raw_data_dict["Reduction"], 16) + receive["Ret"] = raw_data_dict + except:... + return receive def getCandidateInfo(self, node_id, from_address=None): """ @@ -253,14 +267,20 @@ def getCandidateInfo(self, node_id, from_address=None): data = rlp.encode([rlp.encode(int(1105)), rlp.encode(bytes.fromhex(node_id))]) raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) parse = str(raw_data, encoding="utf8").replace('\\', '').replace('"{', '{').replace('}"', '}') - raw_data_dict = json.loads(parse) - if raw_data_dict["Data"] != "": - raw_data_dict["Data"]["Shares"] = int(raw_data_dict["Data"]["Shares"], 16) - raw_data_dict["Data"]["Released"] = int(raw_data_dict["Data"]["Released"], 16) - raw_data_dict["Data"]["ReleasedHes"] = int(raw_data_dict["Data"]["ReleasedHes"], 16) - raw_data_dict["Data"]["RestrictingPlan"] = int(raw_data_dict["Data"]["RestrictingPlan"], 16) - raw_data_dict["Data"]["RestrictingPlanHes"] = int(raw_data_dict["Data"]["RestrictingPlanHes"], 16) - return raw_data_dict + receive = json.loads(parse) + try: + raw_data_dict = receive["Ret"] + raw_data_dict["Shares"] = int(raw_data_dict["Shares"], 16) + raw_data_dict["Released"] = int(raw_data_dict["Released"], 16) + raw_data_dict["ReleasedHes"] = int(raw_data_dict["ReleasedHes"], 16) + raw_data_dict["RestrictingPlan"] = int(raw_data_dict["RestrictingPlan"], 16) + raw_data_dict["RestrictingPlanHes"] = int(raw_data_dict["RestrictingPlanHes"], 16) + raw_data_dict["DelegateRewardTotal"] = int(raw_data_dict["DelegateRewardTotal"], 16) + raw_data_dict["DelegateTotal"] = int(raw_data_dict["DelegateTotal"], 16) + raw_data_dict["DelegateTotalHes"] = int(raw_data_dict["DelegateTotalHes"], 16) + receive["Ret"] = raw_data_dict + except:... + return receive def reportDuplicateSign(self, typ, data, pri_key, transaction_cfg=None): """ @@ -281,7 +301,7 @@ def reportDuplicateSign(self, typ, data, pri_key, transaction_cfg=None): data = rlp.encode([rlp.encode(int(3000)), rlp.encode(typ), rlp.encode(data)]) return send_obj_transaction(self, data, self.web3.penaltyAddress, pri_key, transaction_cfg) - def checkDuplicateSign(self, typ, check_address, block_number, from_address=None): + def checkDuplicateSign(self, typ, node_id, block_number, from_address=None): """ Check if the node has been reported too much :param typ: Represents double sign type, 1:prepareBlock, 2: prepareVote, 3:viewChange @@ -291,9 +311,7 @@ def checkDuplicateSign(self, typ, check_address, block_number, from_address=None :return: todo fill """ - if check_address[:2] == '0x': - check_address = check_address[2:] - data = rlp.encode([rlp.encode(int(3001)), rlp.encode(int(typ)), rlp.encode(bytes.fromhex(check_address)), rlp.encode(block_number)]) + data = rlp.encode([rlp.encode(int(3001)), rlp.encode(int(typ)), rlp.encode(bytes.fromhex(node_id)), rlp.encode(block_number)]) raw_data = call_obj(self, from_address, self.web3.penaltyAddress, data) receive = str(raw_data, encoding="ISO-8859-1") if receive == "": @@ -326,14 +344,15 @@ def createRestrictingPlan(self, account, plan, pri_key, transaction_cfg=None): :return: if is need analyze return transaction result dict if is not need analyze return transaction hash """ - if account[:2] == '0x': - account = account[2:] + # if account[:2] == '0x': + # account = account[2:] + account = bech32_address_bytes(account) plan_list = [] for dict_ in plan: - # v = [dict_[k] for k in dict_] - plan_list.append(dict_.values()) + v = [dict_[k] for k in dict_] + plan_list.append(v) rlp_list = rlp.encode(plan_list) - data = rlp.encode([rlp.encode(int(4000)), rlp.encode(bytes.fromhex(account)), rlp_list]) + data = rlp.encode([rlp.encode(int(4000)), rlp.encode(account), rlp_list]) return send_obj_transaction(self, data, self.web3.restrictingAddress, pri_key, transaction_cfg) def getRestrictingInfo(self, account, from_address=None): @@ -344,19 +363,63 @@ def getRestrictingInfo(self, account, from_address=None): :return: todo fill """ - if account[:2] == '0x': - account = account[2:] - data = rlp.encode([rlp.encode(int(4100)), rlp.encode(bytes.fromhex(account))]) + # if account[:2] == '0x': + # account = account[2:] + account = bech32_address_bytes(account) + data = rlp.encode([rlp.encode(int(4100)), rlp.encode(account)]) raw_data = call_obj(self, from_address, self.web3.restrictingAddress, data) receive = json.loads(str(raw_data, encoding="ISO-8859-1")) - raw_data_dict = receive["Data"] - if raw_data_dict != "": - data = json.loads(data) - data["balance"] = int(data["balance"], 16) - data["Pledge"] = int(data["Pledge"], 16) - data["debt"] = int(data["debt"], 16) - if data["plans"]: - for i in data["plans"]: + try: + raw_data_dict = receive["Ret"] + raw_data_dict["balance"] = int(raw_data_dict["balance"], 16) + raw_data_dict["Pledge"] = int(raw_data_dict["Pledge"], 16) + raw_data_dict["debt"] = int(raw_data_dict["debt"], 16) + if raw_data_dict["plans"]: + for i in raw_data_dict["plans"]: i["amount"] = int(i["amount"], 16) - receive["Data"] = data + receive["Ret"] = raw_data_dict + except:... + return receive + + def getPackageReward(self, from_address=None): + data = rlp.encode([rlp.encode(int(1200))]) + raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) + receive = json.loads(str(raw_data, encoding="ISO-8859-1")) + ret = receive["Ret"] + receive["Ret"] = int(str(ret), 16) return receive + + def getStakingReward(self, from_address=None): + data = rlp.encode([rlp.encode(int(1201))]) + raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) + receive = json.loads(str(raw_data, encoding="ISO-8859-1")) + ret = receive["Ret"] + receive["Ret"] = int(str(ret), 16) + return receive + + def getAvgPackTime(self, from_address=None): + data = rlp.encode([rlp.encode(int(1202))]) + raw_data = call_obj(self, from_address, self.web3.stakingAddress, data) + receive = json.loads(str(raw_data, encoding="ISO-8859-1")) + return receive + + def getDelegateReward(self, from_address, node_ids=[]): + node_id_bytes = [bytes.fromhex(node_id) for node_id in node_ids] + tmp_from_address = bech32_address_bytes(from_address) + data = [rlp.encode(int(5100)), rlp.encode(tmp_from_address), rlp.encode(node_id_bytes)] + data = rlp.encode(data) + raw_data = call_obj(self, from_address, self.web3.delegateRewardAddress, data) + receive = json.loads(str(raw_data, encoding="ISO-8859-1")) + try: + raw_data_dict = receive["Ret"] + result = [] + for d in raw_data_dict: + d["reward"] = int(d["reward"], 16) + result.append(d) + receive["Ret"] = result + except:... + return receive + + def withdrawDelegateReward(self, pri_key, transaction_cfg=None): + data = rlp.encode([rlp.encode(int(5000))]) + return send_obj_transaction(self, data, self.web3.delegateRewardAddress, pri_key, transaction_cfg) \ No newline at end of file diff --git a/client_sdk_python/utils/transactions.py b/client_sdk_python/utils/transactions.py index 9467b0b..9a9506d 100644 --- a/client_sdk_python/utils/transactions.py +++ b/client_sdk_python/utils/transactions.py @@ -11,7 +11,7 @@ from hexbytes import HexBytes -from eth_keys.datatypes import PrivateKey +from platon_keys.datatypes import PrivateKey VALID_TRANSACTION_PARAMS = [ 'from', @@ -34,32 +34,39 @@ def call_obj(obj, from_address, to_address, data): - to_address = obj.web3.toChecksumAddress(to_address) + # to_address = obj.web3.toChecksumAddress(to_address) if from_address is None: return obj.web3.platon.call({"to": to_address, "data": data}) - from_address = obj.web3.toChecksumAddress(from_address) + # from_address = obj.web3.toChecksumAddress(from_address) return obj.web3.platon.call({"from": from_address, "to": to_address, "data": data}) def send_obj_transaction(obj, data, to_address, pri_key, transaction_cfg: dict): + transaction_dict = {} if transaction_cfg is None: transaction_cfg = {} if transaction_cfg.get("gasPrice", None) is None: - transaction_cfg["gasPrice"] = obj.web3.platon.gasPrice + transaction_dict["gasPrice"] = obj.web3.platon.gasPrice + else: + transaction_dict["gasPrice"] = transaction_cfg["gasPrice"] if transaction_cfg.get("nonce", None) is None: - raw_from_address = PrivateKey(bytes.fromhex(pri_key)).public_key.to_address() - from_address = obj.web3.toChecksumAddress(raw_from_address) - transaction_cfg["nonce"] = obj.web3.platon.getTransactionCount(from_address) + from_address = obj.web3.pubkey_to_address(PrivateKey(bytes.fromhex(pri_key)).public_key) + transaction_dict["nonce"] = obj.web3.platon.getTransactionCount(from_address) + else: + transaction_dict["nonce"] = transaction_cfg["nonce"] if transaction_cfg.get("gas", None) is None: - transaction_data = {"to": to_address, "data": data} - transaction_cfg["gas"] = obj.web3.platon.estimateGas(transaction_data) - transaction_cfg["chainId"] = obj.web3.chainId - transaction_cfg["to"] = to_address - transaction_cfg["data"] = data + from_address = obj.web3.pubkey_to_address(PrivateKey(bytes.fromhex(pri_key)).public_key) + transaction_data = {"to": to_address, "data": data, "from": from_address} + transaction_dict["gas"] = obj.web3.platon.estimateGas(transaction_data) + else: + transaction_dict["gas"] = transaction_cfg["gas"] + transaction_dict["chainId"] = obj.web3.chainId + transaction_dict["to"] = to_address + transaction_dict["data"] = data if transaction_cfg.get("value", 0) > 0: - transaction_cfg["value"] = int(transaction_cfg.get("value", 0)) + transaction_dict["value"] = int(transaction_cfg.get("value", 0)) signed_transaction_dict = obj.web3.platon.account.signTransaction( - transaction_cfg, pri_key + transaction_dict, pri_key, net_type=obj.web3.net_type ) signed_data = signed_transaction_dict.rawTransaction tx_hash = HexBytes(obj.web3.platon.sendRawTransaction(signed_data)).hex() @@ -172,7 +179,7 @@ def assert_valid_transaction_params(transaction_params): def prepare_replacement_transaction(web3, current_transaction, new_transaction): - if current_transaction['blockHash'] != HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'): + if current_transaction['blockHash'] != HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'): raise ValueError('Supplied transaction with hash {} has already been mined' .format(current_transaction['hash'])) diff --git a/client_sdk_python/utils/validation.py b/client_sdk_python/utils/validation.py index bc3f224..6c2a39b 100644 --- a/client_sdk_python/utils/validation.py +++ b/client_sdk_python/utils/validation.py @@ -143,31 +143,32 @@ def validate_address(value): """ Helper function for validating an address """ - if is_bytes(value): - if not is_binary_address(value): - raise InvalidAddress("Address must be 20 bytes when input type is bytes", value) - return - - if not isinstance(value, str): - raise TypeError('Address {} must be provided as a string'.format(value)) - if not is_hex_address(value): - raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) - if not is_checksum_address(value): - if value == value.lower(): - raise InvalidAddress( - "Web3.py only accepts checksum addresses. " - "The software that gave you this non-checksum address should be considered unsafe, " - "please file it as a bug on their platform. " - "Try using an ENS name instead. Or, if you must accept lower safety, " - "use Web3.toChecksumAddress(lower_case_address).", - value, - ) - else: - raise InvalidAddress( - "Address has an invalid EIP-55 checksum. " - "After looking up the address from the original source, try again.", - value, - ) + # if is_bytes(value): + # if not is_binary_address(value): + # raise InvalidAddress("Address must be 20 bytes when input type is bytes", value) + # return + # + # if not isinstance(value, str): + # raise TypeError('Address {} must be provided as a string'.format(value)) + # if not is_hex_address(value): + # raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) + # if not is_checksum_address(value): + # if value == value.lower(): + # raise InvalidAddress( + # "Web3.py only accepts checksum addresses. " + # "The software that gave you this non-checksum address should be considered unsafe, " + # "please file it as a bug on their platform. " + # "Try using an ENS name instead. Or, if you must accept lower safety, " + # "use Web3.toChecksumAddress(lower_case_address).", + # value, + # ) + # else: + # raise InvalidAddress( + # "Address has an invalid EIP-55 checksum. " + # "After looking up the address from the original source, try again.", + # value, + # ) + pass def has_one_val(*args, **kwargs): diff --git a/setup.py b/setup.py index 48fba10..9dbf9da 100644 --- a/setup.py +++ b/setup.py @@ -60,25 +60,25 @@ setup( name='client_sdk_python', # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility. - version='0.3.0', + version='0.7.0', description="""PlatON Client SDK python""", # long_description_markdown_filename='README.md', - author='Piper Merriam', - author_email='pipermerriam@gmail.com', + author='Piper awake', + author_email='hietel366435@gmail.com', url='https://github.com/PlatONnetwork/client-sdk-python', include_package_data=True, install_requires=[ "toolz>=0.9.0,<1.0.0;implementation_name=='pypy'", "cytoolz>=0.9.0,<1.0.0;implementation_name=='cpython'", "eth-abi>=1.2.0,<2.0.0", - "eth-account>=0.2.1,<0.4.0", + "platon-account>=0.1.2", "eth-utils>=1.2.0,<2.0.0", "hexbytes>=0.1.0,<1.0.0", "lru-dict>=1.1.6,<2.0.0", "eth-hash[pycryptodome]>=0.2.0,<1.0.0", "requests>=2.16.0,<3.0.0", "websockets>=6.0.0,<7.0.0", - "pypiwin32>=223;platform_system=='Windows'", 'rlp' + "pypiwin32>=223;platform_system=='Windows'", 'rlp', 'pysha3' ], setup_requires=['setuptools-markdown'], python_requires='>=3.6,<4',