From 1ed031b0c8e6f25c2117796a2613aef690b6a48b Mon Sep 17 00:00:00 2001 From: Nikki Kang Date: Fri, 12 Jan 2024 01:13:01 -0800 Subject: [PATCH 1/2] allow dashboard wallet user tx to be signed w/ message containing user handle --- ...st_dashboard_wallet_user_entity_manager.py | 79 +++++++++++++++++-- .../src/api/v1/models/users.py | 11 --- .../entities/dashboard_wallet_user.py | 33 +++++--- 3 files changed, 97 insertions(+), 26 deletions(-) diff --git a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py index d0845b55cf8..6e47fdc0246 100644 --- a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py +++ b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py @@ -47,6 +47,15 @@ "message": "Connecting Audius protocol dashboard wallet 0x09439dc8d52396c29e4414664C84f36Cf9A8d500 at 1686252024", }, }, + { + "wallet": "0xe05aC1EfC30cE2F7615186802E2B14CC8759616F", + # 0634902d1af06d2da2ac9badd6718e210b028d753b89b93aaeba8e4438bad4ae + "user_id": 2, + "wallet_signature": { + "signature": "5e38c49b2a0d8157a672d5ccd10abb54758967833efd69c25fbe530e339b00d16d665e1d21688ab629db292d401d53e95cb5701420eaa5fb2cfffbe959cb72f51c", + "message": "Connecting Audius user @user_2 at 1686252024", + }, + } ] @@ -144,6 +153,20 @@ def test_index_dashboard_wallet_user(app, mocker): ) }, ], + "CreateDashboardWalletUserTx5": [ + { + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": new_dashboard_wallet_users_data[4]["user_id"], + "_action": Action.CREATE, + "_metadata": f"""{{"wallet": "{new_dashboard_wallet_users_data[4]["wallet"]}", "wallet_signature": {{"signature": "{new_dashboard_wallet_users_data[4]["wallet_signature"]["signature"]}", "message": "{new_dashboard_wallet_users_data[4]["wallet_signature"]["message"]}"}}}}""", + "_signer": "user2wallet", + } + ) + }, + ], } entity_manager_txs = [ @@ -165,6 +188,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): { "user_id": user_id, "wallet": user_wallets.get(user_id, None) or f"user{user_id}wallet", + "handle": f"User_{user_id}" } for user_id in range(1, 6) ], @@ -190,7 +214,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): # validate db records all_dwus: List[DashboardWalletUser] = session.query(DashboardWalletUser).all() - assert len(all_dwus) == 5 + assert len(all_dwus) == 6 for expected_item in new_dashboard_wallet_users_data: found_matches = [ item @@ -430,6 +454,51 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): ) }, ], + "CreateDashboardWalletUserInvalidTx16": [ + { + # Handle in wallet signature incorrect + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": 4, + "_action": Action.CREATE, + "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "d4421fbf7e0275fd9caa20cd65a347f2dcab339490918862463caf1bc68459984641640d76cde8ecae7b9fce045e3d975351176fca61b28450279a78ca1489961b", "message": "Connecting Audius user @beep at 1686252026"}}""", + "_signer": "user4wallet", + } + ) + }, + ], + "CreateDashboardWalletUserInvalidTx17": [ + { + # Wallet signature message has extra words (handle format) + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": 4, + "_action": Action.CREATE, + "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "a1e77d57b020b5ba9d0025fe827e24ad271f52bca1d4c0a7640fc825bc651f8564cf72aae2d42f9e3688fd4686ff57671413d01b1c2d66e626ca62d0546c1ab81c", "message": "Connecting Audius user @User_4 Kangaroo at 1686252026"}}""", + "_signer": "user4wallet", + } + ) + }, + ], + "CreateDashboardWalletUserInvalidTx17": [ + { + # Wallet signature message has extra words (user id format) + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": 4, + "_action": Action.CREATE, + "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "50d65bdc7f3c6b364258f5fac4f74d570b2b74a8e6547cc1495a339803b6decb13d7420789bc2c9679eda4aabb72d028c1f0857bfce755c1ace6213ea2f9add51c", "message": "Connecting Audius user id 4 Kangaroo at 1686252026"}}""", + "_signer": "user4wallet", + } + ) + }, + ], } entity_manager_txs = [ @@ -451,7 +520,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): # validate db records all_dwus: List[DashboardWalletUser] = session.query(DashboardWalletUser).all() # make sure no new rows were added - assert len(all_dwus) == 5 + assert len(all_dwus) == 6 # # Test invalid delete txs tx_receipts = { @@ -521,7 +590,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): # validate db records all_dwus: List[DashboardWalletUser] = session.query(DashboardWalletUser).all() # make sure no new rows were added or deleted - assert len(all_dwus) == 5 + assert len(all_dwus) == 6 expected_deleted_items = [ new_dashboard_wallet_users_data[0], @@ -579,7 +648,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): ) # validate db records all_dwus: List[DashboardWalletUser] = session.query(DashboardWalletUser).all() - assert len(all_dwus) == 5 + assert len(all_dwus) == 6 for expected_item in expected_deleted_items: found_matches = [ @@ -634,7 +703,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): # validate db records all_dwus: List[DashboardWalletUser] = session.query(DashboardWalletUser).all() - assert len(all_dwus) == 5 + assert len(all_dwus) == 6 for expected_item in second_set_new_dashboard_wallet_users_data: found_matches = [ item diff --git a/packages/discovery-provider/src/api/v1/models/users.py b/packages/discovery-provider/src/api/v1/models/users.py index 292ed6b3458..3314c01e132 100644 --- a/packages/discovery-provider/src/api/v1/models/users.py +++ b/packages/discovery-provider/src/api/v1/models/users.py @@ -133,14 +133,6 @@ }, ) -tx_signature = ns.model( - "tx_signature", - { - "message": fields.String(required=True), - "signature": fields.String(required=True), - }, -) - decoded_user_token = ns.model( "decoded_user_token", { @@ -154,9 +146,6 @@ ), "sub": fields.String(required=True), "iat": fields.String(required=True), - "txSignature": fields.Nested( - tx_signature, required=False, allow_null=True, skip_none=True - ), }, ) diff --git a/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py b/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py index 7d07cc0b907..dd345d4324e 100644 --- a/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py +++ b/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py @@ -44,18 +44,30 @@ def is_within_5_minutes(timestamp_str): def matches_user_id(hash_or_int_id, int_id): return hash_or_int_id == str(int_id) or decode_string_id(hash_or_int_id) == int_id - -def verify_dashboard_wallet_signature(dashboard_wallet, user_id, wallet_signature): +def has_valid_user_id_message(wallet_signature, user_id): + wallet_signature_split = wallet_signature.get("message", "").split() + return wallet_signature.get("message", "").startswith( + "Connecting Audius user id" + ) and matches_user_id( + wallet_signature_split[-3], user_id + ) and len(wallet_signature_split) == 7 + +def has_valid_user_handle_message(wallet_signature, user_handle): + wallet_signature_split = wallet_signature.get("message", "").split() + return wallet_signature.get("message", "").startswith( + "Connecting Audius user @" + ) and wallet_signature_split[3].lower() == f"@{user_handle.lower()}" and len(wallet_signature_split) == 6 + +def verify_dashboard_wallet_signature(dashboard_wallet, user_id, user_handle, wallet_signature): + if not isinstance(wallet_signature, dict): + raise IndexingValidationError("Invalid Create Dashboard Wallet Transaction, wallet signature malformatted") + wallet_signature_split = wallet_signature.get("message", "").split() if ( # Expect wallet_signature message to be "Connecting Audius user id {user hash id} at {timestamp}" not isinstance(wallet_signature, dict) - or not wallet_signature.get("message", "").startswith( - "Connecting Audius user id" - ) - or not matches_user_id( - (wallet_signature.get("message", "").split()[-3]), user_id - ) - or not is_within_5_minutes((wallet_signature.get("message", "").split())[-1]) + or (not has_valid_user_id_message(wallet_signature, user_id) and not has_valid_user_handle_message(wallet_signature, user_handle)) + or not wallet_signature_split[-2] == 'at' + or not is_within_5_minutes((wallet_signature_split)[-1]) ): raise IndexingValidationError( "Invalid Create Dashboard Wallet Transaction, wallet signature provided does not have correct message" @@ -171,6 +183,7 @@ def validate_dashboard_wallet_user_tx(params: ManageEntityParameters, metadata): ) user_wallet = params.existing_records["User"][user_id].wallet + user_handle = params.existing_records["User"][user_id].handle user_matches_signer = user_wallet and user_wallet.lower() == params.signer.lower() dashboard_wallet_matches_signer = dashboard_wallet == params.signer.lower() if params.action == Action.DELETE: @@ -213,7 +226,7 @@ def validate_dashboard_wallet_user_tx(params: ManageEntityParameters, metadata): ) if user_matches_signer: verify_dashboard_wallet_signature( - dashboard_wallet, user_id, metadata["wallet_signature"] + dashboard_wallet, user_id, user_handle, metadata["wallet_signature"] ) else: verify_user_signature( From d8456f24150ad0c00d7bf3b12b27f4a13e214d50 Mon Sep 17 00:00:00 2001 From: Nikki Kang Date: Fri, 12 Jan 2024 01:18:02 -0800 Subject: [PATCH 2/2] lint --- ...st_dashboard_wallet_user_entity_manager.py | 58 +++++++++---------- .../entities/dashboard_wallet_user.py | 37 ++++++++---- .../sdk/api/dashboard-wallet-users/types.ts | 2 +- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py index 6e47fdc0246..6f1d082d0c6 100644 --- a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py +++ b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_dashboard_wallet_user_entity_manager.py @@ -55,7 +55,7 @@ "signature": "5e38c49b2a0d8157a672d5ccd10abb54758967833efd69c25fbe530e339b00d16d665e1d21688ab629db292d401d53e95cb5701420eaa5fb2cfffbe959cb72f51c", "message": "Connecting Audius user @user_2 at 1686252024", }, - } + }, ] @@ -188,7 +188,7 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): { "user_id": user_id, "wallet": user_wallets.get(user_id, None) or f"user{user_id}wallet", - "handle": f"User_{user_id}" + "handle": f"User_{user_id}", } for user_id in range(1, 6) ], @@ -470,34 +470,34 @@ def get_events_side_effect(_, tx_receipt: TxReceipt): }, ], "CreateDashboardWalletUserInvalidTx17": [ - { - # Wallet signature message has extra words (handle format) - "args": AttributeDict( - { - "_entityId": 0, - "_entityType": EntityType.DASHBOARD_WALLET_USER, - "_userId": 4, - "_action": Action.CREATE, - "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "a1e77d57b020b5ba9d0025fe827e24ad271f52bca1d4c0a7640fc825bc651f8564cf72aae2d42f9e3688fd4686ff57671413d01b1c2d66e626ca62d0546c1ab81c", "message": "Connecting Audius user @User_4 Kangaroo at 1686252026"}}""", - "_signer": "user4wallet", - } - ) - }, + { + # Wallet signature message has extra words (handle format) + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": 4, + "_action": Action.CREATE, + "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "a1e77d57b020b5ba9d0025fe827e24ad271f52bca1d4c0a7640fc825bc651f8564cf72aae2d42f9e3688fd4686ff57671413d01b1c2d66e626ca62d0546c1ab81c", "message": "Connecting Audius user @User_4 Kangaroo at 1686252026"}}""", + "_signer": "user4wallet", + } + ) + }, ], - "CreateDashboardWalletUserInvalidTx17": [ - { - # Wallet signature message has extra words (user id format) - "args": AttributeDict( - { - "_entityId": 0, - "_entityType": EntityType.DASHBOARD_WALLET_USER, - "_userId": 4, - "_action": Action.CREATE, - "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "50d65bdc7f3c6b364258f5fac4f74d570b2b74a8e6547cc1495a339803b6decb13d7420789bc2c9679eda4aabb72d028c1f0857bfce755c1ace6213ea2f9add51c", "message": "Connecting Audius user id 4 Kangaroo at 1686252026"}}""", - "_signer": "user4wallet", - } - ) - }, + "CreateDashboardWalletUserInvalidTx18": [ + { + # Wallet signature message has extra words (user id format) + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.DASHBOARD_WALLET_USER, + "_userId": 4, + "_action": Action.CREATE, + "_metadata": """{"wallet": "0x5B905C04b15e3BD152224E9738dA726DF6d103B6", "wallet_signature": {"signature": "50d65bdc7f3c6b364258f5fac4f74d570b2b74a8e6547cc1495a339803b6decb13d7420789bc2c9679eda4aabb72d028c1f0857bfce755c1ace6213ea2f9add51c", "message": "Connecting Audius user id 4 Kangaroo at 1686252026"}}""", + "_signer": "user4wallet", + } + ) + }, ], } diff --git a/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py b/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py index dd345d4324e..d43e7755ce9 100644 --- a/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py +++ b/packages/discovery-provider/src/tasks/entity_manager/entities/dashboard_wallet_user.py @@ -44,29 +44,42 @@ def is_within_5_minutes(timestamp_str): def matches_user_id(hash_or_int_id, int_id): return hash_or_int_id == str(int_id) or decode_string_id(hash_or_int_id) == int_id + def has_valid_user_id_message(wallet_signature, user_id): wallet_signature_split = wallet_signature.get("message", "").split() - return wallet_signature.get("message", "").startswith( - "Connecting Audius user id" - ) and matches_user_id( - wallet_signature_split[-3], user_id - ) and len(wallet_signature_split) == 7 + return ( + wallet_signature.get("message", "").startswith("Connecting Audius user id") + and matches_user_id(wallet_signature_split[-3], user_id) + and len(wallet_signature_split) == 7 + ) + def has_valid_user_handle_message(wallet_signature, user_handle): wallet_signature_split = wallet_signature.get("message", "").split() - return wallet_signature.get("message", "").startswith( - "Connecting Audius user @" - ) and wallet_signature_split[3].lower() == f"@{user_handle.lower()}" and len(wallet_signature_split) == 6 + return ( + wallet_signature.get("message", "").startswith("Connecting Audius user @") + and wallet_signature_split[3].lower() == f"@{user_handle.lower()}" + and len(wallet_signature_split) == 6 + ) -def verify_dashboard_wallet_signature(dashboard_wallet, user_id, user_handle, wallet_signature): + +def verify_dashboard_wallet_signature( + dashboard_wallet, user_id, user_handle, wallet_signature +): if not isinstance(wallet_signature, dict): - raise IndexingValidationError("Invalid Create Dashboard Wallet Transaction, wallet signature malformatted") + raise IndexingValidationError( + "Invalid Create Dashboard Wallet Transaction, wallet signature malformatted" + ) wallet_signature_split = wallet_signature.get("message", "").split() if ( # Expect wallet_signature message to be "Connecting Audius user id {user hash id} at {timestamp}" + # OR "Connecting Audius user @{handle} at {timestamp}" not isinstance(wallet_signature, dict) - or (not has_valid_user_id_message(wallet_signature, user_id) and not has_valid_user_handle_message(wallet_signature, user_handle)) - or not wallet_signature_split[-2] == 'at' + or ( + not has_valid_user_id_message(wallet_signature, user_id) + and not has_valid_user_handle_message(wallet_signature, user_handle) + ) + or not wallet_signature_split[-2] == "at" or not is_within_5_minutes((wallet_signature_split)[-1]) ): raise IndexingValidationError( diff --git a/packages/libs/src/sdk/api/dashboard-wallet-users/types.ts b/packages/libs/src/sdk/api/dashboard-wallet-users/types.ts index aa1b5361e11..18b55fac1fa 100644 --- a/packages/libs/src/sdk/api/dashboard-wallet-users/types.ts +++ b/packages/libs/src/sdk/api/dashboard-wallet-users/types.ts @@ -11,7 +11,7 @@ export const CreateDashboardWalletUser = z userId: HashId, walletSignature: z .object({ - /** Message should be of the form: "Connecting Audius user id a93jl at 39823489" */ + /** Message should be of the form: "Connecting Audius user id a93jl at 39823489" OR "Connecting Audius user @jill1990 at 39823489" */ message: z.string(), signature: z.string() })