From 75dd873660c421b2aa0f1267b43dc428b33152c4 Mon Sep 17 00:00:00 2001 From: Yosuke Otosu Date: Fri, 22 Sep 2023 12:09:55 +0900 Subject: [PATCH] Record token update operation caused by scheduled event as history --- batch/processor_scheduled_events.py | 43 ++- .../test_batch_processor_scheduled_events.py | 298 +++++------------- 2 files changed, 120 insertions(+), 221 deletions(-) diff --git a/batch/processor_scheduled_events.py b/batch/processor_scheduled_events.py index 8b44153c..d2f86624 100644 --- a/batch/processor_scheduled_events.py +++ b/batch/processor_scheduled_events.py @@ -54,6 +54,8 @@ ScheduledEvents, ScheduledEventType, TokenType, + TokenUpdateOperationCategory, + TokenUpdateOperationLog, ) from app.utils.e2ee_utils import E2EEUtils from app.utils.web3_utils import Web3Wrapper @@ -199,21 +201,42 @@ def __process(self, db_session: Session, events_list: List[ScheduledEvents]): if _event.token_type == TokenType.IBET_SHARE.value: # Update if _event.event_type == ScheduledEventType.UPDATE.value: + token_contract = IbetShareContract(_event.token_address) + original_contents = token_contract.get().__dict__ _update_data = IbetShareUpdateParams(**_event.data) - IbetShareContract(_event.token_address).update( + token_contract.update( data=_update_data, tx_from=_event.issuer_address, private_key=private_key, ) + self.__sink_on_token_update_operation_log( + db_session=db_session, + token_address=_event.token_address, + issuer_address=_event.issuer_address, + token_type=_event.token_type, + arguments=_update_data.model_dump(exclude_none=True), + original_contents=original_contents, + ) + elif _event.token_type == TokenType.IBET_STRAIGHT_BOND.value: # Update if _event.event_type == ScheduledEventType.UPDATE.value: + token_contract = IbetStraightBondContract(_event.token_address) + original_contents = token_contract.get().__dict__ _update_data = IbetStraightBondUpdateParams(**_event.data) IbetStraightBondContract(_event.token_address).update( data=_update_data, tx_from=_event.issuer_address, private_key=private_key, ) + self.__sink_on_token_update_operation_log( + db_session=db_session, + token_address=_event.token_address, + issuer_address=_event.issuer_address, + token_type=_event.token_type, + arguments=_update_data.model_dump(exclude_none=True), + original_contents=original_contents, + ) self.__sink_on_finish_event_process( db_session=db_session, record_id=_event.id, status=1 @@ -282,6 +305,24 @@ def __sink_on_error_notification( } db_session.add(notification) + @staticmethod + def __sink_on_token_update_operation_log( + db_session: Session, + token_address: str, + issuer_address: str, + token_type: str, + arguments: dict, + original_contents: dict, + ): + operation_log = TokenUpdateOperationLog() + operation_log.token_address = token_address + operation_log.issuer_address = issuer_address + operation_log.type = token_type + operation_log.arguments = arguments + operation_log.original_contents = original_contents + operation_log.operation_category = TokenUpdateOperationCategory.UPDATE.value + db_session.add(operation_log) + class Worker: def __init__(self, thread_num: int): diff --git a/tests/test_batch_processor_scheduled_events.py b/tests/test_batch_processor_scheduled_events.py index 31eff204..3648d5cd 100644 --- a/tests/test_batch_processor_scheduled_events.py +++ b/tests/test_batch_processor_scheduled_events.py @@ -22,6 +22,7 @@ import pytest from sqlalchemy import select +from web3.datastructures import AttributeDict from app.exceptions import ContractRevertError, SendTransactionError from app.model.db import ( @@ -31,6 +32,8 @@ ScheduledEvents, ScheduledEventType, TokenType, + TokenUpdateOperationCategory, + TokenUpdateOperationLog, ) from app.utils.e2ee_utils import E2EEUtils from batch.processor_scheduled_events import LOG, Processor @@ -53,10 +56,9 @@ class TestProcessor: # Normal Case ########################################################################### - # + # # IbetStraightBond - # unset additional info - def test_normal_1_1(self, processor, db): + def test_normal_1(self, processor, db): test_account = config_eth_account("user1") _issuer_address = test_account["address"] _keyfile = test_account["keyfile_json"] @@ -139,8 +141,12 @@ def test_normal_1_1(self, processor, db): target="app.model.blockchain.token.IbetStraightBondContract.update", return_value=None, ) + IbetStraightBondContract_get = patch( + target="app.model.blockchain.token.IbetStraightBondContract.get", + return_value=AttributeDict({}), + ) - with IbetStraightBondContract_update: + with IbetStraightBondContract_update, IbetStraightBondContract_get: # Execute batch processor.process() @@ -163,122 +169,36 @@ def test_normal_1_1(self, processor, db): .limit(1) ).first() assert _scheduled_event.status == 0 - - # - # IbetStraightBond - # set additional info - def test_normal_1_2(self, processor, db): - test_account = config_eth_account("user1") - _issuer_address = test_account["address"] - _keyfile = test_account["keyfile_json"] - _token_address_1 = "token_address_test1" - _token_address_2 = "token_address_test2" - _token_address_3 = "token_address_test3" - - # Prepare data : Account - account = Account() - account.issuer_address = _issuer_address - account.eoa_password = E2EEUtils.encrypt("password") - account.keyfile = _keyfile - db.add(account) - - # prepare data : ScheduledEvents - datetime_past_utc = datetime.now(timezone.utc) + timedelta(days=-1) - datetime_past_str = datetime_past_utc.isoformat() - datetime_now_utc = datetime.now(timezone.utc) - datetime_now_str = datetime_now_utc.isoformat() - datetime_pending_utc = datetime.now(timezone.utc) + timedelta(days=1) - datetime_pending_str = datetime_pending_utc.isoformat() - update_data = { + _operation_log = db.scalars( + select(TokenUpdateOperationLog) + .where(TokenUpdateOperationLog.token_address == _token_address_2) + .limit(1) + ).first() + assert _operation_log.issuer_address == _issuer_address + assert _operation_log.type == TokenType.IBET_STRAIGHT_BOND.value + assert _operation_log.arguments == { + "contact_information": "問い合わせ先test", "face_value": 10000, - "interest_rate": 0.5, "interest_payment_date": ["0101", "0701"], - "redemption_value": 11000, - "transferable": False, - "image_url": [ - "http://sampleurl.com/some_image1.png", - "http://sampleurl.com/some_image2.png", - "http://sampleurl.com/some_image3.png", - ], - "status": False, + "interest_rate": 0.5, "is_offering": False, "is_redeemed": True, - "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", - "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", - "is_canceled": False, + "redemption_value": 11000, + "status": False, + "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", + "transferable": False, } - - # TokenType: STRAIGHT_BOND, status is 2, will not change - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_1 - token_event.token_type = TokenType.IBET_STRAIGHT_BOND.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_past_str - token_event.status = 2 - token_event.data = update_data - db.add(token_event) - - # TokenType: STRAIGHT_BOND, status will be "1: Succeeded" - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_2 - token_event.token_type = TokenType.IBET_STRAIGHT_BOND.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_now_str - token_event.status = 0 - token_event.data = update_data - db.add(token_event) - - # TokenType: STRAIGHT_BOND, status will be "0: Pending" - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_3 - token_event.token_type = TokenType.IBET_STRAIGHT_BOND.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_pending_str - token_event.status = 0 - token_event.data = update_data - db.add(token_event) - - db.commit() - - # mock - IbetStraightBondContract_update = patch( - target="app.model.blockchain.token.IbetStraightBondContract.update", - return_value=None, + assert _operation_log.original_contents == {} + assert ( + _operation_log.operation_category + == TokenUpdateOperationCategory.UPDATE.value ) - with IbetStraightBondContract_update: - # Execute batch - processor.process() - - # Assertion - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_1) - .limit(1) - ).first() - assert _scheduled_event.status == 2 - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_2) - .limit(1) - ).first() - assert _scheduled_event.status == 1 - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_3) - .limit(1) - ).first() - assert _scheduled_event.status == 0 - - # + # # IbetShare - # unset additional info - def test_normal_2_1(self, processor, db): + def test_normal_2(self, processor, db): test_account = config_eth_account("user1") _issuer_address = test_account["address"] _keyfile = test_account["keyfile_json"] @@ -361,8 +281,12 @@ def test_normal_2_1(self, processor, db): target="app.model.blockchain.token.IbetShareContract.update", return_value=None, ) + IbetShareContract_get = patch( + target="app.model.blockchain.token.IbetShareContract.get", + return_value=AttributeDict({}), + ) - with IbetShareContract_update: + with IbetShareContract_update, IbetShareContract_get: # Execute batch processor.process() @@ -385,118 +309,33 @@ def test_normal_2_1(self, processor, db): .limit(1) ).first() assert _scheduled_event.status == 0 - - # - # IbetShare - # set additional info - def test_normal_2_2(self, processor, db): - test_account = config_eth_account("user1") - _issuer_address = test_account["address"] - _keyfile = test_account["keyfile_json"] - _token_address_1 = "token_address_test_1" - _token_address_2 = "token_address_test_2" - _token_address_3 = "token_address_test_3" - - # Prepare data : Account - account = Account() - account.issuer_address = _issuer_address - account.eoa_password = E2EEUtils.encrypt("password") - account.keyfile = _keyfile - db.add(account) - - # prepare data : ScheduledEvents - datetime_past_utc = datetime.now(timezone.utc) + timedelta(days=-1) - datetime_past_str = datetime_past_utc.isoformat() - datetime_now_utc = datetime.now(timezone.utc) - datetime_now_str = datetime_now_utc.isoformat() - datetime_pending_utc = datetime.now(timezone.utc) + timedelta(days=1) - datetime_pending_str = datetime_pending_utc.isoformat() - - update_data = { + _operation_log = db.scalars( + select(TokenUpdateOperationLog) + .where(TokenUpdateOperationLog.token_address == _token_address_2) + .limit(1) + ).first() + assert _operation_log.issuer_address == _issuer_address + assert _operation_log.type == TokenType.IBET_SHARE.value + assert _operation_log.arguments == { "cancellation_date": "20221231", - "dividends": 345.67, - "dividend_record_date": "20211231", + "contact_information": "問い合わせ先test", "dividend_payment_date": "20211231", - "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", - "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", - "image_url": [ - "http://sampleurl.com/some_image1.png", - "http://sampleurl.com/some_image2.png", - "http://sampleurl.com/some_image3.png", - ], - "transferable": False, - "status": False, + "dividend_record_date": "20211231", + "dividends": 345.67, + "is_canceled": False, "is_offering": False, - "contact_information": "問い合わせ先test", + "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", "privacy_policy": "プライバシーポリシーtest", - "is_canceled": False, + "status": False, + "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", + "transferable": False, } - - # TokenType: STRAIGHT_BOND, status is 2, will not change - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_1 - token_event.token_type = TokenType.IBET_STRAIGHT_BOND.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_past_str - token_event.status = 2 - token_event.data = update_data - db.add(token_event) - - # TokenType: STRAIGHT_BOND, status will be "1: Succeeded" - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_2 - token_event.token_type = TokenType.IBET_SHARE.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_now_str - token_event.status = 0 - token_event.data = update_data - db.add(token_event) - - # TokenType: STRAIGHT_BOND, status will be "0: Pending" - token_event = ScheduledEvents() - token_event.issuer_address = _issuer_address - token_event.token_address = _token_address_3 - token_event.token_type = TokenType.IBET_SHARE.value - token_event.event_type = ScheduledEventType.UPDATE.value - token_event.scheduled_datetime = datetime_pending_str - token_event.status = 0 - token_event.data = update_data - db.add(token_event) - - db.commit() - - # mock - IbetShareContract_update = patch( - target="app.model.blockchain.token.IbetShareContract.update", - return_value=None, + assert _operation_log.original_contents == {} + assert ( + _operation_log.operation_category + == TokenUpdateOperationCategory.UPDATE.value ) - with IbetShareContract_update: - # Execute batch - processor.process() - - # Assertion - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_1) - .limit(1) - ).first() - assert _scheduled_event.status == 2 - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_2) - .limit(1) - ).first() - assert _scheduled_event.status == 1 - _scheduled_event = db.scalars( - select(ScheduledEvents) - .where(ScheduledEvents.token_address == _token_address_3) - .limit(1) - ).first() - assert _scheduled_event.status == 0 - ########################################################################### # Error Case ########################################################################### @@ -644,8 +483,12 @@ def test_error_3(self, processor, db): target="app.model.blockchain.token.IbetStraightBondContract.update", side_effect=SendTransactionError(), ) + IbetStraightBondContract_get = patch( + target="app.model.blockchain.token.IbetStraightBondContract.get", + return_value=AttributeDict({}), + ) - with IbetStraightBondContract_update: + with IbetStraightBondContract_update, IbetStraightBondContract_get: # Execute batch processor.process() @@ -710,7 +553,12 @@ def test_error_4(self, processor, db): side_effect=SendTransactionError(), ) - with IbetShareContract_update: + IbetShareContract_get = patch( + target="app.model.blockchain.token.IbetShareContract.get", + return_value=AttributeDict({}), + ) + + with IbetShareContract_update, IbetShareContract_get: # Execute batch processor.process() @@ -774,7 +622,12 @@ def test_error_5(self, processor, db, caplog): side_effect=ContractRevertError("999999"), ) - with IbetStraightBondContract_update: + IbetStraightBondContract_get = patch( + target="app.model.blockchain.token.IbetStraightBondContract.get", + return_value=AttributeDict({}), + ) + + with IbetStraightBondContract_update, IbetStraightBondContract_get: # Execute batch processor.process() @@ -849,7 +702,12 @@ def test_error_6(self, processor, db, caplog): side_effect=ContractRevertError("999999"), ) - with IbetShareContract_update: + IbetShareContract_get = patch( + target="app.model.blockchain.token.IbetShareContract.get", + return_value=AttributeDict({}), + ) + + with IbetShareContract_update, IbetShareContract_get: # Execute batch processor.process()