def __init__(self, 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 = ''
def to_utf8_str(ascii_str: str) -> str: try: utf8_str = binascii.a2b_hex(ascii_str) utf8_str = utf8_str.decode('utf-8') except (ValueError, binascii.Error) as e: raise SDKException(ErrorCode.other_error(e.args[0])) return utf8_str
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 new_add_recovery_transaction(self, ont_id: str, pub_key: str or bytes, b58_recovery_address: str, b58_payer_address: str, gas_limit: int, gas_price: int): """ This interface is used to generate a Transaction object which is used to add the recovery. :param ont_id: OntId. :param pub_key: the hexadecimal public key in the form of string. :param b58_recovery_address: a base58 encode address which indicate who is the recovery. :param b58_payer_address: a base58 encode address which indicate who will pay for the transaction. :param gas_limit: an int value that indicate the gas limit. :param gas_price: an int value that indicate the gas price. :return: """ if isinstance(pub_key, str): bytes_pub_key = bytes.fromhex(pub_key) elif isinstance(pub_key, bytes): bytes_pub_key = pub_key else: raise SDKException( ErrorCode.params_type_error( 'a bytes or str type of public key is required.')) bytes_recovery_address = Address.b58decode( b58_recovery_address).to_bytes() args = dict(ontid=ont_id.encode('utf-8'), recovery=bytes_recovery_address, pk=bytes_pub_key) tx = self.__generate_transaction('addRecovery', args, b58_payer_address, gas_limit, gas_price) return tx
def read_var_int(self, max_size=sys.maxsize): """ Read a variable length integer from the stream. The NEO network protocol supports encoded storage for space saving. See: http://docs.neo.org/en-us/node/network-protocol.html#convention Args: max_size (int): (Optional) maximum number of bytes to read. Returns: int: """ fb = self.read_byte() if fb is 0: return fb if hex(fb) == '0xfd': value = self.read_uint16() elif hex(fb) == '0xfe': value = self.read_uint32() elif hex(fb) == '0xff': value = self.read_uint64() else: value = fb if value > max_size: raise SDKException(ErrorCode.param_err('Invalid format')) return int(value)
def new_registry_ont_id_transaction(self, ont_id: str, pub_key: str or bytes, b58_payer_address: str, gas_limit: int, gas_price: int) -> Transaction: """ This interface is used to generate a Transaction object which is used to register ONT ID. :param ont_id: OntId. :param pub_key: the hexadecimal public key in the form of string. :param b58_payer_address: a base58 encode address which indicate who will pay for the transaction. :param gas_limit: an int value that indicate the gas limit. :param gas_price: an int value that indicate the gas price. :return: a Transaction object which is used to register ONT ID. """ if isinstance(pub_key, str): bytes_ctrl_pub_key = bytes.fromhex(pub_key) elif isinstance(pub_key, bytes): bytes_ctrl_pub_key = pub_key else: raise SDKException( ErrorCode.param_err( 'a bytes or str type of public key is required.')) args = dict(ontid=ont_id.encode('utf-8'), ctrl_pk=bytes_ctrl_pub_key) tx = self.__generate_transaction('regIDWithPublicKey', args, b58_payer_address, gas_limit, gas_price) return tx
def parse_pub_keys(ont_id: str, raw_pub_keys: str or bytes) -> list: if isinstance(raw_pub_keys, str): stream = StreamManager.get_stream(bytearray.fromhex(raw_pub_keys)) elif isinstance(raw_pub_keys, bytes): stream = StreamManager.get_stream(raw_pub_keys) else: raise SDKException( ErrorCode.params_type_error( 'bytes or str parameter is required.')) reader = BinaryReader(stream) pub_keys = list() while True: try: kid = f'{ont_id}#keys-{reader.read_int32()}' bytes_key = reader.read_var_bytes() hex_pub_key = bytes_key.hex() if len(bytes_key) == 33: key_info = dict(PubKeyId=kid, Type=KeyType.ECDSA.name, Curve=Curve.P256.name, Value=hex_pub_key) else: key_type = KeyType.from_label(bytes_key[0]) curve = Curve.from_label(bytes_key[1]) key_info = dict(PubKeyId=kid, Type=key_type, Curve=curve, Value=hex_pub_key) pub_keys.append(key_info) except SDKException as e: if e.args[0] != 10001: raise e else: break return pub_keys
def generate_blk_proof(self, commit_tx_hash: str, is_big_endian: bool = True, hex_contract_address: str = ''): if len(hex_contract_address) == 0: hex_contract_address = self.__sdk.neo_vm.claim_record().hex_contract_address count = 0 while True: try: merkle_proof = self.__sdk.default_network.get_merkle_proof(commit_tx_hash) if isinstance(merkle_proof, dict): break except SDKException as e: if count > 5 or 'INVALID PARAMS' not in e.args[1]: raise e sleep(6) count += 1 tx_block_height = merkle_proof['BlockHeight'] current_block_height = merkle_proof['CurBlockHeight'] target_hash = merkle_proof['TransactionsRoot'] merkle_root = merkle_proof['CurBlockRoot'] target_hash_list = merkle_proof['TargetHashes'] proof_node = MerkleVerifier.get_proof(tx_block_height, target_hash_list, current_block_height) result = MerkleVerifier.validate_proof(proof_node, target_hash, merkle_root, is_big_endian) if not result: raise SDKException(ErrorCode.other_error('Invalid merkle proof')) self.__blk_proof.set_proof(commit_tx_hash, hex_contract_address, tx_block_height, merkle_root, proof_node) return self.__blk_proof
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()
def default_signature_scheme(self, scheme: SignatureScheme): if isinstance(scheme, SignatureScheme): self.__default_signature_scheme = scheme self.__wallet_manager.set_signature_scheme(scheme) else: raise SDKException( ErrorCode.other_error('Invalid signature scheme'))
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 get_public_key_by_bytes_private_key(private_key: bytes): if not isinstance(private_key, bytes): raise SDKException(ErrorCode.other_error('The type of private key should be bytes.')) if len(private_key) != 32: raise SDKException(ErrorCode.other_error('The length of private key should be 32 bytes.')) private_key = SigningKey.from_string(string=private_key, curve=NIST256p) public_key = private_key.get_verifying_key() order = public_key.pubkey.order x_int = public_key.pubkey.point.x() y_int = public_key.pubkey.point.y() x_str = number_to_string(x_int, order) if y_int % 2 == 0: point_str = b''.join([b'\x02', x_str]) else: point_str = b''.join([b'\x03', x_str]) return point_str
def decrypt_with_gcm_mode(nonce: bytes, mac_tag: bytes, cipher_text: bytes, private_key: bytes, hdr: bytes, encode_g_tilde: bytes): if not isinstance(private_key, bytes): raise SDKException(ErrorCode.other_error('the length of private key should be 32 bytes.')) if len(private_key) != 32: raise SDKException(ErrorCode.other_error('the length of private key should be 32 bytes.')) str_g_tilde_x = encode_g_tilde[1:33] str_g_tilde_y = encode_g_tilde[33:65] g_tilde_x = string_to_number(str_g_tilde_x) g_tilde_y = string_to_number(str_g_tilde_y) g_tilde = Point(NIST256p.curve, g_tilde_x, g_tilde_y, NIST256p.order) h_tilde = g_tilde * SigningKey.from_string(string=private_key, curve=NIST256p).privkey.secret_multiplier seed = b''.join([encode_g_tilde, number_to_string(h_tilde.x(), NIST256p.order)]) aes_key = pbkdf2(seed, 32) plain_text = AESHandler.aes_gcm_decrypt(cipher_text, hdr, nonce, mac_tag, aes_key) return plain_text
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.'))
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 get_tx_hash(event: dict): ContractEventParser.__check_event(event) try: return event['TxHash'] except KeyError: raise SDKException( ErrorCode.other_error('TxHash not found in event'))
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_state(event: dict): ContractEventParser.__check_event(event) try: return event['State'] except KeyError: raise SDKException( ErrorCode.other_error('State not found in event'))
def new_revoke_public_key_transaction(self, ont_id: str, bytes_operator: bytes, revoked_pub_key: str or bytes, b58_payer_address: str, gas_limit: int, gas_price: int): """ This interface is used to generate a Transaction object which is used to remove public key. :param ont_id: OntId. :param bytes_operator: operator args in from of bytes. :param revoked_pub_key: a public key string which will be removed. :param b58_payer_address: a base58 encode address which indicate who will pay for the transaction. :param gas_limit: an int value that indicate the gas limit. :param gas_price: an int value that indicate the gas price. :return: a Transaction object which is used to remove public key. """ if isinstance(revoked_pub_key, str): bytes_revoked_pub_key = bytes.fromhex(revoked_pub_key) elif isinstance(revoked_pub_key, bytes): bytes_revoked_pub_key = revoked_pub_key else: raise SDKException( ErrorCode.params_type_error( 'a bytes or str type of public key is required.')) bytes_ont_id = ont_id.encode('utf-8') args = dict(ontid=bytes_ont_id, pk=bytes_revoked_pub_key, operator=bytes_operator) tx = self.__generate_transaction('removeKey', args, b58_payer_address, gas_limit, gas_price) return tx
def get_gas_consumed(event: dict): ContractEventParser.__check_event(event) try: return event['GasConsumed'] except KeyError: raise SDKException( ErrorCode.other_error('Gas consumed not found in event'))
def new_remove_attribute_transaction(self, ont_id: str, pub_key: str or bytes, attrib_key: str, b58_payer_address: str, gas_limit: int, gas_price: int): """ This interface is used to generate a Transaction object which is used to remove attribute. :param ont_id: OntId. :param pub_key: the hexadecimal public key in the form of string. :param attrib_key: a string which is used to indicate which attribute we want to remove. :param b58_payer_address: a base58 encode address which indicate who will pay for the transaction. :param gas_limit: an int value that indicate the gas limit. :param gas_price: an int value that indicate the gas price. :return: a Transaction object which is used to remove attribute. """ if isinstance(pub_key, str): bytes_pub_key = bytes.fromhex(pub_key) elif isinstance(pub_key, bytes): bytes_pub_key = pub_key else: raise SDKException( ErrorCode.params_type_error( 'a bytes or str type of public key is required.')) args = dict(ontid=ont_id.encode('utf-8'), attrib_key=attrib_key.encode('utf-8'), pk=bytes_pub_key) tx = self.__generate_transaction('removeAttribute', args, b58_payer_address, gas_limit, gas_price) return tx
def get_notify_list(event: dict): ContractEventParser.__check_event(event) try: return event['Notify'] except KeyError: raise SDKException( ErrorCode.other_error('Notify not found in event'))
def parse_attributes(serialized_attributes: str or bytes): if len(serialized_attributes) == 0: return list() if isinstance(serialized_attributes, str): stream = StreamManager.get_stream( bytearray.fromhex(serialized_attributes)) elif isinstance(serialized_attributes, bytes): stream = StreamManager.get_stream(serialized_attributes) else: raise SDKException( ErrorCode.params_type_error( 'bytes or str parameter is required.')) reader = BinaryReader(stream) attributes_list = [] while True: try: attr_key = reader.read_var_bytes().decode('utf-8') attr_type = reader.read_var_bytes().decode('utf-8') attr_value = reader.read_var_bytes().decode('utf-8') attr = dict(Key=attr_key, Type=attr_type, Value=attr_value) attributes_list.append(attr) except SDKException as e: if 10000 < e.args[0] < 20000: break else: raise e return attributes_list
def generate_blk_proof(self, iss_acct: Account, payer: Account, gas_limit: int, gas_price: int, is_big_endian: bool = True, hex_contract_address: str = ''): if not isinstance(hex_contract_address, str): raise SDKException(ErrorCode.require_str_params) if len(hex_contract_address) != 0 and len(hex_contract_address) == 40: self.__sdk.neo_vm.claim_record( ).hex_contract_address = hex_contract_address tx_hash = self.__sdk.neo_vm.claim_record().commit( self.payload.jti, iss_acct, self.payload.sub, payer, gas_limit, gas_price) sleep(10) hex_contract_address = self.__sdk.neo_vm.claim_record( ).hex_contract_address merkle_proof = self.__sdk.get_network().get_merkle_proof(tx_hash) tx_block_height = merkle_proof['BlockHeight'] current_block_height = merkle_proof['CurBlockHeight'] target_hash = merkle_proof['TransactionsRoot'] merkle_root = merkle_proof['CurBlockRoot'] target_hash_list = merkle_proof['TargetHashes'] proof_node = MerkleVerifier.get_proof(tx_block_height, target_hash_list, current_block_height) result = MerkleVerifier.validate_proof(proof_node, target_hash, merkle_root, is_big_endian) if not result: raise SDKException(ErrorCode.other_error('Invalid merkle proof')) self.__blk_proof.set_proof(tx_hash, hex_contract_address, tx_block_height, merkle_root, proof_node) return self.__blk_proof
def __deserialize_stack_item(reader: BinaryReader): t = reader.read_byte() if t == BuildParams.Type.bytearray_type.value: b = reader.read_var_bytes() return b elif t == BuildParams.Type.bool_type.value: return reader.read_bool() elif t == BuildParams.Type.int_type.value: b = reader.read_var_bytes() return ContractDataParser.__big_int_from_bytes(bytearray(b)) elif t == BuildParams.Type.struct_type.value or t == BuildParams.Type.array_type.value: count = reader.read_var_int() item_list = list() for _ in range(count): item = ContractDataParser.__deserialize_stack_item(reader) item_list.append(item) if t == BuildParams.Type.struct_type.value: return Struct(item_list) return item_list elif t == BuildParams.Type.dict_type.value: count = reader.read_var_int() item_dict = dict() for _ in range(count): key = ContractDataParser.__deserialize_stack_item(reader) value = ContractDataParser.__deserialize_stack_item(reader) item_dict[key] = value return item_dict else: raise SDKException(ErrorCode.other_error('type error'))
def load_file(self): with open(self.__wallet_path, 'rb') as f: content = f.read() if content.startswith(codecs.BOM_UTF8): content = content[len(codecs.BOM_UTF8):] content = content.decode('utf-8') wallet_dict = json.loads(content) create_time = wallet_dict.get('createTime', '') default_id = wallet_dict.get('defaultOntid', '') default_address = wallet_dict.get('defaultAccountAddress', '') identities = wallet_dict.get('identities', list()) try: scrypt_dict = wallet_dict['scrypt'] scrypt_obj = Scrypt(scrypt_dict.get('n', 16384), scrypt_dict.get('r', 8), scrypt_dict.get('p', 8), scrypt_dict.get('dk_len', 64)) wallet = WalletData(wallet_dict['name'], wallet_dict['version'], create_time, default_id, default_address, scrypt_obj, identities, wallet_dict['accounts']) except KeyError as e: raise SDKException( ErrorCode.param_err(f'wallet file format error: {e}.')) return wallet
def to_b58_address(hex_address: str) -> str: try: bytes_address = binascii.a2b_hex(hex_address) except binascii.Error as e: raise SDKException(ErrorCode.other_error(e.args[0])) address = Address(bytes_address) return address.b58encode()
def wrapper(*args, **kwargs): ont_id = args[1] 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)) return func(*args, **kwargs)
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 decrypt_with_cbc_mode(cipher_text: bytes, private_key: bytes, iv: bytes, encode_g_tilde: bytes) -> bytes: aes_key = ECIES.generate_decrypt_aes_key(private_key, encode_g_tilde) try: plain_text = AESHandler.aes_cbc_decrypt(cipher_text, iv, aes_key) except ValueError as e: raise SDKException(ErrorCode.other_error(e.args[0])) return plain_text