-
Notifications
You must be signed in to change notification settings - Fork 105
Implement account permission delegation - XLS-74d and XLS-75d amendments #840
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
Changes from 15 commits
d2ee762
63f7dd3
e42af9f
4b1491b
1d4b3d8
cf31824
b8deada
190b666
9d15f54
5b16990
f2a496d
3626419
d1ebbaa
c36fa12
0c900a9
c536ef5
b70dbfd
0a85fa1
88d4b8a
13f3eaa
30cc5fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
from tests.integration.integration_test_case import IntegrationTestCase | ||
from tests.integration.it_utils import ( | ||
fund_wallet_async, | ||
sign_and_reliable_submission_async, | ||
test_async_and_sync, | ||
) | ||
from xrpl.models.requests import LedgerData, LedgerEntry, LedgerEntryType | ||
from xrpl.models.requests.ledger_entry import Delegate | ||
from xrpl.models.response import ResponseStatus | ||
from xrpl.models.transactions import ( | ||
AccountSet, | ||
DelegateSet, | ||
GranularPermission, | ||
Payment, | ||
) | ||
from xrpl.models.transactions.delegate_set import Permission | ||
from xrpl.models.transactions.types import TransactionType | ||
from xrpl.utils import xrp_to_drops | ||
from xrpl.wallet.main import Wallet | ||
|
||
|
||
class TestDelegateSet(IntegrationTestCase): | ||
@test_async_and_sync(globals()) | ||
async def test_delegation_with_no_permission(self, client): | ||
# Note: Using WALLET, DESTINATION accounts could pollute the test results | ||
alice = Wallet.create() | ||
await fund_wallet_async(alice) | ||
bob = Wallet.create() | ||
await fund_wallet_async(bob) | ||
carol = Wallet.create() | ||
await fund_wallet_async(carol) | ||
|
||
# Use bob's account to execute a transaction on behalf of alice | ||
payment = Payment( | ||
account=alice.address, | ||
amount=xrp_to_drops(1), | ||
destination=carol.address, | ||
delegate=bob.address, | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
payment, bob, client, check_fee=False | ||
) | ||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
|
||
# The lack of AccountPermissionSet transaction will result in a tecNO_PERMISSION | ||
self.assertEqual(response.result["engine_result"], "tecNO_PERMISSION") | ||
|
||
@test_async_and_sync(globals()) | ||
async def test_delegate_set_workflow(self, client): | ||
# Note: Using WALLET, DESTINATION accounts could pollute the test results | ||
alice = Wallet.create() | ||
await fund_wallet_async(alice) | ||
bob = Wallet.create() | ||
await fund_wallet_async(bob) | ||
carol = Wallet.create() | ||
await fund_wallet_async(carol) | ||
|
||
delegate_set = DelegateSet( | ||
account=alice.address, | ||
authorize=bob.address, | ||
# Authorize bob account to execute Payment transactions and | ||
# modify the domain of an account behalf of alice's account. | ||
permissions=[ | ||
Permission(permission_value=TransactionType.PAYMENT), | ||
Permission(permission_value=GranularPermission.ACCOUNT_DOMAIN_SET), | ||
], | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
delegate_set, alice, client, check_fee=False | ||
) | ||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
self.assertEqual(response.result["engine_result"], "tesSUCCESS") | ||
|
||
# Use the bob's account to execute a transaction on behalf of alice | ||
payment = Payment( | ||
account=alice.address, | ||
amount=xrp_to_drops(1), | ||
destination=carol.address, | ||
delegate=bob.address, | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
payment, bob, client, check_fee=False | ||
) | ||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
self.assertEqual(response.result["engine_result"], "tesSUCCESS") | ||
|
||
# Validate that the transaction was signed by bob | ||
self.assertEqual(response.result["tx_json"]["Account"], alice.address) | ||
self.assertEqual(response.result["tx_json"]["Delegate"], bob.address) | ||
self.assertEqual(response.result["tx_json"]["SigningPubKey"], bob.public_key) | ||
|
||
# Use the bob's account to execute a transaction on behalf of alice | ||
account_set = AccountSet( | ||
account=alice.address, | ||
delegate=bob.address, | ||
email_hash="10000000002000000000300000000012", | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
account_set, bob, client, check_fee=False | ||
) | ||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
self.assertEqual(response.result["engine_result"], "tecNO_PERMISSION") | ||
|
||
@test_async_and_sync(globals()) | ||
async def test_fetch_delegate_ledger_entry(self, client): | ||
# Note: Using WALLET, DESTINATION accounts could pollute the test results | ||
alice = Wallet.create() | ||
await fund_wallet_async(alice) | ||
bob = Wallet.create() | ||
await fund_wallet_async(bob) | ||
|
||
delegate_set = DelegateSet( | ||
account=alice.address, | ||
authorize=bob.address, | ||
# Authorize bob's account to execute Payment transactions | ||
# and authorize a trustline on behalf of alice's account. | ||
permissions=[ | ||
Permission(permission_value=TransactionType.PAYMENT), | ||
Permission(permission_value=GranularPermission.TRUSTLINE_AUTHORIZE), | ||
], | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
delegate_set, alice, client, check_fee=False | ||
) | ||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
self.assertEqual(response.result["engine_result"], "tesSUCCESS") | ||
|
||
ledger_entry_response = await client.request( | ||
LedgerEntry( | ||
delegate=Delegate( | ||
account=alice.address, | ||
authorize=bob.address, | ||
), | ||
) | ||
) | ||
self.assertTrue(ledger_entry_response.is_successful()) | ||
self.assertEqual( | ||
ledger_entry_response.result["node"]["LedgerEntryType"], | ||
"Delegate", | ||
) | ||
self.assertEqual(ledger_entry_response.result["node"]["Account"], alice.address) | ||
self.assertEqual(ledger_entry_response.result["node"]["Authorize"], bob.address) | ||
self.assertEqual(len(ledger_entry_response.result["node"]["Permissions"]), 2) | ||
|
||
@test_async_and_sync(globals()) | ||
async def test_fetch_delegate_ledger_data(self, client): | ||
# Note: Using WALLET, DESTINATION accounts could pollute the test results | ||
alice = Wallet.create() | ||
await fund_wallet_async(alice) | ||
bob = Wallet.create() | ||
await fund_wallet_async(bob) | ||
|
||
delegate_set = DelegateSet( | ||
account=alice.address, | ||
authorize=bob.address, | ||
# Authorize bob's account to execute Payment transactions | ||
# and authorize a trustline on behalf of alice's account. | ||
permissions=[ | ||
Permission(permission_value=TransactionType.PAYMENT), | ||
Permission(permission_value=GranularPermission.TRUSTLINE_AUTHORIZE), | ||
], | ||
) | ||
response = await sign_and_reliable_submission_async( | ||
delegate_set, alice, client, check_fee=False | ||
) | ||
|
||
self.assertEqual(response.status, ResponseStatus.SUCCESS) | ||
self.assertEqual(response.result["engine_result"], "tesSUCCESS") | ||
|
||
ledger_index = "validated" | ||
marker = None | ||
granted_permission = set() | ||
while True: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as in JS - why is this necessary? Much more efficient to search via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mvadari I wanted to test for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this test is worth it - it'll take a while and be pretty inefficient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, removed |
||
ledger_data_response = await client.request( | ||
LedgerData( | ||
type=LedgerEntryType.DELEGATE, | ||
ledger_index=ledger_index, | ||
marker=marker, | ||
) | ||
) | ||
for entry in ledger_data_response.result["state"]: | ||
if ( | ||
entry["Account"] == alice.address | ||
and entry["Authorize"] == bob.address | ||
): | ||
granted_permission.add( | ||
entry["Permissions"][0]["Permission"]["PermissionValue"] | ||
) | ||
granted_permission.add( | ||
entry["Permissions"][1]["Permission"]["PermissionValue"] | ||
) | ||
if "marker" not in ledger_data_response.result: | ||
break | ||
marker = ledger_data_response.result["marker"] | ||
ledger_index = ledger_data_response.result["ledger_index"] | ||
|
||
self.assertEqual(len(granted_permission), 2) | ||
self.assertTrue(TransactionType.PAYMENT.value in granted_permission) | ||
self.assertTrue( | ||
GranularPermission.TRUSTLINE_AUTHORIZE.value in granted_permission | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
[ | ||
{ | ||
"json": { | ||
"Account": "raD5qJMAShLeHZXf9wjUmo6vRK4arj9cF3", | ||
"Fee": "10", | ||
"Flags": 0, | ||
"Sequence": 103929, | ||
"SigningPubKey": | ||
"028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166", | ||
"TakerGets": { | ||
"value": "1694.768", | ||
"currency": "ILS", | ||
"issuer": "rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9" | ||
}, | ||
"TakerPays": "98957503520", | ||
"TransactionType": "OfferCreate", | ||
"TxnSignature": "304502202ABE08D5E78D1E74A4C18F2714F64E87B8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C0591CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C" | ||
}, | ||
"buffer": "120007220000000024000195F964400000170A53AC2065D5460561EC9DE000000000000000000000000000494C53000000000092D705968936C419CE614BF264B5EEB1CEA47FF468400000000000000A7321028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A71667447304502202ABE08D5E78D1E74A4C18F2714F64E87B8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C0591CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C811439408A69F0895E62149CFCC006FB89FA7D1E6E5D" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To match the other fixture files, this field should be called |
||
}, | ||
{ | ||
"json": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be added to the other fixtures file, not this one - |
||
"Account": "rMryaYXZMchTWBovAzEsMzid9FUwmrmcH7", | ||
"Authorize": "r4Vp2qvKR59guHDn4Yzw9owTzDVnt6TJZA", | ||
"Fee": "200", | ||
"Flags": 2147483648, | ||
"NetworkID": 63456, | ||
"Permissions": [ | ||
{ | ||
"Permission": { | ||
"PermissionValue": "Payment" | ||
} | ||
}, | ||
{ | ||
"Permission": { | ||
"PermissionValue": "TrustSet" | ||
} | ||
} | ||
], | ||
"Sequence": 2328, | ||
"SigningPubKey": "ED510865F867CDFCB944D435812ACF23D231E5C14534B146BCE46A2F794D198B77", | ||
"TransactionType": "DelegateSet", | ||
"TxnSignature": "D05A89D0B489DEC1CECBE0D33BA656C929CDCCC75D4D41B282B378544975B87A70C3E42147D980D1F6E2E4DC6316C99D7E2D4F6335F147C71C0DAA0D6516150D" | ||
}, | ||
"buffer": "120040210000F7E0228000000024000009186840000000000000C87321ED510865F867CDFCB944D435812ACF23D231E5C14534B146BCE46A2F794D198B777440D05A89D0B489DEC1CECBE0D33BA656C929CDCCC75D4D41B282B378544975B87A70C3E42147D980D1F6E2E4DC6316C99D7E2D4F6335F147C71C0DAA0D6516150D8114DB9157872FA63FAF7432CD300911A43B981B85A28514EBA79C385B47C50D52445DF2676EEC0231F732CEF01DEF203400000001E1EF203400000015E1F1" | ||
} | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: newline at the end of the file |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,37 @@ | ||
import json | ||
import os | ||
from unittest import TestCase | ||
|
||
from xrpl.core.binarycodec.binary_wrappers.binary_parser import BinaryParser | ||
from xrpl.core.binarycodec.types.st_object import STObject | ||
|
||
expected_json = { | ||
"Account": "raD5qJMAShLeHZXf9wjUmo6vRK4arj9cF3", | ||
"Fee": "10", | ||
"Flags": 0, | ||
"Sequence": 103929, | ||
"SigningPubKey": ( | ||
"028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166" | ||
), | ||
"TakerGets": { | ||
"value": "1694.768", | ||
"currency": "ILS", | ||
"issuer": "rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9", | ||
}, | ||
"TakerPays": "98957503520", | ||
"TransactionType": "OfferCreate", | ||
"TxnSignature": ( | ||
"304502202ABE08D5E78D1E74A4C18F2714F64E87B8BD57444AFA5733" | ||
"109EB3C077077520022100DB335EE97386E4C0591CAC024D50E9230D8" | ||
"F171EEB901B5E5E4BD6D1E0AEF98C" | ||
), | ||
} | ||
|
||
buffer = ( | ||
"120007220000000024000195F964400000170A53AC2065D5460561E" | ||
"C9DE000000000000000000000000000494C53000000000092D70596" | ||
"8936C419CE614BF264B5EEB1CEA47FF468400000000000000A73210" | ||
"28472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F" | ||
"418D6A71667447304502202ABE08D5E78D1E74A4C18F2714F64E87B" | ||
"8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C059" | ||
"1CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C811439408" | ||
"A69F0895E62149CFCC006FB89FA7D1E6E5D" | ||
) | ||
_FILENAME = "../fixtures/data/serialized-dict-fixtures.json" | ||
dirname = os.path.dirname(__file__) | ||
absolute_path = os.path.join(dirname, _FILENAME) | ||
with open(absolute_path) as data_driven_tests: | ||
_FIXTURES_JSON = json.load(data_driven_tests) | ||
|
||
|
||
class TestSTObject(TestCase): | ||
maxDiff = 1000 | ||
|
||
def test_from_value(self): | ||
transaction = STObject.from_value(expected_json) | ||
self.assertEqual(buffer, str(transaction).upper()) | ||
for test in _FIXTURES_JSON: | ||
Patel-Raj11 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expected_json = test["json"] | ||
buffer = test["buffer"] | ||
transaction = STObject.from_value(expected_json) | ||
self.assertEqual(buffer, str(transaction).upper()) | ||
|
||
def test_from_value_to_json(self): | ||
transaction = STObject.from_value(expected_json) | ||
self.assertEqual(transaction.to_json(), expected_json) | ||
for test in _FIXTURES_JSON: | ||
expected_json = test["json"] | ||
transaction = STObject.from_value(expected_json) | ||
self.assertEqual(transaction.to_json(), expected_json) | ||
|
||
def test_from_parser_to_json(self): | ||
parser = BinaryParser(buffer) | ||
transaction = STObject.from_parser(parser) | ||
self.assertEqual(transaction.to_json(), expected_json) | ||
for test in _FIXTURES_JSON: | ||
expected_json = test["json"] | ||
buffer = test["buffer"] | ||
parser = BinaryParser(buffer) | ||
transaction = STObject.from_parser(parser) | ||
self.assertEqual(transaction.to_json(), expected_json) |
Uh oh!
There was an error while loading. Please reload this page.