diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 74b10356..625cc448 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,6 +24,8 @@ on: required: true KMS_KEY_ID: required: true + ADHOC_KMS_KEY_ID: + required: true GOOGLE_CREDENTIAL: required: true APPLE_CREDENTIAL: @@ -135,6 +137,7 @@ jobs: ODIN_GQL_URL: ${{ vars.ODIN_GQL_URL }} HEIMDALL_GQL_URL: ${{ vars.HEIMDALL_GQL_URL }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} GOOGLE_PACKAGE_NAME: ${{ vars.GOOGLE_PACKAGE_NAME }} APPLE_BUNDLE_ID: ${{ vars.APPLE_BUNDLE_ID }} @@ -172,6 +175,7 @@ jobs: ODIN_GQL_URL: ${{ vars.ODIN_GQL_URL }} HEIMDALL_GQL_URL: ${{ vars.HEIMDALL_GQL_URL }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} GOOGLE_PACKAGE_NAME: ${{ vars.GOOGLE_PACKAGE_NAME }} APPLE_BUNDLE_ID: ${{ vars.APPLE_BUNDLE_ID }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c64ac53..8a8d86e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,6 +43,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} APPLE_CREDENTIAL: ${{ secrets.APPLE_CREDENTIAL }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} @@ -71,6 +72,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} APPLE_CREDENTIAL: ${{ secrets.APPLE_CREDENTIAL }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} @@ -111,6 +113,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} APPLE_CREDENTIAL: ${{ secrets.APPLE_CREDENTIAL }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} diff --git a/.github/workflows/synth.yml b/.github/workflows/synth.yml index 8ccc9b90..a7ffb2f1 100644 --- a/.github/workflows/synth.yml +++ b/.github/workflows/synth.yml @@ -18,6 +18,8 @@ on: required: true KMS_KEY_ID: required: true + ADHOC_KMS_KEY_ID: + required: true GOOGLE_CREDENTIAL: required: true APPLE_CREDENTIAL: @@ -122,6 +124,7 @@ jobs: ODIN_GQL_URL: ${{ vars.ODIN_GQL_URL }} HEIMDALL_GQL_URL: ${{ vars.HEIMDALL_GQL_URL }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + ADHOC_KMS_KEY_ID: ${{ secrets.ADHOC_KMS_KEY_ID }} GOOGLE_CREDENTIAL: ${{ secrets.GOOGLE_CREDENTIAL }} GOOGLE_PACKAGE_NAME: ${{ vars.GOOGLE_PACKAGE_NAME }} APPLE_BUNDLE_ID: ${{ vars.APPLE_BUNDLE_ID }} diff --git a/common/__init__.py b/common/__init__.py index 14a8a463..df1bc8bc 100644 --- a/common/__init__.py +++ b/common/__init__.py @@ -58,6 +58,7 @@ class Config: headless: str = "http://localhost" kms_key_id: Optional[str] = None + adhoc_kms_key_id: Optional[str] = None golden_dust_request_sheet_id: Optional[str] = None golden_dust_work_sheet_id: Optional[str] = None form_sheet: Optional[str] = None diff --git a/common/_crypto.py b/common/_crypto.py index 4e336061..20552a4e 100644 --- a/common/_crypto.py +++ b/common/_crypto.py @@ -144,6 +144,8 @@ def sign_tx(self, unsigned_tx: bytes) -> bytes: return der_encode(seq) + +# Deprecated def derive_address(address: Union[str, bytes], key: Union[str, bytes], get_byte: bool = False) -> Union[bytes, str]: """ Derive given address using key. diff --git a/common/consts.py b/common/consts.py index f88f91b0..d6dbb044 100644 --- a/common/consts.py +++ b/common/consts.py @@ -32,6 +32,17 @@ "SOULSTONE_1004", # Lil' Fenrir ) +ITEM_FUNGIBLE_ID_DICT = { + "400000": "3991e04dd808dc0bc24b21f5adb7bf1997312f8700daf1334bf34936e8a0813a", + "500000": "00dfffe23964af9b284d121dae476571b7836b8d9e2e5f510d92a840fecc64fe", + "600201": "f8faf92c9c0d0e8e06694361ea87bfc8b29a8ae8de93044b98470a57636ed0e0", + "600202": "08f566bb43570aad34c1790901f824dd5609db880afebd5382fcec054203d92a", + "600203": "", + "800201": "1a755098a2bc0659a063107df62e2ff9b3cdaba34d96b79519f504b996f53820", + "CRYSTAL": "FAV__CRYSTAL", + "RUNE_GOLDENLEAF": "FAV__RUNE_GOLDENLEAF", +} + GQL_DICT = { PlanetID.ODIN: os.environ.get("ODIN_GQL_URL"), PlanetID.ODIN_INTERNAL: os.environ.get("ODIN_GQL_URL"), diff --git a/common/lib9c/actions/issue_tokens_from_garage.py b/common/lib9c/actions/issue_tokens_from_garage.py new file mode 100644 index 00000000..56042c6b --- /dev/null +++ b/common/lib9c/actions/issue_tokens_from_garage.py @@ -0,0 +1,53 @@ +from typing import List, Optional + +from common.lib9c.actions import ActionBase +from common.lib9c.models.fungible_asset_value import FungibleAssetValue + + +class FavIssueSpec: + def __init__(self, fav: FungibleAssetValue): + self._fav = fav + + @property + def plain_value(self): + return \ + [ + self._fav.plain_value, + None # No Item spec + ] + + +class ItemIssueSpec: + def __init__(self, fungible_item_id: [str | bytes], amount: int): + self.item_id = fungible_item_id if isinstance(fungible_item_id, bytes) else bytes.fromhex(fungible_item_id) + self.amount = amount + + @property + def plain_value(self): + return \ + [ + None, # No FAV spec + [ + self.item_id, + self.amount + ] + ] + + +class IssueTokensFromGarage(ActionBase): + """ + Python port of `IssueTokensFromGarage` action from lib9c + + - type_id: `issue_tokens_from_garage` + - values: List[spec] + - spec: List[FavIssueSpec | ItemIssueSpec] + """ + TYPE_ID: str = "issue_tokens_from_garage" + + def __init__(self, *, values: List[FavIssueSpec | ItemIssueSpec], _id: Optional[str] = None): + super().__init__(self.TYPE_ID, _id) + self._values: List[FavIssueSpec | ItemIssueSpec] = values + + @property + def _plain_value(self): + return [x.plain_value for x in self._values] if self._values else None diff --git a/common/lib9c/exceptions.py b/common/lib9c/exceptions.py new file mode 100644 index 00000000..c27e5c0f --- /dev/null +++ b/common/lib9c/exceptions.py @@ -0,0 +1,2 @@ +class InvalidAmountException(Exception): + pass diff --git a/common/lib9c/models/address.py b/common/lib9c/models/address.py index c8086946..377a6a20 100644 --- a/common/lib9c/models/address.py +++ b/common/lib9c/models/address.py @@ -1,5 +1,10 @@ from __future__ import annotations +import hashlib +import hmac + +import eth_utils + class Address: def __init__(self, addr: str): @@ -20,5 +25,46 @@ def long_format(self): def short_format(self): return self.raw.hex() + def derive(self, key: str) -> Address: + return Address( + self.__checksum_encode(hmac.new( + key.encode("utf-8"), + self.raw, + digestmod=hashlib.sha1 + ).digest()) + ) + + def __checksum_encode(self, addr: bytes) -> str: # Takes a 20-byte binary address as input + """ + Convert input address to checksum encoded address without prefix "0x" + See [ERC-55](https://eips.ethereum.org/EIPS/eip-55) + + :param addr: 20-bytes binary address + :return: checksum encoded address as string + """ + hex_addr = addr.hex() + checksum_buffer = "" + + # Treat the hex address as ascii/utf-8 for keccak256 hashing + hashed_address = eth_utils.keccak(text=hex_addr).hex() + + # Iterate over each character in the hex address + for nibble_index, character in enumerate(hex_addr): + if character in "0123456789": + # We can't upper-case the decimal digits + checksum_buffer += character + elif character in "abcdef": + # Check if the corresponding hex digit (nibble) in the hash is 8 or higher + hashed_address_nibble = int(hashed_address[nibble_index], 16) + if hashed_address_nibble > 7: + checksum_buffer += character.upper() + else: + checksum_buffer += character + else: + raise eth_utils.ValidationError( + f"Unrecognized hex character {character!r} at position {nibble_index}" + ) + return checksum_buffer + def __eq__(self, other: Address): return self.raw == other.raw diff --git a/common/shared_stack.py b/common/shared_stack.py index d0571b4e..f5253400 100644 --- a/common/shared_stack.py +++ b/common/shared_stack.py @@ -94,6 +94,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: # SecureStrings in Parameter Store PARAMETER_LIST = ( ("KMS_KEY_ID", True), + ("ADHOC_KMS_KEY_ID", True), ("GOOGLE_CREDENTIAL", True), ("APPLE_CREDENTIAL", True), ("SEASON_PASS_JWT_SECRET", True), diff --git a/common/utils/aws.py b/common/utils/aws.py index 65ee1234..4cc235e4 100644 --- a/common/utils/aws.py +++ b/common/utils/aws.py @@ -21,10 +21,13 @@ def fetch_secrets(region: str, secret_arn: str) -> Dict: return json.loads(resp["SecretString"]) -def fetch_kms_key_id(stage: str, region: str) -> Optional[str]: +def fetch_kms_key_id(stage: str, region: str, adhoc: bool = False) -> Optional[str]: client = boto3.client("ssm", region_name=region) try: - return client.get_parameter(Name=f"{stage}_9c_IAP_KMS_KEY_ID", WithDecryption=True)["Parameter"]["Value"] + return client.get_parameter( + Name=f"{stage}_9c_IAP{'_ADHOC' if adhoc else ''}_KMS_KEY_ID", + WithDecryption=True + )["Parameter"]["Value"] except Exception as e: logger.error(e) return None diff --git a/tests/lib9c/actions/test_issue_tokens_from_garage.py b/tests/lib9c/actions/test_issue_tokens_from_garage.py new file mode 100644 index 00000000..8fdf1e3c --- /dev/null +++ b/tests/lib9c/actions/test_issue_tokens_from_garage.py @@ -0,0 +1,120 @@ +from decimal import Decimal + +import pytest + +from common.lib9c.actions.issue_tokens_from_garage import IssueTokensFromGarage, FavIssueSpec, ItemIssueSpec +from common.lib9c.models.currency import Currency +from common.lib9c.models.fungible_asset_value import FungibleAssetValue + +TEST_ID = "0d0d9e0cbc1b11eeb0dc6fd71476142a" +TEST_FUNGIBLE_ITEM_ID = "f8faf92c9c0d0e8e06694361ea87bfc8b29a8ae8de93044b98470a57636ed0e0" # Golden Dust +TEST_FUNGIBLE_ITEM_BINARY = b'\xf8\xfa\xf9,\x9c\r\x0e\x8e\x06iCa\xea\x87\xbf\xc8\xb2\x9a\x8a\xe8\xde\x93\x04K\x98G\nWcn\xd0\xe0' +ACTION_TEST_DATA = [ + ( + { + "_id": TEST_ID, + "values": [ + FavIssueSpec(FungibleAssetValue.from_raw_data("Crystal", 18, amount=Decimal("1000"))) + ] + }, + [ + [ + [ + { + "ticker": "Crystal", + "decimalPlaces": b'\x12', + "minters": None, + }, + 1000 * 10 ** 18 + ], + None + ], + ] + ), + ( + { + "_id": TEST_ID, + "values": [ + ItemIssueSpec(TEST_FUNGIBLE_ITEM_ID, 42) + ] + }, + [ + [ + None, + [ + TEST_FUNGIBLE_ITEM_BINARY, + 42 + ] + ] + ] + ), + ( + { + "_id": TEST_ID, + "values": [ + ItemIssueSpec("f8faf92c9c0d0e8e06694361ea87bfc8b29a8ae8de93044b98470a57636ed0e0", 42) + ] + }, + [ + [ + None, + [ + b'\xf8\xfa\xf9,\x9c\r\x0e\x8e\x06iCa\xea\x87\xbf\xc8\xb2\x9a\x8a\xe8\xde\x93\x04K\x98G\nWcn\xd0\xe0', + 42 + ] + ] + ] + ), + ( + { + "_id": TEST_ID, + "values": [ + FavIssueSpec(FungibleAssetValue.from_raw_data("Crystal", 18, amount=Decimal("1000"))), + ItemIssueSpec("f8faf92c9c0d0e8e06694361ea87bfc8b29a8ae8de93044b98470a57636ed0e0", 42) + ] + }, + [ + [ + [ + { + "ticker": "Crystal", + "decimalPlaces": b'\x12', + "minters": None, + }, + 1000 * 10 ** 18 + ], + None + ], + [ + None, + [ + b'\xf8\xfa\xf9,\x9c\r\x0e\x8e\x06iCa\xea\x87\xbf\xc8\xb2\x9a\x8a\xe8\xde\x93\x04K\x98G\nWcn\xd0\xe0', + 42 + ] + ] + ] + ) +] + + +def test_fav_issue_spec(): + fav = FungibleAssetValue(Currency("Crystal", 18), Decimal(42)) + fav_issue_spec = FavIssueSpec(fav) + assert fav_issue_spec.plain_value[0] == fav.plain_value + + +@pytest.mark.parametrize("data", [TEST_FUNGIBLE_ITEM_ID, TEST_FUNGIBLE_ITEM_BINARY]) +def test_item_issue_spec(data): + item_issue_spec = ItemIssueSpec(data, 42) + assert item_issue_spec.plain_value == [None, [TEST_FUNGIBLE_ITEM_BINARY, 42]] + + +@pytest.mark.parametrize("test_data", ACTION_TEST_DATA) +def test_action(test_data): + data, expected = test_data + action = IssueTokensFromGarage(**data) + plain_value = action.plain_value + values = plain_value["values"] + + assert plain_value["type_id"] == "issue_tokens_from_garage" + assert values == expected diff --git a/tests/lib9c/models/test_address.py b/tests/lib9c/models/test_address.py index dd247ed8..606219b0 100644 --- a/tests/lib9c/models/test_address.py +++ b/tests/lib9c/models/test_address.py @@ -30,3 +30,12 @@ def test_address(addr): def test_address_error(addr): with pytest.raises(ValueError) as e: Address(addr) + + +@pytest.mark.parametrize(("key", "result"), [ + ("key1", "0x518c0044a81c7d3747Ad416Df7227e53233F2e10"), + ("key2", "0xEb5cC07Eb104B082C1ae82E018340e989700ca31") +]) +def test_address_derive(key, result): + addr = Address("0xa5f7e0bd63AD2749D66380f36Eb33Fe0ba50A27D") + assert addr.derive(key) == Address(result) diff --git a/worker/worker/issue_tokens.py b/worker/worker/issue_tokens.py new file mode 100644 index 00000000..fd3c919d --- /dev/null +++ b/worker/worker/issue_tokens.py @@ -0,0 +1,65 @@ +# This worker manually issues token from input data which in following format: +# List[Tuple[{ticker:str}, {decimal_places:int}, {fungible_id:str}, {item_id:int}, {amount:int}]] +# Use `ticker`, `decimal_places` and `amount` for FAV +# minters of the FAV is always assumed as None. +# Use `fungible_id` or `item_id` and `amount` for item. +# If both fungible_id and item_id are provided, the worker uses fungible_id. +import datetime +import os +from decimal import Decimal + +from common import logger +from common._crypto import Account +from common._graphql import GQL +from common.consts import ITEM_FUNGIBLE_ID_DICT +from common.lib9c.actions.issue_tokens_from_garage import FavIssueSpec, ItemIssueSpec, IssueTokensFromGarage +from common.lib9c.models.fungible_asset_value import FungibleAssetValue +from common.utils.aws import fetch_parameter, fetch_kms_key_id +from common.utils.receipt import PlanetID +from common.utils.transaction import create_unsigned_tx, append_signature_to_unsigned_tx + +# NOTE: Use this lambda function by executing manually in AWS console. + +# Set these values by manual form here +NONCE = 0 +PLANET_ID = PlanetID.XXX +GQL_URL = "https://example.com/graphql" # Use Odin/Heimdall GQL host +USE_ADHOC = True +# to here + +HEADLESS_GQL_JWT_SECRET = fetch_parameter( + os.environ.get("REGION_NAME"), + f"{os.environ.get('STAGE')}_9c_IAP_HEADLESS_GQL_JWT_SECRET", + True +)["Value"] +DICT_HEADER = ("ticker", "decimal_places", "fungible_id", "item_id", "amount") + + +def issue(event, context): + spec_list = [] + gql = GQL(GQL_URL, HEADLESS_GQL_JWT_SECRET) + account = Account(fetch_kms_key_id(os.environ.get("STAGE"), os.environ.get("REGION_NAME"), adhoc=USE_ADHOC)) + + for data in event: + data = dict(zip(DICT_HEADER, data)) + if data["ticker"]: + spec_list.append(FavIssueSpec(FungibleAssetValue.from_raw_data( + ticker=data["ticker"], decimal_places=int(data["decimal_places"]), minters=None, + amount=Decimal(data["amount"])) + )) + else: + fungible_id = data["fungible_id"] if data["fungible_id"] else ITEM_FUNGIBLE_ID_DICT[data["item_id"]] + spec_list.append(ItemIssueSpec(fungible_item_id=fungible_id, amount=int(data["amount"]))) + + action = IssueTokensFromGarage(values=spec_list) + utx = create_unsigned_tx( + planet_id=PLANET_ID, public_key=account.pubkey.hex(), address=account.address, nonce=NONCE, + plain_value=action.plain_value, timestamp=datetime.datetime.utcnow() + datetime.timedelta(days=1) + ) + signature = account.sign_tx(utx) + signed_tx = append_signature_to_unsigned_tx(utx, signature) + success, message, tx_id = gql.stage(signed_tx) + + logger.info(f"{success}::'{message}'::{tx_id} with nonce {NONCE}") + logger.debug(signed_tx.hex()) + return success, message, tx_id, NONCE, signed_tx.hex() diff --git a/worker/worker_cdk_stack.py b/worker/worker_cdk_stack.py index 71e04e01..69cef809 100644 --- a/worker/worker_cdk_stack.py +++ b/worker/worker_cdk_stack.py @@ -64,6 +64,14 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: resources=[f"arn:aws:kms:{config.region_name}:{config.account_id}:key/{kms_key_id}"] ) ) + resp = ssm.get_parameter(Name=f"{config.stage}_9c_IAP_ADHOC_KMS_KEY_ID", WithDecryption=True) + kms_key_id = resp["Parameter"]["Value"] + role.add_to_policy( + _iam.PolicyStatement( + actions=["kms:GetPublicKey", "kms:Sign"], + resources=[f"arn:aws:kms:{config.region_name}:{config.account_id}:key/{kms_key_id}"] + ) + ) role.add_to_policy( _iam.PolicyStatement( actions=["ssm:GetParameter"], @@ -71,6 +79,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: shared_stack.google_credential_arn, shared_stack.apple_credential_arn, shared_stack.kms_key_id_arn, + shared_stack.adhoc_kms_key_id_arn, shared_stack.voucher_jwt_secret_arn, shared_stack.headless_gql_jwt_secret_arn, ] @@ -181,8 +190,9 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: if config.stage == "mainnet": ten_minute_event_rule.add_target(_event_targets.LambdaFunction(status_monitor)) - else: - hourly_event_rule.add_target(_event_targets.LambdaFunction(status_monitor)) + # If you want to test monitor in internal, uncomment following statement + # else: + # hourly_event_rule.add_target(_event_targets.LambdaFunction(status_monitor)) # IAP Voucher voucher_env = deepcopy(env) @@ -224,43 +234,44 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: ) everyday_0100_rule.add_target(_event_targets.LambdaFunction(google_refund_handler)) - # Golden dust by NCG handler - env["GOLDEN_DUST_REQUEST_SHEET_ID"] = config.golden_dust_request_sheet_id - env["GOLDEN_DUST_WORK_SHEET_ID"] = config.golden_dust_work_sheet_id - env["FORM_SHEET"] = config.form_sheet - gd_handler = _lambda.Function( - self, f"{config.stage}-9c-iap-goldendust-handler-function", - function_name=f"{config.stage}-9c-iap-goldendust-handler", - runtime=_lambda.Runtime.PYTHON_3_10, - description="Request handler for Golden dust by NCG for PC users", - code=_lambda.AssetCode("worker/worker", exclude=exclude_list), - handler="golden_dust_by_ncg.handle_request", - layers=[layer], - role=role, - vpc=shared_stack.vpc, - timeout=cdk_core.Duration.minutes(8), - environment=env, - memory_size=512, - reserved_concurrent_executions=1, - ) - ten_minute_event_rule.add_target(_event_targets.LambdaFunction(gd_handler)) - - # Golden dust unload Tx. tracker - gd_tracker = _lambda.Function( - self, f"{config.stage}-9c-iap-goldendust-tracker-function", - function_name=f"{config.stage}-9c-iap-goldendust-tracker", - runtime=_lambda.Runtime.PYTHON_3_10, - description=f"Tx. status tracker for golden dust unload for PC users", - code=_lambda.AssetCode("worker/worker", exclude=exclude_list), - handler="golden_dust_by_ncg.track_tx", - layers=[layer], - role=role, - vpc=shared_stack.vpc, - timeout=cdk_core.Duration.seconds(50), - environment=env, - memory_size=256, - ) - minute_event_rule.add_target(_event_targets.LambdaFunction(gd_tracker)) + # Event finished + # # Golden dust by NCG handler + # env["GOLDEN_DUST_REQUEST_SHEET_ID"] = config.golden_dust_request_sheet_id + # env["GOLDEN_DUST_WORK_SHEET_ID"] = config.golden_dust_work_sheet_id + # env["FORM_SHEET"] = config.form_sheet + # gd_handler = _lambda.Function( + # self, f"{config.stage}-9c-iap-goldendust-handler-function", + # function_name=f"{config.stage}-9c-iap-goldendust-handler", + # runtime=_lambda.Runtime.PYTHON_3_10, + # description="Request handler for Golden dust by NCG for PC users", + # code=_lambda.AssetCode("worker/worker", exclude=exclude_list), + # handler="golden_dust_by_ncg.handle_request", + # layers=[layer], + # role=role, + # vpc=shared_stack.vpc, + # timeout=cdk_core.Duration.minutes(8), + # environment=env, + # memory_size=512, + # reserved_concurrent_executions=1, + # ) + # ten_minute_event_rule.add_target(_event_targets.LambdaFunction(gd_handler)) + # + # # Golden dust unload Tx. tracker + # gd_tracker = _lambda.Function( + # self, f"{config.stage}-9c-iap-goldendust-tracker-function", + # function_name=f"{config.stage}-9c-iap-goldendust-tracker", + # runtime=_lambda.Runtime.PYTHON_3_10, + # description=f"Tx. status tracker for golden dust unload for PC users", + # code=_lambda.AssetCode("worker/worker", exclude=exclude_list), + # handler="golden_dust_by_ncg.track_tx", + # layers=[layer], + # role=role, + # vpc=shared_stack.vpc, + # timeout=cdk_core.Duration.seconds(50), + # environment=env, + # memory_size=256, + # ) + # minute_event_rule.add_target(_event_targets.LambdaFunction(gd_tracker)) # Manual unload function # This function does not have trigger. Go to AWS console and run manually. @@ -280,6 +291,21 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: memory_size=512, ) + token_issuer = _lambda.Function( + self, f"{config.stage}-9c-iap-token-issue-function", + function_name=f"{config.stage}-9c-iap-issue-token", + runtime=_lambda.Runtime.PYTHON_3_10, + description=f"Execute IssueTokensFromGarage action", + code=_lambda.AssetCode("worker/worker", exclude=exclude_list), + handler="issue_tokens.issue", + layers=[layer], + role=role, + vpc=shared_stack.vpc, + timeout=cdk_core.Duration.seconds(300), # 5min + environment=env, + memory_size=256, + ) + lambda_warmer = _lambda.Function( self, f"{config.stage}-9c-iap-lambda-warmer", function_name=f"{config.stage}-9c-iap-lambda-warmer",