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 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 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 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 compute_min_fee(self, tx_data: dict, tx_descriptor: dict, tx_raw: list) -> int: """ Compute the minimum fee for a transaction Args: tx_data (dict): the transaction data tx_descriptor (dict): the descriptor of the transaction fields tx_raw: the unencoded rlp data of the transaction Returns: the minimum fee for the transaction """ # if the fee is none means it is not necessary to compute the fee if tx_descriptor.get("fee") is None: return 0 # extract the parameters from the descriptor fee_field_index = tx_descriptor.get("schema").get("fee").index fee_type = tx_descriptor.get("fee").fee_type base_gas_multiplier = tx_descriptor.get("fee").base_gas_multiplier # for property based multiplier if isinstance(base_gas_multiplier, tuple): # check CONTRACT_CALL definition for an example field_modifier_value = tx_data.get(base_gas_multiplier[0]) base_gas_multiplier = base_gas_multiplier[1].get( field_modifier_value) # if the fee is ttl based compute the ttl component ttl_component = 0 if fee_type is TTL_BASED: ttl = tx_data.get(tx_descriptor.get("fee").ttl_field) ttl_component += (math.ceil( 32000 * ttl / math.floor(60 * 24 * 365 / self.key_block_interval))) # if the fee is for a ga meta compute the enclosed tx size based fee ga_surplus = 0 if fee_type == GA_META: tx_size = len(tx_raw[tx_descriptor.get("tx").index]) ga_surplus = (self.base_gas * base_gas_multiplier + tx_size * self.gas_per_byte) # begin calculation current_fee, min_fee = -1, defaults.FEE while current_fee != min_fee: # save the min fee into current fee current_fee = min_fee # update the fee value tx_raw[fee_field_index] = _int(current_fee) # first calculate the size part tx_size = len(rlp.encode(tx_raw)) min_fee = self.base_gas * base_gas_multiplier + tx_size * self.gas_per_byte # add the ttl_component min_fee += ttl_component # remove the ga_surplus min_fee -= ga_surplus # finally multiply for gas the gas price min_fee *= self.gas_price return min_fee
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")
def build_tx_object(tx_data, tx_raw, fee_idx, min_fee): # if fee is not set use the min fee if tx_data.get("fee") <= 0: tx_data["fee"] = min_fee # if it is set check that is greater then the minimum fee elif tx_data.get("fee") < min_fee: raise TransactionFeeTooLow( f'Minimum transaction fee is {min_fee}, provided fee is {tx_data.get("fee")}' ) tx_native[fee_idx] = _int(tx_data.get("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")
def compute_min_fee(self, tx_data: dict, tx_descriptor: dict, tx_raw: list) -> int: # if the fee is none means it is not necessary to compute the fee if tx_descriptor.get("fee") is None: return 0 # extract the parameters from the descriptor fee_field_index = tx_descriptor.get("schema").get("fee").index base_gas_multiplier = tx_descriptor.get("fee").base_gas_multiplier fee_type = tx_descriptor.get("fee").fee_type # if the fee is ttl based compute the ttl component ttl_component = 0 if fee_type is TTL_BASED: ttl = tx_data.get(tx_descriptor.get("fee").ttl_field) ttl_component += (math.ceil( 32000 * ttl / math.floor(60 * 24 * 365 / self.key_block_interval))) # if the fee is for a ga meta compute the enclosed tx size based fee ga_surplus = 0 if fee_type == GA_META: tx_size = len(tx_raw[tx_descriptor.get("tx").index]) ga_surplus = (self.base_gas * base_gas_multiplier + tx_size * self.gas_per_byte) # begin calculation current_fee, min_fee = -1, defaults.FEE while current_fee != min_fee: # save the min fee into current fee current_fee = min_fee # update the fee value tx_raw[fee_field_index] = _int(current_fee) # first calculate the size part tx_size = len(rlp.encode(tx_raw)) min_fee = self.base_gas * base_gas_multiplier + tx_size * self.gas_per_byte # add the ttl_component min_fee += ttl_component # remove the ga_surplus min_fee -= ga_surplus # finally multiply for gas the gas price min_fee *= self.gas_price return min_fee
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}")