Esempio n. 1
0
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
Esempio n. 2
0
 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
Esempio n. 3
0
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
Esempio n. 4
0
    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
Esempio n. 5
0
 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)
Esempio n. 6
0
 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")
Esempio n. 7
0
 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)
Esempio n. 8
0
 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)
Esempio n. 9
0
 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
Esempio n. 10
0
    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)
Esempio n. 11
0
 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)
Esempio n. 12
0
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
Esempio n. 13
0
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'
Esempio n. 15
0
    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)
Esempio n. 16
0
 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())
Esempio n. 17
0
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}")
Esempio n. 18
0
 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()}"