Skip to content

Commit

Permalink
Fix: rosetta zero balance accounts tests (near#8833)
Browse files Browse the repository at this point in the history
(My bad, didn't realize that the `S-automerge` tag would merge both directions, so this is a separate PR.)

Also a bit of clean-up w.r.t. magic numbers.

Implicit account test no longer has to worry about the `LIQUID_BALANCE_FOR_STORAGE` subaccount.
  • Loading branch information
encody authored and nikurt committed Apr 5, 2023
1 parent ef4ae2b commit d5ed2de
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 77 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

14 changes: 7 additions & 7 deletions chain/rosetta-rpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "near-rosetta-rpc"
version = "0.0.0"
authors.workspace = true
publish = false
edition.workspace = true
name = "near-rosetta-rpc"
publish = false
version = "0.0.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -24,15 +24,15 @@ thiserror.workspace = true
tokio.workspace = true
validator.workspace = true

near-primitives.workspace = true
near-account-id.workspace = true
near-crypto.workspace = true
near-chain-configs.workspace = true
near-client.workspace = true
near-client-primitives.workspace = true
near-client.workspace = true
near-crypto.workspace = true
near-network.workspace = true
near-o11y.workspace = true

near-primitives.workspace = true
node-runtime.workspace = true

[dev-dependencies]
insta.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion chain/rosetta-rpc/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ where

/// Zero-balance account (NEP-448)
fn is_zero_balance_account(account: &near_primitives::account::Account) -> bool {
account.storage_usage() <= 770
account.storage_usage() <= node_runtime::ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT
}

/// Tokens not locked due to staking (=liquid) but reserved for state.
Expand Down
218 changes: 149 additions & 69 deletions pytest/tests/sanity/rosetta.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
TxIdentifier = typing.Union[str, JsonDict]


def account_identifier(account_id: str) -> JsonDict:
return {'address': account_id}


def block_identifier(block_id: BlockIdentifier) -> JsonDict:
if isinstance(block_id, int):
return {'index': block_id}
Expand Down Expand Up @@ -261,6 +265,36 @@ def transfer(self, *, src: key.Key, dst: key.Key, amount: int,
},
}, **kw)

def add_full_access_key(self, account: key.Key, public_key_hex: str,
**kw) -> RosettaExecResult:
return self.exec_operations(
account, {
"operation_identifier": {
"index": 0
},
"type": "INITIATE_ADD_KEY",
"account": {
"address": account.account_id
}
}, {
"operation_identifier": {
"index": 1
},
"related_operations": [{
"index": 0
}],
"type": "ADD_KEY",
"account": {
"address": account.account_id
},
"metadata": {
"public_key": {
"hex_bytes": public_key_hex,
"curve_type": "edwards25519"
}
}
}, **kw)

def delete_account(self, account: key.Key, refund_to: key.Key,
**kw) -> RosettaExecResult:
return self.exec_operations(
Expand Down Expand Up @@ -325,6 +359,11 @@ def get_transaction(self, *, block_id: BlockIdentifier,
transaction_identifier=tx_identifier(tx_id))
return res['transaction']

def get_account_balances(self, *, account_id: str) -> JsonDict:
res = self.rpc('/account/balance',
account_identifier=account_identifier(account_id))
return res['balances']


class RosettaTestCase(unittest.TestCase):
node = None
Expand All @@ -350,6 +389,109 @@ def setUpClass(cls) -> None:
def tearDownClass(cls) -> None:
cls.node.cleanup()

def test_zero_balance_account(self) -> None:
"""Tests storage staking requirements for low-storage accounts.
Creates an implicit account by sending it 1 yoctoNEAR (not enough to
cover storage). However, the zero-balance allowance established in
NEP-448 should cover the storage staking requirement. Then, we
transfer 10**22 yoctoNEAR to the account, which should be enough to
cover the storage staking requirement for the 6 full-access keys we
then add to the account, exceeding the zero-balance account allowance.
"""

test_amount = 10**22
key_space_cost = 41964925000000000000
validator = self.node.validator_key
implicit = key.Key.implicit_account()

# first transfer 1 yoctoNEAR to create the account
# not enough to cover storage, but the zero-balance allowance should cover it
result = self.rosetta.transfer(src=validator, dst=implicit, amount=1)

block = result.block()
tx = result.transaction()
json_res = self.node.get_tx(result.near_hash, implicit.account_id)
json_res = json_res['result']
receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids']
receipt_id = {'hash': 'receipt:' + receipt_ids[0]}

# Fetch the receipt through Rosetta RPC.
result = RosettaExecResult(self.rosetta, block, receipt_id)
related = result.related(0)

balances = self.rosetta.get_account_balances(
account_id=implicit.account_id)

# even though 1 yoctoNEAR is not enough to cover the storage cost,
# since the account should be consuming less than 770 bytes of storage,
# it should be allowed nonetheless.
self.assertEqual(balances, [{
'value': '1',
'currency': {
'symbol': 'NEAR',
'decimals': 24
}
}])

# transfer the rest of the amount
result = self.rosetta.transfer(src=validator,
dst=implicit,
amount=(test_amount - 1))

block = result.block()
tx = result.transaction()
json_res = self.node.get_tx(result.near_hash, implicit.account_id)
json_res = json_res['result']
receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids']
receipt_id = {'hash': 'receipt:' + receipt_ids[0]}

# Fetch the receipt through Rosetta RPC.
result = RosettaExecResult(self.rosetta, block, receipt_id)
related = result.related(0)

balances = self.rosetta.get_account_balances(
account_id=implicit.account_id)

self.assertEqual(balances, [{
'value': str(test_amount),
'currency': {
'symbol': 'NEAR',
'decimals': 24
}
}])

# add 6 keys to go over the zero-balance account free storage allowance
public_keys_hex = [
"17595386a67d36afc73872e60916f83217d789dc60b5d037563998e6651111cf",
"7940aac79a425f194621ab5c4e38b7841dddae90b20eaf28f7f78caec911bcf4",
"0554fffef36614d7c49b3088c4c1fb66613ff05fb30927b582b43aed0b25b549",
"09d36e25c5a3ac440a798252982dd92b67d8de60894df3177cb4ff30a890cafd",
"e0ca119be7211f3dfed1768fc9ab235b6af06a205077ef23166dd1cbfd2ac7fc",
"98f1a49296fb7156980d325a25e1bfeb4f123dd98c90fa0492699c55387f7ef3",
]
for pk in public_keys_hex:
result = self.rosetta.add_full_access_key(implicit, pk)

block = result.block()
tx = result.transaction()
json_res = self.node.get_tx(result.near_hash, implicit.account_id)
json_res = json_res['result']
receipt_ids = json_res['transaction_outcome']['outcome'][
'receipt_ids']
receipt_id = {'hash': 'receipt:' + receipt_ids[0]}

# Fetch the receipt through Rosetta RPC.
result = RosettaExecResult(self.rosetta, block, receipt_id)
related = result.related(0)

balances = self.rosetta.get_account_balances(
account_id=implicit.account_id)

# no longer a zero-balance account
self.assertEqual(test_amount - key_space_cost * len(public_keys_hex),
int(balances[0]['value']))

def test_get_block(self) -> None:
"""Tests getting blocks and transactions.
Expand All @@ -375,32 +517,13 @@ def test_get_block(self) -> None:
'decimals': 24,
'symbol': 'NEAR'
},
'value': '999999999998180000000000000000000'
'value': '1000000000000000000000000000000000'
},
'operation_identifier': {
'index': 0
},
'status': 'SUCCESS',
'type': 'TRANSFER'
}, {
'account': {
'address': 'near',
'sub_account': {
'address': 'LIQUID_BALANCE_FOR_STORAGE'
}
},
'amount': {
'currency': {
'decimals': 24,
'symbol': 'NEAR'
},
'value': '1820000000000000000000'
},
'operation_identifier': {
'index': 1
},
'status': 'SUCCESS',
'type': 'TRANSFER'
}, {
'account': {
'address': 'test0'
Expand All @@ -413,7 +536,7 @@ def test_get_block(self) -> None:
'value': '950000000000000000000000000000000'
},
'operation_identifier': {
'index': 2
'index': 1
},
'status': 'SUCCESS',
'type': 'TRANSFER'
Expand All @@ -432,7 +555,7 @@ def test_get_block(self) -> None:
'value': '50000000000000000000000000000000'
},
'operation_identifier': {
'index': 3
'index': 2
},
'status': 'SUCCESS',
'type': 'TRANSFER'
Expand Down Expand Up @@ -567,7 +690,7 @@ def test_implicit_account(self) -> None:
validator = self.node.validator_key
implicit = key.Key.implicit_account()

### 1. Create implicit account.
# 1. Create implicit account.
logger.info(f'Creating implicit account: {implicit.account_id}')
result = self.rosetta.transfer(src=validator,
dst=implicit,
Expand Down Expand Up @@ -666,31 +789,7 @@ def test_implicit_account(self) -> None:
'address': implicit.account_id,
},
'amount': {
'value': '8180000000000000000000',
'currency': {
'symbol': 'NEAR',
'decimals': 24
}
}
}, {
'operation_identifier': {
'index': 1
},
'type': 'TRANSFER',
'status': 'SUCCESS',
'metadata': {
'predecessor_id': {
'address': 'test0'
}
},
'account': {
'address': implicit.account_id,
'sub_account': {
'address': 'LIQUID_BALANCE_FOR_STORAGE'
}
},
'amount': {
'value': '1820000000000000000000',
'value': '10000000000000000000000',
'currency': {
'symbol': 'NEAR',
'decimals': 24
Expand Down Expand Up @@ -738,7 +837,7 @@ def test_implicit_account(self) -> None:
'transaction_identifier': related.identifier
}, related.transaction())

### 2. Delete the account.
# 2. Delete the account.
logger.info(f'Deleting implicit account: {implicit.account_id}')
result = self.rosetta.delete_account(implicit, refund_to=validator)

Expand Down Expand Up @@ -808,32 +907,13 @@ def test_implicit_account(self) -> None:
'decimals': 24,
'symbol': 'NEAR'
},
'value': '-8128890300000000000000'
'value': '-9948890300000000000000'
},
'operation_identifier': {
'index': 0
},
'status': 'SUCCESS',
'type': 'TRANSFER'
}, {
'account': {
'address': implicit.account_id,
'sub_account': {
'address': 'LIQUID_BALANCE_FOR_STORAGE'
}
},
'amount': {
'currency': {
'decimals': 24,
'symbol': 'NEAR'
},
'value': '-1820000000000000000000'
},
'operation_identifier': {
'index': 1
},
'status': 'SUCCESS',
'type': 'TRANSFER'
}],
'related_transactions': [{
'direction': 'forward',
Expand Down

0 comments on commit d5ed2de

Please sign in to comment.