def is_valid_hash(hash_str, prefix=None): """ Validate an aeternity hash, optionally restrict to a specific prefix. The validation will check if the hash parameter is of the form prefix_hash and that the hash is valid according to the decode function. :param hash_str: the hash to validate :param prefix: the prefix to restrict the validation to """ try: # decode the hash hashing.decode(hash_str) # if prefix is not set then is valid if prefix is None: return True # let's check the prefix if not isinstance(prefix, list): prefix = [prefix] # if a match is not found then raise ValueError match = False for p in prefix: match = match or prefix_match(p, hash_str) if not match: raise ValueError('Invalid prefix') # a match was found return True except ValueError as e: return False
def _txdata_to_txobject(self, data: dict, descriptor: dict, metadata: dict = {}, compute_hash=True) -> TxObject: # initialize the right data size # this is PYTHON to POSTBODY schema = descriptor.get("schema", []) raw_data = [0] * (len(schema) + 1) # the +1 is for the tag # set the tx tag first raw_data[0] = _int(data.get("tag")) # parse fields and encode them for label, fn in schema.items(): if fn.field_type == _INT: raw_data[fn.index] = _int(data.get(label, 0)) elif fn.field_type == _ID: raw_data[fn.index] = _id(data.get(label)) elif fn.field_type == _ENC: raw_data[fn.index] = decode(data.get(label)) elif fn.field_type == _OTTL_TYPE: raw_data[fn.index] = _int(idf.ORACLE_TTL_TYPES.get(data.get(label))) elif fn.field_type == _SG: # signatures are always a list raw_data[fn.index] = [decode(sg) for sg in data.get(label, [])] elif fn.field_type == _VM_ABI: # vm/abi are encoded in the same 32bit length block raw_data[fn.index] = _int(data.get("vm_version")) + _int(data.get("abi_version"), 2) elif fn.field_type == _BIN: # this are binary string #TODO: may be byte array raw_data[fn.index] = _binary(data.get(label)) elif fn.field_type == _PTR: # this are name pointers raw_data[fn.index] = [[_binary(p.get("key")), _id(p.get("id"))] for p in data.get(label, [])] elif fn.field_type == _TX: # this can be raw or tx object tx = data.get(label).tx if hasattr(data.get(label), "tx") else data.get(label) raw_data[fn.index] = decode(tx) # encode the transaction in rlp rlp_tx = rlp.encode(raw_data) # encode the tx in base64 rlp_b64_tx = encode(idf.TRANSACTION, rlp_tx) # copy the data before modifying tx_data = copy.deepcopy(data) # build the tx object txo = TxObject( data=namedtupled.map(tx_data, _nt_name="TxData"), tx=rlp_b64_tx, ) # compute the tx hash if compute_hash: txo.hash = hash_encode(idf.TRANSACTION_HASH, rlp_tx) # copy the metadata if exists or initialize it if None tx_meta = copy.deepcopy(metadata) if metadata is not None else {} # compute the minimum fee if descriptor.get("fee") is not None: tx_meta["min_fee"] = self.compute_min_fee(data, descriptor, raw_data) # only set the metadata if it is not empty txo.set_metadata(tx_meta) return txo
def is_signature_valid(account_id, signature, data: bytes) -> bool: """ Verify the signature of a message :param account_id: the account id signing the message :param signature: the signature of the message :param data: the message that has been signed :return: true if the signature for the message is valid, false otherwise """ try: id = hashing.decode(account_id) if isinstance(account_id, str) else account_id sg = hashing.decode(signature) if isinstance(signature, str) else signature VerifyKey(id).verify(data, sg) return True except Exception: return False
def _delegate_common(self, account: Account, *kwargs): """ Utility method to create a delegate signature for a contract Args: account: the account authorizing the transaction kwargs: the list of additional entity ids to be added to the data to be signed Returns: the signature to use for delegation """ sig_data = self.config.network_id.encode("utf8") + hashing.decode(account.get_address()) for _id in kwargs: sig_data += hashing.decode(_id) # sign the data sig = account.sign(sig_data) return sig
def compute_tx_hash(encoded_tx: str) -> str: """ Generate the hash from a signed and encoded transaction :param encoded_tx: an encoded signed transaction """ tx_raw = decode(encoded_tx) return hash_encode(idf.TRANSACTION_HASH, tx_raw)
def sign_encode_transaction(self, tx, metadata: dict=None): """ Sign, encode and compute the hash of a transaction :param tx: the TxObject to be signed :param metadata: additional data to include in the output of the signed transaction object :return: encoded_signed_tx, encoded_signature, tx_hash """ # decode the transaction if not in native mode transaction = _tx_native(op=UNPACK_TX, tx=tx.tx if hasattr(tx, "tx") else tx) # get the transaction as byte list tx_raw = decode(transaction.tx) # sign the transaction signature = self.account.sign(_binary(self.network_id) + tx_raw) # encode the transaction encoded_signed_tx, encoded_signature = self.encode_signed_transaction(tx_raw, signature) # compute the hash tx_hash = TxBuilder.compute_tx_hash(encoded_signed_tx) # return the object tx = dict( data=transaction.data, metadata=metadata, tx=encoded_signed_tx, hash=tx_hash, signature=encoded_signature, network_id=self.network_id, ) return namedtupled.map(tx, _nt_name="TxObject")
def compute_tx_hash(signed_tx): """ Generate the hash from a signed and encoded transaction :param signed_tx: an encoded signed transaction """ signed = hashing.decode(signed_tx) return hashing.hash_encode("th", signed)
def parse_tx_string(self, tx_string) -> TxObject: """ Parse a transaction string to a transaction object """ # it is an encoded tx, happens when there is a tx to decode rlp_data = decode(tx_string) return self._rlptx_to_txobject(rlp_data)
def sign_encode_transaction(self, tx): """ Sign, encode and compute the hash of a transaction :return: encoded_signed_tx, encoded_signature, tx_hash """ # decode the transaction if not in native mode transaction = hashing.decode(tx.tx) if hasattr( tx, "tx") else hashing.decode(tx) # sign the transaction signature = self.account.sign( hashing.to_bytes(self.network_id) + transaction) # encode the transaction encoded_signed_tx, encoded_signature = self.encode_signed_transaction( transaction, signature) # compute the hash tx_hash = TxBuilder.compute_tx_hash(encoded_signed_tx) # return the return encoded_signed_tx, encoded_signature, tx_hash
def tx_spend(self, recipient_id, amount, payload, fee, ttl): """ create a spend transaction :param recipient_id: the public key of the recipient :param amount: the amount to send :param payload: the payload associated with the data :param fee: the fee for the transaction :param ttl: the relative ttl of the transaction """ # compute the absolute ttl and the nonce nonce, ttl = self._get_nonce_ttl(ttl) if self.native_transactions: sid = hashing.to_bytes(ID_TAG_ACCOUNT) + hashing.decode( self.account.get_address()) rid = hashing.to_bytes(ID_TAG_ACCOUNT) + hashing.decode( recipient_id) tx = [ hashing.to_bytes(OBJECT_TAG_SPEND_TRANSACTION), hashing.to_bytes(VSN), sid, rid, hashing.to_bytes(amount), hashing.to_bytes(fee), hashing.to_bytes(ttl), hashing.to_bytes(nonce), hashing.to_bytes(payload) ] tx = hashing.encode_rlp("tx", tx) else: # send the update transaction body = { "recipient_id": recipient_id, "amount": amount, "fee": fee, "sender_id": self.account.get_address(), "payload": payload, "ttl": ttl, "nonce": nonce, } # request a spend transaction tx = self.epoch.post_spend(body=body) return self.sign_encode_transaction(tx)
def sign_transaction(self, transaction: TxObject, metadata: dict = None) -> str: """ Sign, encode and compute the hash of a transaction :param tx: the TxObject to be signed :param metadata: additional data to include in the output of the signed transaction object :return: encoded_signed_tx, encoded_signature, tx_hash """ # get the transaction as byte list tx_raw = decode(transaction.tx) # sign the transaction signature = self.account.sign(_binary(self.network_id) + tx_raw) # pack and encode the transaction return encode(idf.SIGNATURE, signature)
def test_transaction_tx_object_spend(): amount = 1870600000000000000 fee = 20500000000000 nonce = 316260 payload = "ba_VGltZSBpcyBtb25leSwgbXkgZnJpZW5kcy4gL1lvdXJzIEJlZXBvb2wuLyrtvsY=" recipient_id = "ak_YZeWQYL8UzStPmvPdQREcvrdVTAA6xd3jim3PohRbMX2983hg" sender_id = "ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv" _type = "SpendTx" ttl = 0 version = 1 tag = idf.OBJECT_TAG_SPEND_TRANSACTION signatures = "sg_RogsZGaYNefpT9hCZwEQNbgHVqWeR8MHD624xUYPjgjQPnK61vAuw7i63kCsYCiaRpbkYgyZEF4i9ipDAB6VS1AhrARwh" tx_hash = "th_JPyq2xJuxsm8qWdwuySnGde9SU2CTqvqKFikW2jLoTfyMg2BF" # meta properties meta_min_fee = 17740000000000 api_spend_tx = { "amount": amount, "fee": fee, "nonce": nonce, "payload": payload, "recipient_id": recipient_id, "sender_id": sender_id, "type": _type, "version": version } api_signed_tx = Munch.fromDict({ "block_height": 181115, "block_hash": "mh_2essz8vfq4A8UZU98Jpm5VcB3Wt7p5CbQhzBgojjsM4AFq6z2s", "hash": tx_hash, "signatures": [signatures], "tx": api_spend_tx }) txbl = transactions.TxBuilder() txo_from_py = txbl.tx_spend(sender_id, recipient_id, amount, hashing.decode(payload), fee, ttl, nonce) txo_from_api = txbl.parse_node_reply(api_signed_tx).data.tx txo_from_str = txbl.parse_tx_string(txo_from_py.tx) assert txo_from_py.tx == txo_from_str.tx assert txo_from_py.tx == txo_from_api.tx assert txo_from_py.get("recipient_id") == txo_from_api.get( "recipient_id") == txo_from_str.get("recipient_id") == recipient_id assert txo_from_py.meta("min_fee") == meta_min_fee assert txo_from_api.meta("min_fee") == meta_min_fee assert txo_from_str.meta("min_fee") == meta_min_fee
def test_sophia_contract_tx_call(): contract = EPOCH_CLI.Contract(aer_identity_contract) tx = contract.tx_create(ACCOUNT, gas=1000) print("contract: ", contract.address) print("tx contract: ", tx) result = contract.tx_call(ACCOUNT, 'main', '42', gas=1000) assert result is not None assert result.return_type == 'ok' print("return", result.return_value) print("raw", hashing.decode(result.return_value)) # assert result.return_value.lower() == hashing.encode("cb", f'{hex(42)[2:].zfill(64).lower()}') val, remote_type = contract.decode_data(result.return_value, 'int') assert val == 42 assert remote_type == 'word'
def _sophia_contract_tx_call_online(node_cli, account): contract = node_cli.Contract(aer_identity_contract) tx = contract.tx_create(account, gas=100000) print("contract: ", contract.address) print("tx contract: ", tx) _, result = contract.tx_call(account, 'main', '42', gas=500000) assert result is not None assert result.return_type == 'ok' print("return", result.return_value) print("raw", hashing.decode(result.return_value)) # assert result.return_value.lower() == hashing.encode("cb", f'{hex(42)[2:].zfill(64).lower()}') val, remote_type = contract.decode_data(result.return_value, 'int') assert val == 42 assert remote_type == 'word'
def sign_transaction(self, transaction: TxObject, metadata: dict = None) -> str: """ Sign, encode and compute the hash of a transaction Args: tx (TxObject): the TxObject to be signed Returns: the encoded and prefixed signature """ # get the transaction as byte list tx_raw = decode(transaction.tx) # sign the transaction signature = self.account.sign(_binary(self.network_id) + tx_raw) # pack and encode the transaction return encode(idf.SIGNATURE, signature)
def _raw_key(cls, key_string): """decode a key with different method between signing and addresses""" key_string = str(key_string) if utils.is_valid_hash(key_string, prefix=ACCOUNT_ID): return hashing.decode(key_string.strip()) return bytes.fromhex(key_string.strip())
def _tx_native(op, **kwargs): def std_fee(tx_raw, fee_idx, base_gas_multiplier=1): # calculates the standard minimum transaction fee tx_copy = tx_raw # create a copy of the input tx_copy[fee_idx] = _int(0, 8) # replace fee with a byte array of lenght 8 return (defaults.BASE_GAS * base_gas_multiplier + len(rlp.encode(tx_copy)) * defaults.GAS_PER_BYTE) * defaults.GAS_PRICE def contract_fee(tx_raw, fee_idx, gas, base_gas_multiplier=1): # estimate the contract creation fee tx_copy = tx_raw # create a copy of the input tx_copy[fee_idx] = _int(0, 8) # replace fee with a byte array of lenght 8 return (defaults.BASE_GAS * base_gas_multiplier + gas + len(rlp.encode(tx_copy)) * defaults.GAS_PER_BYTE) * defaults.GAS_PRICE def oracle_fee(tx_raw, fee_idx, relative_ttl): # estimate oracle fees tx_copy = tx_raw # create a copy of the input tx_copy[fee_idx] = _int(0, 8) # replace fee with a byte array of lenght 8 fee = (defaults.BASE_GAS + len(rlp.encode(tx_copy)) * defaults.GAS_PER_BYTE) fee += math.ceil(32000 * relative_ttl / math.floor(60 * 24 * 365 / defaults.KEY_BLOCK_INTERVAL)) return fee * defaults.GAS_PRICE def build_tx_object(tx_data, tx_raw, fee_idx, min_fee): if tx_data.get("fee") < min_fee: tx_native[fee_idx] = _int(min_fee) tx_data["fee"] = min_fee tx_encoded = encode_rlp(idf.TRANSACTION, tx_native) tx = dict( data=tx_data, tx=tx_encoded, hash=TxBuilder.compute_tx_hash(tx_encoded), ) return namedtupled.map(tx, _nt_name="TxObject") # prepare tag and version if op == PACK_TX: tag = kwargs.get("tag", 0) vsn = kwargs.get("vsn", 1) tx_data = kwargs elif op == UNPACK_TX: tx_native = decode_rlp(kwargs.get("tx", [])) tag = _int_decode(tx_native[0]) else: raise Exception("Invalid operation") # check the tags if tag == idf.OBJECT_TAG_SPEND_TRANSACTION: tx_field_fee_index = 5 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("sender_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("recipient_id")), _int(kwargs.get("amount")), _int(kwargs.get("fee")), # index 5 _int(kwargs.get("ttl")), _int(kwargs.get("nonce")), _binary(kwargs.get("payload")) ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), sender_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), recipient_id=encode(idf.ACCOUNT_ID, tx_native[3][1:]), amount=_int_decode(tx_native[4]), fee=_int_decode(tx_native[5]), ttl=_int_decode(tx_native[6]), nonce=_int_decode(tx_native[7]), payload=_binary_decode(tx_native[8]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION: tx_field_fee_index = 5 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_COMMITMENT, kwargs.get("commitment_id")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")) ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), commitment_id=encode(idf.COMMITMENT, tx_native[4][1:]), fee=_int_decode(tx_native[5]), ttl=_int_decode(tx_native[6]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION: tx_field_fee_index = 6 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), decode(kwargs.get("name")), _binary(kwargs.get("name_salt")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")) ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), name=_binary_decode(tx_native[4], str), name_salt=_binary_decode(tx_native[5], int), fee=_int_decode(tx_native[6]), ttl=_int_decode(tx_native[7]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION: tx_field_fee_index = 8 if op == PACK_TX: # pack transaction # first asseble the pointers def pointer_tag(pointer): return { "account_pubkey": idf.ID_TAG_ACCOUNT, "oracle_pubkey": idf.ID_TAG_ORACLE, "contract_pubkey": idf.ID_TAG_CONTRACT, "channel_pubkey": idf.ID_TAG_CHANNEL }.get(pointer.get("key")) ptrs = [[_binary(p.get("key")), _id(pointer_tag(p), p.get("id"))] for p in kwargs.get("pointers", [])] # then build the transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_NAME, kwargs.get("name_id")), _int(kwargs.get("name_ttl")), ptrs, _int(kwargs.get("client_ttl")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")) ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), name=encode(idf.NAME, tx_native[4][1:]), name_ttl=_int_decode(tx_native[5]), pointers=[], # TODO: decode pointers client_ttl=_int_decode(tx_native[7]), fee=_int_decode(tx_native[8]), ttl=_int_decode(tx_native[9]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_NAME_SERVICE_TRANSFER_TRANSACTION: tx_field_fee_index = 6 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_NAME, kwargs.get("name_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("recipient_id")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), name=encode(idf.NAME, tx_native[4][1:]), recipient_id=encode(idf.ACCOUNT_ID, tx_native[5][1:]), fee=_int_decode(tx_native[6]), ttl=_int_decode(tx_native[7]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_NAME_SERVICE_REVOKE_TRANSACTION: tx_field_fee_index = 5 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_NAME, kwargs.get("name_id")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), ] min_fee = std_fee(tx_native, tx_field_fee_index) elif op == UNPACK_TX: # unpack transaction tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), name=encode(idf.NAME, tx_native[4][1:]), fee=_int_decode(tx_native[5]), ttl=_int_decode(tx_native[6]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_CONTRACT_CREATE_TRANSACTION: tx_field_fee_index = 6 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("owner_id")), _int(kwargs.get("nonce")), _binary(decode(kwargs.get("code"))), _int(kwargs.get("vm_version")) + _int(kwargs.get("abi_version"), 2), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), _int(kwargs.get("deposit")), _int(kwargs.get("amount")), _int(kwargs.get("gas")), _int(kwargs.get("gas_price")), _binary(decode(kwargs.get("call_data"))), ] # TODO: verify the fee caluclation for the contract min_fee = contract_fee(tx_native, tx_field_fee_index, kwargs.get("gas"), base_gas_multiplier=5) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), owner_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), code=encode(idf.BYTECODE, tx_native[4][1:]), vm_version=_int_decode(tx_native[5][0:vml - 2]), abi_version=_int_decode(tx_native[5][vml - 2:]), fee=_int_decode(tx_native[6]), ttl=_int_decode(tx_native[7]), deposit=_int_decode(tx_native[8]), amount=_int_decode(tx_native[9]), gas=_int_decode(tx_native[10]), gas_price=_int_decode(tx_native[11]), call_data=encode(idf.BYTECODE, tx_native[12][1:]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_CONTRACT_CALL_TRANSACTION: tx_field_fee_index = 6 if op == PACK_TX: # pack transaction tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("caller_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_CONTRACT, kwargs.get("contract_id")), _int(kwargs.get("abi_version")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), _int(kwargs.get("amount")), _int(kwargs.get("gas")), _int(kwargs.get("gas_price")), _binary(decode(kwargs.get("call_data"))), ] min_fee = contract_fee(tx_native, tx_field_fee_index, kwargs.get("gas"), base_gas_multiplier=30) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), caller_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), contract_id=encode(idf.CONTRACT_ID, tx_native[4][1:]), abi_version=_int_decode(tx_native[5]), fee=_int_decode(tx_native[6]), ttl=_int_decode(tx_native[7]), amount=_int_decode(tx_native[8]), gas=_int_decode(tx_native[9]), gas_price=_int_decode(tx_native[10]), call_data=encode(idf.BYTECODE, tx_native[11][1:]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_CHANNEL_CREATE_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("initiator")), _int(kwargs.get("initiator_amount")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("responder")), _int(kwargs.get("responder_amount")), _int(kwargs.get("channel_reserve")), _int(kwargs.get("lock_period")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), # _[id(delegate_ids)], TODO: handle delegate ids _binary(kwargs.get("state_hash")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 9 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_DEPOSIT_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _int(kwargs.get("amount")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _binary(kwargs.get("state_hash")), _int(kwargs.get("round")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 6 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_WITHDRAW_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("to_id")), _int(kwargs.get("amount")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _binary(kwargs.get("state_hash")), _int(kwargs.get("round")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 6 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_CLOSE_MUTUAL_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _int(kwargs.get("initiator_amount_final")), _int(kwargs.get("responder_amount_final")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 7 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_CLOSE_SOLO_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _binary(kwargs.get("payload")), # _poi(kwargs.get("poi")), TODO: implement support for _poi _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 7 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_SLASH_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _binary(kwargs.get("payload")), # _poi(kwargs.get("poi")), TODO: implement support for _poi _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 7 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_SETTLE_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _int(kwargs.get("initiator_amount_final")), _int(kwargs.get("responder_amount_final")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 7 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_SNAPSHOT_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _binary(kwargs.get("payload")), _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 6 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_CHANNEL_FORCE_PROGRESS_TRANSACTION: tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_CHANNEL, kwargs.get("channel_id")), _id(idf.ID_TAG_ACCOUNT, kwargs.get("from_id")), _binary(kwargs.get("payload")), _int(kwargs.get("round")), _binary(kwargs.get("update")), _binary(kwargs.get("state_hash")), # _trees(kwargs.get("offchain_trees")), TODO: implement support for _trees _int(kwargs.get("ttl")), _int(kwargs.get("fee")), _int(kwargs.get("nonce")), ] tx_field_fee_index = 9 min_fee = std_fee(tx_native, tx_field_fee_index) elif tag == idf.OBJECT_TAG_ORACLE_REGISTER_TRANSACTION: tx_field_fee_index = 9 if op == PACK_TX: # pack transaction oracle_ttl = kwargs.get("oracle_ttl", {}) tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("account_id")), _int(kwargs.get("nonce")), _binary(kwargs.get("query_format")), _binary(kwargs.get("response_format")), _int(kwargs.get("query_fee")), _int(0 if oracle_ttl.get("type") == idf.ORACLE_TTL_TYPE_DELTA else 1), _int(oracle_ttl.get("value")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), _int(kwargs.get("vm_version")), ] min_fee = oracle_fee(tx_native, tx_field_fee_index, oracle_ttl.get("value")) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), account_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), query_format=_binary_decode(tx_native[4]), response_format=_binary_decode(tx_native[5]), query_fee=_int_decode(tx_native[6]), oracle_ttl=dict( type=idf.ORACLE_TTL_TYPE_DELTA if _int_decode(tx_native[7]) else idf.ORACLE_TTL_TYPE_BLOCK, value=_int_decode(tx_native[8]), ), fee=_int_decode(tx_native[9]), ttl=_int_decode(tx_native[10]), vm_version=_int_decode(tx_native[11]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_ORACLE_QUERY_TRANSACTION: tx_field_fee_index = 11 if op == PACK_TX: # pack transaction query_ttl = kwargs.get("query_ttl", {}) response_ttl = kwargs.get("response_ttl", {}) tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ACCOUNT, kwargs.get("sender_id")), _int(kwargs.get("nonce")), _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), _binary(kwargs.get("query")), _int(kwargs.get("query_fee")), _int(0 if query_ttl.get("type") == idf.ORACLE_TTL_TYPE_DELTA else 1), _int(query_ttl.get("value")), _int(0 if response_ttl.get("type") == idf.ORACLE_TTL_TYPE_DELTA else 1), _int(response_ttl.get("value")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), ] min_fee = oracle_fee(tx_native, tx_field_fee_index, query_ttl.get("value")) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), sender_id=encode(idf.ACCOUNT_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), oracle_id=encode(idf.ORACLE_ID, tx_native[4][1:]), query=_binary_decode(tx_native[5]), query_fee=_int_decode(tx_native[6]), query_ttl=dict( type=idf.ORACLE_TTL_TYPE_DELTA if _int_decode(tx_native[7]) else idf.ORACLE_TTL_TYPE_BLOCK, value=_int_decode(tx_native[8]), ), response_ttl=dict( type=idf.ORACLE_TTL_TYPE_DELTA if _int_decode(tx_native[9]) else idf.ORACLE_TTL_TYPE_BLOCK, value=_int_decode(tx_native[10]), ), fee=_int_decode(tx_native[11]), ttl=_int_decode(tx_native[12]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_ORACLE_RESPONSE_TRANSACTION: tx_field_fee_index = 8 if op == PACK_TX: # pack transaction response_ttl = kwargs.get("response_ttl", {}) tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), _int(kwargs.get("nonce")), decode(kwargs.get("query_id")), _binary(kwargs.get("response")), _int(0 if response_ttl.get("type") == idf.ORACLE_TTL_TYPE_DELTA else 1), _int(response_ttl.get("value")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), ] min_fee = oracle_fee(tx_native, tx_field_fee_index, response_ttl.get("value")) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), oracle_id=encode(idf.ORACLE_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), query_id=encode(idf.ORACLE_QUERY_ID, tx_native[4][1:]), response=_binary(tx_native[5]), response_ttl=dict( type=idf.ORACLE_TTL_TYPE_DELTA if _int_decode(tx_native[6]) else idf.ORACLE_TTL_TYPE_BLOCK, value=_int_decode(tx_native[7]), ), fee=_int_decode(tx_native[8]), ttl=_int_decode(tx_native[9]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) elif tag == idf.OBJECT_TAG_ORACLE_EXTEND_TRANSACTION: tx_field_fee_index = 6 if op == PACK_TX: # pack transaction oracle_ttl = kwargs.get("oracle_ttl", {}) tx_native = [ _int(tag), _int(vsn), _id(idf.ID_TAG_ORACLE, kwargs.get("oracle_id")), _int(kwargs.get("nonce")), _int(0 if oracle_ttl.get("type", {}) == idf.ORACLE_TTL_TYPE_DELTA else 1), _int(oracle_ttl.get("value")), _int(kwargs.get("fee")), _int(kwargs.get("ttl")), ] min_fee = oracle_fee(tx_native, tx_field_fee_index, oracle_ttl.get("value")) elif op == UNPACK_TX: # unpack transaction vml = len(tx_native[5]) # this is used to extract the abi and vm version from the 5th field tx_data = dict( tag=tag, vsn=_int_decode(tx_native[1]), oracle_id=encode(idf.ORACLE_ID, tx_native[2][1:]), nonce=_int_decode(tx_native[3]), oracle_ttl=dict( type=idf.ORACLE_TTL_TYPE_DELTA if _int_decode(tx_native[4]) else idf.ORACLE_TTL_TYPE_BLOCK, value=_int_decode(tx_native[5]), ), fee=_int_decode(tx_native[6]), ttl=_int_decode(tx_native[7]), ) min_fee = tx_data.get("fee") else: raise Exception("Invalid operation") return build_tx_object(tx_data, tx_native, tx_field_fee_index, min_fee) else: raise UnsupportedTransactionType(f"Unusupported transaction tag {tag}")
def to_sophia_bytes(self, arg, generic, bindings={}): if isinstance(arg, str): val = hashing.decode(arg).hex() if utils.is_valid_hash(arg) else arg return f'#{val}' elif isinstance(arg, bytes): return f"#{arg.hex()}"