Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [[Unreleased]]

### Added
- Add a utility function `generate_escrow_cryptoconditions` to generate cryptographic condition and fulfillment for conditional escrows

## [4.0.0] - 2024-12-23

### Added
Expand Down Expand Up @@ -53,9 +56,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rectify the incorrect usage of a transaction flag name: Update `TF_NO_DIRECT_RIPPLE` to `TF_NO_RIPPLE_DIRECT`
- Add the missing `AMMDeposit` Flag `TF_TWO_ASSET_IF_EMPTY`

### Removed:
- Remove Hooks faucet since it's now on the Xahau testnet.

## [2.5.0] - 2023-11-30

### Added
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ websockets = ">=11,<14"
Deprecated = "^1.2.13"
types-Deprecated = "^1.2.9"
pycryptodome = "^3.16.0"
cryptoconditions = "0.8.1"

[tool.poetry.dev-dependencies]
flake8 = "^7.0.0"
Expand Down
40 changes: 40 additions & 0 deletions snippets/send_escrow.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Example of how we can set up an escrow"""

from datetime import datetime
from os import urandom
from time import sleep

from xrpl.account import get_balance
from xrpl.clients import JsonRpcClient
from xrpl.models import AccountObjects, EscrowCreate, EscrowFinish
from xrpl.models.transactions.escrow_create import generate_escrow_cryptoconditions
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Import is correctly structured, but function needs to be exposed

Based on the verification:

  • The generate_escrow_cryptoconditions function is defined in xrpl/models/transactions/escrow_create.py
  • The function is newly added according to the CHANGELOG.md
  • The function is not currently exposed in the package's public API through xrpl/models/transactions/__init__.py

To make this function properly accessible for external usage, it should be:

  1. Imported in xrpl/models/transactions/__init__.py
  2. Added to the __all__ list in the same file
🔗 Analysis chain

Verify import accessibility.
Ensure generate_escrow_cryptoconditions is properly exposed in the package’s __init__.py if needed for external usage.


Let me gather more information about the package structure and imports.


Let me check one more thing to verify if this is a newly added function.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if generate_escrow_cryptoconditions is imported in __init__.py or directly in other modules
rg --heading --context 5 "generate_escrow_cryptoconditions"

Length of output: 2311


Script:

#!/bin/bash
# Check if the function is exposed in __init__.py files
fd "__init__.py" | xargs rg "generate_escrow_cryptoconditions"

# Check the module structure
fd . --type f --extension py | grep -v "test" | grep "xrpl"

Length of output: 8835


Script:

#!/bin/bash
# Check the content of the models/transactions/__init__.py to see if the function is exposed
cat xrpl/models/transactions/__init__.py

Length of output: 6651

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Developers can use the new function as from xrpl.models.transactions.escrow_create import generate_escrow_cryptoconditions.

Is it really necessary to expose this function through the __init__.py file? Would that have any costs in terms of the size of the xrpl-py python package?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ckeshava, exposing functions in __init__.py does not significantly increase package size unless you're re-exporting many large modules. Typically, it’s more about how you want users to discover and import functionality. If you feel comfortable directing developers to escrow_create, you can certainly leave it out of __init__.py. However, adding a single function there will have a negligible impact on package size; it mainly offers discoverability and an implicit statement that this function is part of the “official” public API.

from xrpl.transaction.reliable_submission import submit_and_wait
from xrpl.utils import datetime_to_ripple_time
from xrpl.wallet import generate_faucet_wallet
Expand Down Expand Up @@ -65,3 +67,41 @@
print("Balances of wallets after Escrow was sent:")
print(get_balance(wallet1.address, client))
print(get_balance(wallet2.address, client))

# Setup conditional escrows
cryptoCondition = generate_escrow_cryptoconditions(urandom(32))

create_tx = EscrowCreate(
account=wallet1.address,
destination=wallet2.address,
amount="1000000",
condition=cryptoCondition["condition"],
cancel_after=datetime_to_ripple_time(datetime.now()) + 100,
)

create_escrow_response = submit_and_wait(create_tx, client, wallet1)
print(create_escrow_response)

# Create an AccountObjects request and have the client call it to see if escrow exists
account_objects_request = AccountObjects(account=wallet1.address)
account_objects = (client.request(account_objects_request)).result["account_objects"]

print("Conditional Escrow object exists in wallet1's account:")
print(account_objects)

# Create an EscrowFinish transaction, then sign, autofill, and send it
finish_tx = EscrowFinish(
account=wallet1.address,
owner=wallet1.address,
offer_sequence=create_escrow_response.result["Sequence"],
fulfillment=cryptoCondition["fulfillment"],
condition=cryptoCondition["condition"],
)

submit_and_wait(finish_tx, client, wallet1)

# The fees for EscrowFinish transaction of a conditional escrows are higher.
# Additionally, the fees scale with the reference load on the server
print("Balances of wallets after Escrow was sent:")
print(get_balance(wallet1.address, client))
print(get_balance(wallet2.address, client))
48 changes: 47 additions & 1 deletion tests/unit/models/transactions/test_escrow_create.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from os import urandom
from unittest import TestCase

from xrpl.models.exceptions import XRPLModelException
from xrpl.models.transactions import EscrowCreate
from xrpl.models.transactions import EscrowCreate, EscrowFinish
from xrpl.models.transactions.escrow_create import generate_escrow_cryptoconditions

_OFFER_SEQUENCE = 1
_OWNER = "rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN"


class TestEscrowCreate(TestCase):
Expand All @@ -24,3 +29,44 @@ def test_final_after_less_than_cancel_after(self):
finish_after=finish_after,
sequence=sequence,
)

def test_escrow_condition_and_fulfillment(self):
account = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
amount = "100"
destination = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
fee = "0.00001"
sequence = 19048

# use os.urandom as the source of cryptographic randomness
condition, fulfillment = generate_escrow_cryptoconditions(urandom(32))

EscrowCreate(
account=account,
amount=amount,
destination=destination,
fee=fee,
sequence=sequence,
condition=condition,
)

# EscrowFinish without the fullfillment must throw an error
with self.assertRaises(XRPLModelException):
EscrowFinish(
account=account,
condition=condition,
fee=fee,
sequence=sequence,
offer_sequence=_OFFER_SEQUENCE,
owner=_OWNER,
)

# execute Escrow finish with the fulfillment
EscrowFinish(
account=account,
condition=condition,
fee=fee,
sequence=sequence,
fulfillment=fulfillment,
offer_sequence=_OFFER_SEQUENCE,
owner=_OWNER,
)
36 changes: 35 additions & 1 deletion xrpl/models/transactions/escrow_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from dataclasses import dataclass, field
from typing import Dict, Optional

from typing_extensions import Self
from cryptoconditions import PreimageSha256 # type: ignore
from typing_extensions import Self, TypedDict

from xrpl.models.amounts import Amount
from xrpl.models.required import REQUIRED
Expand Down Expand Up @@ -84,3 +85,36 @@ def _get_errors(self: Self) -> Dict[str, str]:
)

return errors


class CryptoConditions(TypedDict):
"""
A typed-dictionary containing the condition and the fulfillment for
conditional Escrows
"""

condition: str
fulfillment: str


def generate_escrow_cryptoconditions(secret: bytes) -> CryptoConditions:
"""Generate a condition and fulfillment for escrows

Args:
secret: Cryptographic source of randomness used to generate the condition and
fulfillment

Returns:
A pair of condition and fulfillment is returned

"""
fufill = PreimageSha256(preimage=secret)
cond_fulfillment: CryptoConditions = {
"condition": str.upper(fufill.condition_binary.hex()),
"fulfillment": str.upper(fufill.serialize_binary().hex()),
}
return cond_fulfillment
# return {
# "condition": str.upper(fufill.condition_binary.hex()),
# "fulfillment": str.upper(fufill.serialize_binary().hex()),
# }
Loading