def validate_proof(proof: List[dict], hex_target_hash: str, hex_merkle_root: str, is_big_endian: bool = False): if is_big_endian: hex_merkle_root = NeoData.to_reserve_hex_str(hex_merkle_root) hex_target_hash = NeoData.to_reserve_hex_str(hex_target_hash) if len(proof) == 0: return hex_target_hash == hex_merkle_root else: hex_proof_hash = hex_target_hash for node in proof: if is_big_endian: sibling = NeoData.to_reserve_hex_str(node['TargetHash']) else: sibling = node['TargetHash'] try: direction = node['Direction'].lower() except KeyError: raise SDKException(ErrorCode.other_error('Invalid proof')) if direction == 'left': value = bytes.fromhex('01' + sibling + hex_proof_hash) hex_proof_hash = Digest.sha256(value, is_hex=True) elif direction == 'right': value = bytes.fromhex('01' + hex_proof_hash + sibling) hex_proof_hash = Digest.sha256(value, is_hex=True) else: raise SDKException(ErrorCode.other_error('Invalid proof.')) return hex_proof_hash == hex_merkle_root
def new_transfer_multi_tx(self, transfer_list: list, payer: Union[str, bytes, Address], gas_price: int, gas_limit: int) -> InvokeTransaction: """ This interface is used to generate a transaction which can transfer amount of token from from-account to to-account multiple times. """ func = NeoInvokeFunction('transferMulti') for index, item in enumerate(transfer_list): if not isinstance(item[2], int): raise SDKException( ErrorCode.param_err( 'the data type of value should be int.')) if item[2] < 0: raise SDKException( ErrorCode.param_err( 'the value should be equal or great than 0.')) transfer_list[index] = [ Address.b58decode(item[0]), Address.b58decode(item[1]), item[2] ] for item in transfer_list: func.add_params_value(item) params = InvokeTransaction.generate_neo_vm_invoke_code( self._contract_address, func) tx = InvokeTransaction(payer, gas_price, gas_limit, params) return tx
async def send_neo_vm_transaction(self, contract_address: str or bytes or bytearray, signer: Account or None, payer: Account or None, gas_price: int, gas_limit: int, func: AbiFunction or NeoInvokeFunction, is_full: bool = False): if isinstance(func, AbiFunction): params = BuildParams.serialize_abi_function(func) elif isinstance(func, NeoInvokeFunction): params = func.create_invoke_code() else: raise SDKException( ErrorCode.other_error('the type of func is error.')) contract_address = ensure_bytearray_contract_address(contract_address) params.append(0x67) for i in contract_address: params.append(i) if payer is None: raise SDKException(ErrorCode.param_err('payer account is None.')) tx = Transaction(0, 0xd1, gas_price, gas_limit, payer.get_address_bytes(), params) tx.sign_transaction(payer) if isinstance( signer, Account ) and signer.get_address_base58() != payer.get_address_base58(): tx.add_sign_transaction(signer) return await self.send_raw_transaction(tx, is_full)
async def connect(self): try: self.__ws_client = await client.connect(self.__url) except ConnectionAbortedError as e: raise SDKException(ErrorCode.other_error(e.args[1])) from None except socket.gaierror as e: raise SDKException(ErrorCode.other_error(e.args[1])) from None
def add_multi_sign_transaction(self, m: int, pub_keys: List[bytes] or List[str], signer: Account): """ This interface is used to generate an Transaction object which has multi signature. """ for index, pk in enumerate(pub_keys): if isinstance(pk, str): pub_keys[index] = pk.encode('ascii') pub_keys = ProgramBuilder.sort_public_keys(pub_keys) tx_hash = self.hash256() sig_data = signer.generate_signature(tx_hash) if self.sig_list is None or len(self.sig_list) == 0: self.sig_list = [] elif len(self.sig_list) >= TX_MAX_SIG_SIZE: raise SDKException( ErrorCode.param_err( 'the number of transaction signatures should not be over 16' )) else: for i in range(len(self.sig_list)): if self.sig_list[i].public_keys == pub_keys: if len(self.sig_list[i].sig_data) + 1 > len(pub_keys): raise SDKException( ErrorCode.param_err('too more sigData')) if self.sig_list[i].m != m: raise SDKException(ErrorCode.param_err('M error')) self.sig_list[i].sig_data.append(sig_data) return sig = Sig(pub_keys, m, [sig_data]) self.sig_list.append(sig)
def get_account_data_by_b58_address(self, b58_address: str) -> AccountData: if not isinstance(b58_address, str): raise SDKException(ErrorCode.require_str_params) for acct in self.wallet_in_mem.accounts: if not isinstance(acct, AccountData): raise SDKException(ErrorCode.other_error('Invalid account data in memory.')) if acct.b58_address == b58_address: return acct raise SDKException(ErrorCode.other_error(f'Get account {b58_address} failed.'))
def __init__(self, script_hash: Union[bytes, bytearray]): if not isinstance(script_hash, bytes): try: script_hash = bytes(script_hash) except TypeError: raise SDKException(ErrorCode.other_error('Invalid script hash.')) if len(script_hash) != 20: raise SDKException(ErrorCode.other_error('Invalid script hash.')) self.ZERO = script_hash
def compare_pubkey(pub_key: bytes): if not isinstance(pub_key, bytes): raise SDKException(ErrorCode.other_error('Invalid key.')) if KeyType.from_pubkey(pub_key) == KeyType.SM2: raise SDKException(ErrorCode.other_error('Unsupported key type')) elif KeyType.from_pubkey(pub_key) == KeyType.ECDSA: x = pub_key[1:] return util.string_to_number(x) else: return str(pub_key)
async def get_memory_pool_tx_state(self, tx_hash: str, is_full: bool = False): payload = self.generate_json_rpc_payload(RpcMethod.GET_MEM_POOL_TX_STATE, [tx_hash]) response = await self.__post(payload) if response.get('result', '') == '': raise SDKException(ErrorCode.invalid_tx_hash(tx_hash)) if response.get('error', -1) != 0: raise SDKException(ErrorCode.other_error(response.get('result', ''))) if is_full: return response return response['result']['State']
def get_control_info_by_index(self, ont_id: str, index: int) -> Control: if not isinstance(ont_id, str): raise SDKException(ErrorCode.require_str_params) if not ont_id.startswith(DID_ONT): raise SDKException(ErrorCode.invalid_ont_id_format(ont_id)) identity = self.get_identity_by_ont_id(ont_id) try: ctrl = identity.controls[index] except IndexError: raise SDKException(ErrorCode.other_error(f'Get {ont_id}\'s control account failed.')) return ctrl
def get_public_key_by_hex_private_key(private_key: str): if not isinstance(private_key, str): raise SDKException( ErrorCode.other_error( 'The type of private key should be hexadecimal str.')) if len(private_key) != 64: raise SDKException( ErrorCode.other_error( 'The length of private key should be 64 bytes.')) private_key = bytes.fromhex(private_key) point_str = ECIES.get_public_key_by_bytes_private_key(private_key) return point_str.hex()
def to_int(hex_str: str) -> int: if len(hex_str) == 0: return 0 try: array = bytearray.fromhex(hex_str) except ValueError as e: raise SDKException(ErrorCode.other_error(e.args[0])) array.reverse() try: num = int(bytearray.hex(array), 16) except ValueError as e: raise SDKException(ErrorCode.other_error(e.args[0])) return num
def __post(self, method, b58_address: str or None, pwd: str or None, params): payload = dict(qid=str(randint(0, maxsize)), method=method, params=params) if isinstance(b58_address, str): payload['account'] = b58_address if isinstance(pwd, str): payload['pwd'] = pwd try: response = requests.post(self.__url, json=payload, headers=self.__header, timeout=10) except requests.exceptions.MissingSchema as e: raise SDKException(ErrorCode.connect_err(e.args[0])) from None except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError): raise SDKException(ErrorCode.other_error(''.join(['ConnectionError: ', self.__url]))) from None except requests.exceptions.ReadTimeout: raise SDKException(ErrorCode.other_error(''.join(['ReadTimeout: ', self.__url]))) from None try: content = response.content.decode('utf-8') except Exception as e: raise SDKException(ErrorCode.other_error(e.args[0])) from None if response.status_code != 200: raise SDKException(ErrorCode.other_error(content)) try: content = json.loads(content) except json.decoder.JSONDecodeError as e: raise SDKException(ErrorCode.other_error(e.args[0])) from None if content['error_code'] != 0: if content['error_info'] != '': raise SDKException(ErrorCode.other_error(content['error_info'])) else: raise SDKException(ErrorCode.other_error(content['result'])) return content
async def get_contract(self, hex_contract_address: str, is_full: bool = False) -> dict: """ This interface is used to get the information of smart contract based on the specified hexadecimal hash value. """ if not isinstance(hex_contract_address, str): raise SDKException(ErrorCode.param_err('a hexadecimal contract address is required.')) if len(hex_contract_address) != 40: raise SDKException(ErrorCode.param_err('the length of the contract address should be 40 bytes.')) payload = self.generate_json_rpc_payload(RpcMethod.GET_SMART_CONTRACT, [hex_contract_address, 1]) response = await self.__post(payload) if is_full: return response result = response['result'] return dict() if result is None else result
def get_gas_consumed(event: dict): Event.__check_event(event) try: return event['GasConsumed'] except KeyError: raise SDKException( ErrorCode.other_error('Gas consumed not found in event'))
def get_state(event: dict): Event.__check_event(event) try: return event['State'] except KeyError: raise SDKException( ErrorCode.other_error('State not found in event'))
def get_tx_hash(event: dict): Event.__check_event(event) try: return event['TxHash'] except KeyError: raise SDKException( ErrorCode.other_error('TxHash not found in event'))
def from_signature_scheme(scheme: SignatureScheme): if scheme == SignatureScheme.SHA224withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA256withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA384withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA384withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA512withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA3_224withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA3_256withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA3_384withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SHA3_512withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.RIPEMD160withECDSA: return KeyType.ECDSA elif scheme == SignatureScheme.SM3withSM2: return KeyType.SM2 else: raise SDKException( ErrorCode.other_error('invalid signature scheme.'))
def hex_contract_address(self, hex_contract_address): if isinstance(hex_contract_address, str) and len(hex_contract_address) == 40: self.__hex_contract_address = hex_contract_address else: raise SDKException( ErrorCode.invalid_contract_address(hex_contract_address))
def to_utf8_str(ascii_str: str) -> str: try: utf8_str = bytes.fromhex(ascii_str) utf8_str = utf8_str.decode('utf-8') except ValueError as e: raise SDKException(ErrorCode.other_error(e.args[0])) return utf8_str
async def __send_recv(self, msg: dict, is_full: bool): if self.__ws_client is None or self.__ws_client.closed: try: await self.connect() except TimeoutError: raise SDKException(ErrorCode.connect_timeout( self.__url)) from None await self.__ws_client.send(json.dumps(msg)) response = await self.__ws_client.recv() response = json.loads(response) if is_full: return response if response['Error'] != 0: raise SDKException( ErrorCode.other_error(response.get('Result', ''))) return response.get('Result', dict())
def __init__(self, wallet_path: str = '', scheme: SignatureScheme = SignatureScheme.SHA256withECDSA): if not isinstance(scheme, SignatureScheme): raise SDKException(ErrorCode.other_error('Invalid signature scheme.')) self.scheme = scheme self.wallet_file = WalletData() self.wallet_in_mem = WalletData() self.__wallet_path = wallet_path
def __post(self, url: str, data: str): try: response = requests.post(url, data=data, timeout=10) except requests.exceptions.MissingSchema as e: raise SDKException(ErrorCode.connect_err(e.args[0])) except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError): raise SDKException(ErrorCode.connect_timeout(self._url)) from None if response.status_code != 200: raise SDKException(ErrorCode.other_error(response.content.decode('utf-8'))) try: response = json.loads(response.content.decode('utf-8')) except json.decoder.JSONDecodeError as e: raise SDKException(ErrorCode.other_error(e.args[0])) if response['Error'] != 0: raise SDKException(ErrorCode.other_error(response['Result'])) return response
def get_control_info_by_b58_address(self, ont_id: str, b58_address: str) -> Control: WalletManager.__check_ont_id(ont_id) identity = self.get_identity_by_ont_id(ont_id) for ctrl in identity.controls: if ctrl.b58_address == b58_address: return ctrl raise SDKException(ErrorCode.other_error(f'Get account {b58_address} failed.'))
def __deserialize_stack_item(reader: BinaryReader) -> dict or bytearray: param_type = reader.read_byte() if param_type == BuildParams.Type.bytearray_type.value: b = reader.read_var_bytes() return b elif param_type == BuildParams.Type.bool_type.value: return reader.read_bool() elif param_type == BuildParams.Type.int_type.value: b = reader.read_var_bytes() return NeoData.__big_int_from_bytes(bytearray(b)) elif param_type == BuildParams.Type.struct_type.value or param_type == BuildParams.Type.array_type.value: count = reader.read_var_int() item_list = list() for _ in range(count): item = NeoData.__deserialize_stack_item(reader) item_list.append(item) if param_type == BuildParams.Type.struct_type.value: return Struct(item_list) return item_list elif param_type == BuildParams.Type.dict_type.value: count = reader.read_var_int() item_dict = dict() for _ in range(count): key = NeoData.__deserialize_stack_item(reader) value = NeoData.__deserialize_stack_item(reader) item_dict[key] = value return item_dict else: raise SDKException(ErrorCode.other_error('type error'))
def to_bytes_address(hex_address: str) -> bytes: try: bytes_address = bytes.fromhex(hex_address) except ValueError as e: raise SDKException(ErrorCode.other_error(e.args[0])) address = Address(bytes_address) return address.to_bytes()
def push_bool(self, value: bool): if not isinstance(value, bool): raise SDKException(ErrorCode.other_error('invalid data')) if value: self.write_bytes(WASM_TRUE) else: self.write_bytes(WASM_FALSE)
def write_var_uint(self, value: int): if not isinstance(value, int): raise SDKException(ErrorCode.other_error('invalid data')) if value < 0: raise SDKException(ErrorCode.other_error('invalid data')) elif value < 0xFD: self.write_bytes(value.to_bytes(length=1, byteorder='little', signed=False)) elif value <= 0xFFFF: self.write_bytes(b'\xFD') self.write_bytes(value.to_bytes(length=2, byteorder='little', signed=False)) elif value <= 0xFFFFFFFF: self.write_bytes(b'\xFE') self.write_bytes(value.to_bytes(length=4, byteorder='little', signed=False)) else: self.write_bytes(b'\xFF') self.write_bytes(value.to_bytes(length=8, byteorder='little', signed=False))
def add_identity(self, identity: Identity): for item in self.identities: if item.ont_id == identity.ont_id: raise SDKException( ErrorCode.other_error( 'add identity failed, OntId conflict.')) self.identities.append(identity)
def get_gcm_decoded_private_key(encrypted_key_str: str, password: str, b58_address: str, salt: str, n: int, scheme: SignatureScheme) -> str: """ This interface is used to decrypt an private key which has been encrypted. :param encrypted_key_str: an gcm encrypted private key in the form of string. :param password: the secret pass phrase to generate the keys from. :param b58_address: a base58 encode address which should be correspond with the private key. :param salt: a string to use for better protection from dictionary attacks. :param n: CPU/memory cost parameter. :param scheme: the signature scheme. :return: a private key in the form of string. """ r = 8 p = 8 dk_len = 64 scrypt = Scrypt(n, r, p, dk_len) derived_key = scrypt.generate_kd(password, salt) iv = derived_key[0:12] key = derived_key[32:64] encrypted_key = base64.b64decode(encrypted_key_str).hex() mac_tag = bytes.fromhex(encrypted_key[64:96]) cipher_text = bytes.fromhex(encrypted_key[0:64]) private_key = AESHandler.aes_gcm_decrypt_with_iv(cipher_text, b58_address.encode(), mac_tag, key, iv) if len(private_key) == 0: raise SDKException(ErrorCode.decrypt_encrypted_private_key_error) acct = Account(private_key, scheme) if acct.get_address().b58encode() != b58_address: raise SDKException(ErrorCode.other_error('Address error.')) return private_key.hex()