Пример #1
0
 def test_initializes_if_all_required_fields_present(self):
     tx = Transaction(
         account=_ACCOUNT,
         fee=_FEE,
         sequence=_SEQUENCE,
         transaction_type=TransactionType.ACCOUNT_DELETE,
     )
     self.assertTrue(tx.is_valid())
Пример #2
0
 def test_to_dict_includes_type_as_string(self):
     tx = Transaction(
         account=_ACCOUNT,
         fee=_FEE,
         sequence=_SEQUENCE,
         transaction_type=TransactionType.ACCOUNT_DELETE,
     )
     value = tx.to_dict()["transaction_type"]
     self.assertEqual(type(value), str)
Пример #3
0
 def test_to_dict_flag_list(self):
     tx = Transaction(
         account=_ACCOUNT,
         fee=_FEE,
         sequence=_SEQUENCE,
         transaction_type=TransactionType.ACCOUNT_DELETE,
         flags=[0b1, 0b10, 0b100],
     )
     expected_flags = 0b111
     value = tx.to_dict()["flags"]
     self.assertEqual(value, expected_flags)
Пример #4
0
def _autofill_transaction(transaction: Transaction, client: Client) -> Transaction:
    transaction_json = transaction.to_dict()
    if "sequence" not in transaction_json:
        sequence = get_next_valid_seq_number(transaction_json["account"], client)
        transaction_json["sequence"] = sequence
    if "fee" not in transaction_json:
        transaction_json["fee"] = _calculate_fee_per_transaction_type(
            transaction, client
        )
    if "last_ledger_sequence" not in transaction_json:
        ledger_sequence = get_latest_validated_ledger_sequence(client)
        transaction_json["last_ledger_sequence"] = ledger_sequence + _LEDGER_OFFSET
    return Transaction.from_dict(transaction_json)
Пример #5
0
def submit_transaction(
    transaction: Transaction,
    client: Client,
) -> Response:
    """
    Submits a transaction to the ledger.

    Args:
        transaction: the Transaction to be submitted.
        client: the network client with which to submit the transaction.

    Returns:
        The response from the ledger.

    Raises:
        XRPLRequestFailureException: if the rippled API call fails.
    """
    transaction_json = transaction.to_xrpl()
    transaction_blob = encode(transaction_json)
    response = client.request(SubmitOnly(tx_blob=transaction_blob))
    if response.is_successful():
        return response

    result = cast(Dict[str, Any], response.result)
    raise XRPLRequestFailureException(result)
Пример #6
0
    def _from_dict_special_cases(
        cls: Type[BaseModel],
        param: str,
        param_type: Type[Any],
        param_value: Dict[str, Any],
    ) -> Union[str, Enum, BaseModel, Dict[str, Any]]:
        """Handles all the recursive/more complex cases for `from_dict`."""
        from xrpl.models.amounts import Amount, IssuedCurrencyAmount
        from xrpl.models.currencies import XRP, Currency, IssuedCurrency
        from xrpl.models.transactions.transaction import Transaction

        # TODO: figure out how to make Unions work generically (if possible)

        if param_type == Amount:
            # special case, Union
            if isinstance(param_value, str):
                return param_value
            if not isinstance(param_value, dict):
                raise XRPLModelException(
                    f"{param_type} requires a dictionary of params")
            return IssuedCurrencyAmount.from_dict(param_value)

        if param_type == Currency:
            # special case, Union
            if not isinstance(param_value, dict):
                raise XRPLModelException(
                    f"{param_type} requires a dictionary of params")
            if "currency" in param_value and "issuer" in param_value:
                return IssuedCurrency.from_dict(param_value)
            if "currency" in param_value:
                param_value_copy = {**param_value}
                del param_value_copy["currency"]
                return XRP.from_dict(param_value_copy)
            raise XRPLModelException(f"No valid type for {param}")

        if param_type == Transaction:
            # special case, multiple options (could be any Transaction type)
            if "transaction_type" not in param_value:
                raise XRPLModelException(
                    f"{param} not a valid parameter for {cls.__name__}")
            type_str = param_value["transaction_type"]
            # safely convert type string into the actual type
            transaction_type = Transaction.get_transaction_type(type_str)
            param_value_copy = {**param_value}
            del param_value_copy["transaction_type"]
            return transaction_type.from_dict(param_value_copy)

        if param_type in BaseModel.__subclasses__():
            # any other BaseModel
            if not isinstance(param_value, dict):
                raise XRPLModelException(
                    f"{param_type} requires a dictionary of params")
            # mypy doesn't know that the If checks that it's a subclass of BaseModel
            return param_type.from_dict(param_value)  # type: ignore

        if param_type in Enum.__subclasses__():
            # mypy doesn't know that the If checks that it's a subclass of Enum
            return param_type(param_value)  # type: ignore

        return param_value
Пример #7
0
def send_reliable_submission(transaction: Transaction,
                             client: Client) -> Response:
    """
    Submits a transaction and verifies that it has been included in a validated ledger
    (or has errored/will not be included for some reason).

    `See Reliable Transaction Submission
    <https://xrpl.org/reliable-transaction-submission.html>`_

    Args:
        transaction: the signed transaction to submit to the ledger. Requires a
            `last_ledger_sequence` param.
        client: the network client used to submit the transaction to a rippled node.

    Returns:
        The response from a validated ledger.

    Raises:
        XRPLReliableSubmissionException: if the transaction fails or is missing a
            `last_ledger_sequence` param.
    """
    transaction_hash = transaction.get_hash()
    submit_response = submit_transaction(transaction, client)
    result = cast(Dict[str, Any], submit_response.result)
    if result["engine_result"] != "tesSUCCESS":
        result_code = result["engine_result"]
        result_message = result["engine_result_message"]
        raise XRPLReliableSubmissionException(
            f"Transaction failed, {result_code}: {result_message}")

    return _wait_for_final_transaction_outcome(transaction_hash, client)
Пример #8
0
 def test_from_xrpl_set_fee(self):
     reference_fee_units = 10
     reserve_base = 20000000
     reserve_increment = 5000000
     base_fee = "000000000000000A"
     set_fee_dict = {
         "Account": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
         "BaseFee": base_fee,
         "Fee": "0",
         "ReferenceFeeUnits": reference_fee_units,
         "ReserveBase": reserve_base,
         "ReserveIncrement": reserve_increment,
         "Sequence": 0,
         "SigningPubKey": "",
         "TransactionType": "SetFee",
     }
     expected = SetFee(
         reference_fee_units=reference_fee_units,
         reserve_base=reserve_base,
         reserve_increment=reserve_increment,
         base_fee=base_fee,
     )
     actual = Transaction.from_xrpl(set_fee_dict)
     self.assertEqual(actual, expected)
     full_dict = {**set_fee_dict, "Flags": 0, "TxnSignature": ""}
     self.assertEqual(actual.to_xrpl(), full_dict)
Пример #9
0
def safe_sign_transaction(
    transaction: Transaction,
    wallet: Wallet,
    check_fee: bool = True,
) -> Transaction:
    """
    Signs a transaction locally, without trusting external rippled nodes.

    Args:
        transaction: the transaction to be signed.
        wallet: the wallet with which to sign the transaction.
        check_fee: whether to check if the fee is higher than the expected transaction
            type fee. Defaults to True.

    Returns:
        The signed transaction blob.
    """
    if check_fee:
        _check_fee(transaction)
    transaction_json = _prepare_transaction(transaction, wallet)
    serialized_for_signing = encode_for_signing(transaction_json)
    serialized_bytes = bytes.fromhex(serialized_for_signing)
    signature = sign(serialized_bytes, wallet.private_key)
    transaction_json["TxnSignature"] = signature
    return cast(Transaction, Transaction.from_xrpl(transaction_json))
Пример #10
0
 def test_missing_required_field(self):
     with self.assertRaises(XRPLModelException):
         # missing account
         Transaction(
             fee=_FEE,
             sequence=_SEQUENCE,
             transaction_type=TransactionType.ACCOUNT_DELETE,
         )
Пример #11
0
 def test_from_xrpl(self):
     dirname = os.path.dirname(__file__)
     full_filename = "x-codec-fixtures.json"
     absolute_path = os.path.join(dirname, full_filename)
     with open(absolute_path) as fixtures_file:
         fixtures_json = json.load(fixtures_file)
         for test in fixtures_json["transactions"]:
             x_json = test["xjson"]
             r_json = test["rjson"]
             with self.subTest(json=x_json):
                 tx = Transaction.from_xrpl(x_json)
                 translated_tx = tx.to_xrpl()
                 self.assertEqual(x_json, translated_tx)
             with self.subTest(json=r_json):
                 tx = Transaction.from_xrpl(r_json)
                 translated_tx = tx.to_xrpl()
                 self.assertEqual(r_json, translated_tx)
Пример #12
0
    def from_dict(cls: Type[SubmitMultisigned],
                  value: Dict[str, Any]) -> SubmitMultisigned:
        """
        Construct a new SubmitMultisigned object from a dictionary of parameters.

        Args:
            value: The value to construct the SubmitMultisigned from.

        Returns:
            A new SubmitMultisigned object, constructed using the given parameters.
        """
        fixed_value = {**value}
        if "TransactionType" in fixed_value["tx_json"]:  # xrpl format
            fixed_value["tx_json"] = Transaction.from_xrpl(
                fixed_value["tx_json"])
        return cast(SubmitMultisigned,
                    super(SubmitMultisigned, cls).from_dict(fixed_value))
Пример #13
0
def safe_sign_transaction(
    transaction: Transaction,
    wallet: Wallet,
) -> Transaction:
    """
    Signs a transaction locally, without trusting external rippled nodes.

    Args:
        transaction: the transaction to be signed.
        wallet: the wallet with which to sign the transaction.

    Returns:
        The signed transaction blob.
    """
    transaction_json = _prepare_transaction(transaction, wallet)
    serialized_for_signing = encode_for_signing(transaction_json)
    serialized_bytes = bytes.fromhex(serialized_for_signing)
    signature = sign(serialized_bytes, wallet.private_key)
    transaction_json["TxnSignature"] = signature
    return cast(Transaction, Transaction.from_xrpl(transaction_json))
Пример #14
0
def safe_sign_transaction(transaction: Transaction, wallet: Wallet) -> str:
    """
    Signs a transaction locally, without trusting external rippled nodes.

    Args:
        transaction: the transaction to be signed.
        wallet: the wallet with which to sign the transaction.

    Returns:
        The signed transaction blob.
    """
    # Increment the wallet sequence number, since we're about to use one.
    wallet.next_sequence_num += 1
    transaction_json = transaction_json_to_binary_codec_form(
        transaction.to_dict())
    transaction_json["SigningPubKey"] = wallet.pub_key
    serialized_for_signing = encode_for_signing(transaction_json)
    serialized_bytes = bytes.fromhex(serialized_for_signing)
    signature = sign(serialized_bytes, wallet.priv_key)
    transaction_json["TxnSignature"] = signature
    return encode(transaction_json)
Пример #15
0
def _prepare_transaction(
    transaction: Transaction,
    wallet: Wallet,
) -> Dict[str, Any]:
    """
    Prepares a Transaction by converting it to a JSON-like dictionary, converting the
    field names to CamelCase. If a Client is provided, then it also autofills any
    relevant fields.

    Args:
        transaction: the Transaction to be prepared.
        wallet: the wallet that will be used for signing.

    Returns:
        A JSON-like dictionary that is ready to be signed.

    Raises:
        XRPLException: if both LastLedgerSequence and `ledger_offset` are provided, or
            if an address tag is provided that does not match the X-Address tag.
    """
    transaction_json = transaction_json_to_binary_codec_form(
        transaction.to_dict())
    transaction_json["SigningPubKey"] = wallet.public_key

    _validate_account_xaddress(transaction_json, "Account", "SourceTag")
    if "Destination" in transaction_json:
        _validate_account_xaddress(transaction_json, "Destination",
                                   "DestinationTag")

    # DepositPreauth
    _convert_to_classic_address(transaction_json, "Authorize")
    _convert_to_classic_address(transaction_json, "Unauthorize")
    # EscrowCancel, EscrowFinish
    _convert_to_classic_address(transaction_json, "Owner")
    # SetRegularKey
    _convert_to_classic_address(transaction_json, "RegularKey")

    return transaction_json
Пример #16
0
 def test_from_xrpl_memos(self):
     memo_type = "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963"
     tx = {
         "Account": "rnoGkgSpt6AX1nQxZ2qVGx7Fgw6JEcoQas",
         "TransactionType": "TrustSet",
         "Fee": "10",
         "Sequence": 17892983,
         "Flags": 131072,
         "Memos": [{
             "Memo": {
                 "MemoType": memo_type,
                 "MemoData": "72656e74",
             }
         }],
         "SigningPubKey": "",
         "LimitAmount": {
             "currency": "USD",
             "issuer": "rBPvTKisx7UCGLDtiUZ6mDssXNREuVuL8Y",
             "value": "10",
         },
     }
     expected = TrustSet(
         account="rnoGkgSpt6AX1nQxZ2qVGx7Fgw6JEcoQas",
         fee="10",
         sequence=17892983,
         flags=131072,
         memos=[Memo(
             memo_type=memo_type,
             memo_data="72656e74",
         )],
         limit_amount=IssuedCurrencyAmount(
             currency="USD",
             issuer="rBPvTKisx7UCGLDtiUZ6mDssXNREuVuL8Y",
             value="10"),
     )
     self.assertEqual(Transaction.from_xrpl(tx), expected)
Пример #17
0
 def test_from_xrpl_signers(self):
     txn_sig1 = (
         "F80E201FE295AA08678F8542D8FC18EA18D582A0BD19BE77B9A24479418ADBCF4CAD28E7BD"
         "96137F88DE7736827C7AC6204FBA8DDADB7394E6D704CD1F4CD609")
     txn_sig2 = (
         "036E95B8100EBA2A4A447A3AF24500261BF480A0E8D62EE15D03A697C85E73237A5202BD9A"
         "F2D9C68B8E8A5FA8B8DA4F8DABABE95E8401C5E57EC783291EF80C")
     pubkey1 = "ED621D6D4FF54E809397195C4E24EF05E8500A7CE45CDD211F523A892CDBCDCDB2"
     pubkey2 = "EDD3ABCFF008ECE9ED3073B41913619341519BFF01F07331B56E5D6D2EC4A94A57"
     tx = {
         "Account":
         "rnoGkgSpt6AX1nQxZ2qVGx7Fgw6JEcoQas",
         "TransactionType":
         "TrustSet",
         "Fee":
         "10",
         "Sequence":
         17892983,
         "Flags":
         131072,
         "Signers": [
             {
                 "Signer": {
                     "Account": "rGVXgBz4NraZcwi5vqpmwPW6P4y74A4YvX",
                     "TxnSignature": txn_sig1,
                     "SigningPubKey": pubkey1,
                 }
             },
             {
                 "Signer": {
                     "Account": "rB5q2wsHeXdQeh2KFzBb1CujNAfSKys6GN",
                     "TxnSignature": txn_sig2,
                     "SigningPubKey": pubkey2,
                 }
             },
         ],
         "SigningPubKey":
         "",
         "LimitAmount": {
             "currency": "USD",
             "issuer": "rBPvTKisx7UCGLDtiUZ6mDssXNREuVuL8Y",
             "value": "10",
         },
     }
     expected = TrustSet(
         account="rnoGkgSpt6AX1nQxZ2qVGx7Fgw6JEcoQas",
         fee="10",
         sequence=17892983,
         flags=131072,
         signers=[
             Signer(
                 account="rGVXgBz4NraZcwi5vqpmwPW6P4y74A4YvX",
                 txn_signature=txn_sig1,
                 signing_pub_key=pubkey1,
             ),
             Signer(
                 account="rB5q2wsHeXdQeh2KFzBb1CujNAfSKys6GN",
                 txn_signature=txn_sig2,
                 signing_pub_key=pubkey2,
             ),
         ],
         limit_amount=IssuedCurrencyAmount(
             currency="USD",
             issuer="rBPvTKisx7UCGLDtiUZ6mDssXNREuVuL8Y",
             value="10"),
     )
     self.assertEqual(Transaction.from_xrpl(tx), expected)