Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Batch amendment #757

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8c1f795
update definitions.json
mvadari Oct 2, 2024
ea28bc4
update scripts after rippled refactor
mvadari Oct 2, 2024
dbedfdc
add LedgerStateFix
mvadari Oct 2, 2024
703f4c8
add basic batch models
mvadari Oct 2, 2024
429d6fc
add autofill
mvadari Oct 3, 2024
2677584
add autofill tests
mvadari Oct 3, 2024
b79c7f7
fix multisign issue, add test
mvadari Oct 3, 2024
2443f4c
rename some tests
mvadari Oct 10, 2024
3fa2294
fix multisign so tests pass
mvadari Oct 10, 2024
5a000f7
improve typing
mvadari Oct 10, 2024
76d3666
improve tests, fix more issues
mvadari Oct 10, 2024
5e7bc74
more cleanup
mvadari Oct 10, 2024
68ba657
more typing improvements
mvadari Oct 10, 2024
7aae9d8
update changelog
mvadari Oct 10, 2024
786e156
add binary codec batch encoding
mvadari Oct 10, 2024
1a8939d
add multi-account batch signing helper function
mvadari Oct 10, 2024
c5ea66c
add batch signer combine function
mvadari Oct 10, 2024
41fbe40
move to transaction
mvadari Oct 10, 2024
eb0d88b
add tests, fix issues
mvadari Oct 10, 2024
ca6caec
fix typing issue
mvadari Oct 10, 2024
d3da22a
fix tests
mvadari Oct 10, 2024
c441795
Update main.py
mvadari Oct 10, 2024
a2dca0e
Merge branch 'main' into batch
mvadari Oct 25, 2024
2378081
Merge branch 'main' into batch
mvadari Nov 4, 2024
27a822f
Merge branch 'main' into batch
mvadari Nov 7, 2024
d2119e1
remove BatchTxn field
mvadari Nov 7, 2024
f9cbea7
fix tests
mvadari Nov 7, 2024
7d842c6
fix unrelated TicketSequence bug
mvadari Nov 7, 2024
2a632cf
improve autofilling
mvadari Nov 7, 2024
6f1cbed
better flag handling
mvadari Nov 7, 2024
8ca1005
update changelog
mvadari Nov 7, 2024
77fc6e1
fix integration tests
mvadari Nov 7, 2024
0792b3e
handle batch in batch
mvadari Nov 7, 2024
a2890a9
get working integration test
mvadari Nov 8, 2024
6ff9c60
Merge branch 'main' into batch
mvadari Dec 11, 2024
f29bc15
Merge branch 'main' into batch
mvadari Dec 13, 2024
be36119
Merge branch 'main' into batch
mvadari Dec 20, 2024
4157762
Merge branch 'main' into batch
mvadari Jan 2, 2025
a637da3
rename field
mvadari Jan 2, 2025
8be504d
more renames
mvadari Jan 2, 2025
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
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
"aiounittest",
"altnet",
"asyncio",
"autofilling",
"autofills",
"binarycodec",
"Clawback",
"isnumeric",
"keypair",
"keypairs",
"multiaccount",
"multisign",
"multisigned",
"multisigning",
"nftoken",
"PATHSET",
"rippletest",
Expand All @@ -27,5 +31,8 @@
"source.organizeImports": "always"
}
},
"isort.args": ["--profile", "black"]
"isort.args": [
"--profile",
"black"
]
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### BREAKING CHANGE:
- Remove Python 3.7 support to fix dependency installation and use 3.8 as new default.

### Added
- Support for the `Batch` amendment (XLS-56d).

### Fixed
- Grab the FeeSettings values from the latest validated ledger. Remove hard-coded reference to 10 drops as the reference transaction cost.
- Handle autofilling better when multisigning transactions.
- Better typing for transaction-related helper functions.
- Better handling of `TicketSequence`.

## [3.0.0] - 2024-07-16

Expand Down Expand Up @@ -96,6 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.0.0] - 2023-07-05
### BREAKING CHANGE
- The default signing algorithm in the `Wallet` was changed from secp256k1 to ed25519

### Added:
- Wallet support for regular key compatibility
- Added new ways of wallet generation: `from_seed`, `from_secret`, `from_entropy`, `from_secret_numbers`
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/it_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ async def fund_wallet_async(
destination=wallet.address,
amount=FUNDING_AMOUNT,
)
await sign_and_submit_async(payment, client, MASTER_WALLET, check_fee=True)
await sign_and_submit_async(
payment, client, MASTER_WALLET, autofill=True, check_fee=True
)
await client.request(LEDGER_ACCEPT_REQUEST)


Expand Down
50 changes: 45 additions & 5 deletions tests/integration/sugar/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,33 @@
)
from tests.integration.reusable_values import DESTINATION as DESTINATION_WALLET
from tests.integration.reusable_values import WALLET
from xrpl.asyncio.account import get_next_valid_seq_number
from xrpl.asyncio.ledger import get_fee, get_latest_validated_ledger_sequence
from xrpl.asyncio.transaction import (
XRPLReliableSubmissionException,
autofill,
autofill_and_sign,
sign,
sign_and_submit,
)
from xrpl.asyncio.transaction import submit as submit_transaction_alias_async
from xrpl.asyncio.transaction import submit_and_wait
from xrpl.asyncio.transaction.main import (
_calculate_fee_per_transaction_type,
sign_and_submit,
)
from xrpl.asyncio.transaction.main import _calculate_fee_per_transaction_type
from xrpl.clients import XRPLRequestFailureException
from xrpl.core.addresscodec import classic_address_to_xaddress
from xrpl.core.binarycodec.main import encode
from xrpl.models.amounts.issued_currency_amount import IssuedCurrencyAmount
from xrpl.models.exceptions import XRPLException
from xrpl.models.requests import ServerState, Tx
from xrpl.models.transactions import AccountDelete, AccountSet, EscrowFinish, Payment
from xrpl.models.transactions import (
AccountDelete,
AccountSet,
Batch,
DepositPreauth,
EscrowFinish,
Payment,
TransactionFlag,
)
from xrpl.utils import xrp_to_drops

ACCOUNT = WALLET.address
Expand Down Expand Up @@ -254,6 +261,39 @@ async def test_networkid_reserved_networks(self, client):
self.assertIsNone(transaction.network_id)
self.assertEqual(client.network_id, 1)

@test_async_and_sync(
globals(),
["xrpl.transaction.autofill", "xrpl.account.get_next_valid_seq_number"],
)
async def test_batch_autofill(self, client):
tx = Batch(
account="rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
raw_transactions=[
DepositPreauth(
account=WALLET.address,
authorize="rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
),
DepositPreauth(
account=WALLET.address,
authorize="rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
),
],
)
transaction = await autofill(tx, client)
self.assertEqual(len(transaction.transaction_ids), 2)

sequence = await get_next_valid_seq_number(WALLET.address, client)
for i in range(len(transaction.raw_transactions)):
raw_tx = transaction.raw_transactions[i]
self.assertTrue(raw_tx.has_flag(TransactionFlag.TF_INNER_BATCH_TXN))
self.assertEqual(raw_tx.sequence, sequence + i)
self.assertEqual(raw_tx.get_hash(), transaction.transaction_ids[i])
self.assertIsNone(raw_tx.network_id)
self.assertIsNone(raw_tx.last_ledger_sequence)
self.assertEqual(raw_tx.fee, "0")
self.assertEqual(raw_tx.signing_pub_key, "")
self.assertEqual(raw_tx.txn_signature, "")


class TestSubmitAndWait(IntegrationTestCase):
@test_async_and_sync(
Expand Down
26 changes: 26 additions & 0 deletions tests/integration/transactions/test_batch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
sign_and_reliable_submission_async,
test_async_and_sync,
)
from tests.integration.reusable_values import DESTINATION, WALLET
from xrpl.models import Batch, BatchFlag, Payment
from xrpl.models.response import ResponseStatus


class TestBatch(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_basic_functionality(self, client):
payment = Payment(
account=WALLET.address,
amount="1",
destination=DESTINATION.address,
)
batch = Batch(
account=WALLET.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[payment, payment],
)
response = await sign_and_reliable_submission_async(batch, WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")
27 changes: 27 additions & 0 deletions tests/unit/core/binarycodec/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
encode,
encode_for_multisigning,
encode_for_signing,
encode_for_signing_batch,
encode_for_signing_claim,
)

Expand Down Expand Up @@ -401,6 +402,32 @@ def test_claim(self):
)
self.assertEqual(encode_for_signing_claim(json), expected)

def test_batch(self):
flags = 1
transaction_ids = [
"ABE4871E9083DF66727045D49DEEDD3A6F166EB7F8D1E92FE868F02E76B2C5CA",
"795AAC88B59E95C3497609749127E69F12958BC016C600C770AEEB1474C840B4",
]

json = {"flags": flags, "transaction_ids": transaction_ids}
actual = encode_for_signing_batch(json)
self.assertEqual(
actual,
"".join(
[
# hash prefix
"42434800",
# flags
"00000001",
# transaction_ids length
"00000002",
# transaction_ids
"ABE4871E9083DF66727045D49DEEDD3A6F166EB7F8D1E92FE868F02E76B2C5CA",
"795AAC88B59E95C3497609749127E69F12958BC016C600C770AEEB1474C840B4",
]
),
)

def test_multisig(self):
signing_account = "rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN"
multisig_json = {**signing_json, "SigningPubKey": ""}
Expand Down
43 changes: 30 additions & 13 deletions tests/unit/models/transactions/test_transaction.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from unittest import TestCase

from xrpl.asyncio.transaction.main import sign
from xrpl.core.addresscodec.main import classic_address_to_xaddress
from xrpl.models.exceptions import XRPLModelException
from xrpl.models.transactions import AccountSet, OfferCreate, Payment
from xrpl.models.transactions import AccountSet, DepositPreauth, OfferCreate, Payment
from xrpl.models.transactions.transaction import Transaction
from xrpl.models.transactions.types.transaction_type import TransactionType
from xrpl.transaction.multisign import multisign
Expand Down Expand Up @@ -158,12 +159,28 @@ def test_is_signed_for_multisigned_transaction(self):
multisigned_tx = multisign(tx, [tx_1, tx_2])
self.assertTrue(multisigned_tx.is_signed())

def test_multisigned_transaction_xaddress(self):
tx = DepositPreauth(
account=classic_address_to_xaddress(_WALLET.address, 1, False),
authorize=classic_address_to_xaddress(_ACCOUNT, 1, False),
)
tx_1 = sign(tx, _FIRST_SIGNER, multisign=True)
tx_2 = sign(tx, _SECOND_SIGNER, multisign=True)

for tx_signed in (tx_1, tx_2):
self.assertEqual(tx_signed.account, _WALLET.address)
self.assertEqual(tx_signed.source_tag, 1)
self.assertEqual(tx_signed.authorize, _ACCOUNT)

multisigned_tx = multisign(tx, [tx_1, tx_2])
self.assertTrue(multisigned_tx.is_signed())

# test the usage of DeliverMax field in Payment transactions
def test_payment_txn_API_no_deliver_max(self):
def test_payment_txn_api_no_deliver_max(self):
delivered_amount = "200000"
payment_tx_json = {
"Account": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e",
"Destination": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ",
"Account": _WALLET.address,
"Destination": _ACCOUNT,
"TransactionType": "Payment",
"Amount": delivered_amount,
"Fee": "15",
Expand All @@ -175,11 +192,11 @@ def test_payment_txn_API_no_deliver_max(self):
payment_txn = Payment.from_xrpl(payment_tx_json)
self.assertEqual(delivered_amount, payment_txn.to_dict()["amount"])

def test_payment_txn_API_no_amount(self):
def test_payment_txn_api_no_amount(self):
delivered_amount = "200000"
payment_tx_json = {
"Account": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e",
"Destination": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ",
"Account": _WALLET.address,
"Destination": _ACCOUNT,
"TransactionType": "Payment",
"DeliverMax": delivered_amount,
"Fee": "15",
Expand All @@ -191,10 +208,10 @@ def test_payment_txn_API_no_amount(self):
payment_txn = Payment.from_xrpl(payment_tx_json)
self.assertEqual(delivered_amount, payment_txn.to_dict()["amount"])

def test_payment_txn_API_different_amount_and_deliver_max(self):
def test_payment_txn_api_different_amount_and_deliver_max(self):
payment_tx_json = {
"Account": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e",
"Destination": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ",
"Account": _WALLET.address,
"Destination": _ACCOUNT,
"TransactionType": "Payment",
"DeliverMax": "200000",
"Amount": "200010",
Expand All @@ -207,11 +224,11 @@ def test_payment_txn_API_different_amount_and_deliver_max(self):
with self.assertRaises(XRPLModelException):
Payment.from_xrpl(payment_tx_json)

def test_payment_txn_API_identical_amount_and_deliver_max(self):
def test_payment_txn_api_identical_amount_and_deliver_max(self):
delivered_amount = "200000"
payment_tx_json = {
"Account": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e",
"Destination": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ",
"Account": _WALLET.address,
"Destination": _ACCOUNT,
"TransactionType": "Payment",
"DeliverMax": delivered_amount,
"Amount": delivered_amount,
Expand Down
Empty file.
Loading
Loading