Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
Merge pull request #551 from icon-project/develop
Browse files Browse the repository at this point in the history
Merge pull request from icon-project/develop
  • Loading branch information
cow-hs committed Feb 16, 2021
2 parents 73c8d45 + 042f9cf commit 38c571c
Show file tree
Hide file tree
Showing 69 changed files with 3,347 additions and 847 deletions.
30 changes: 30 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
SHELL := /bin/bash

.PHONY: help
help:
@awk '/^##/{c=substr($$0,3);next}c&&/^[[:alpha:]][-_[:alnum:]]+:/{print substr($$1,1,index($$1,":")),c}1{c=0}'\
$(MAKEFILE_LIST) | column -s: -t

## Run pytest
test:
@python3 -m pytest -ra tests/ || exit -1

## Clean all - clean-build
clean: clean-build

clean-build:
@$(RM) -r build/ dist/
@$(RM) -r .eggs/ eggs/ *.egg-info/

## Build python wheel
build: clean-build
@if [ "$$(python -c 'import sys; print(sys.version_info[0])')" != 3 ]; then \
@echo "The script should be run on python3."; \
exit -1; \
fi

@if ! python -c 'import wheel' &> /dev/null; then \
pip install wheel; \
fi

python3 setup.py bdist_wheel
2 changes: 1 addition & 1 deletion iconservice/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.7.7'
__version__ = '1.8.4'
6 changes: 6 additions & 0 deletions iconservice/base/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ExceptionCode(IntEnum):
INVALID_PACKAGE = 14
SERVICE_NOT_READY = 15
INTERNAL_SERVICE_ERROR = 16
INVALID_BALANCE = 17

# Caused by revert call or user-defined exception.
SCORE_ERROR = 32
Expand Down Expand Up @@ -177,6 +178,11 @@ def __init__(self, message: Optional[str]):
super().__init__(message, ExceptionCode.INTERNAL_SERVICE_ERROR)


class InvalidBalanceException(IconServiceBaseException):
def __init__(self, message: Optional[str]):
super().__init__(message, ExceptionCode.INVALID_BALANCE)


class IconScoreException(IconServiceBaseException):
# All the user-defined exceptions should inherit from this exception including revert call
def __init__(self, message: Optional[str], index: int = 0):
Expand Down
1 change: 1 addition & 0 deletions iconservice/base/type_converter_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class ConstantKeys:
FROM = "from"
TO = "to"
VALUE = "value"
NID = "nid"
STEP_LIMIT = "stepLimit"
FEE = "fee"
NONCE = "nonce"
Expand Down
203 changes: 2 additions & 201 deletions iconservice/database/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@
from iconcommons.logger import Logger

from .batch import TransactionBatchValue
from ..base.exception import DatabaseException, InvalidParamsException, AccessDeniedException
from ..base.exception import DatabaseException
from ..icon_constant import ICON_DB_LOG_TAG, IconScoreContextType
from ..iconscore.context.context import ContextGetter

if TYPE_CHECKING:
from ..base.address import Address
from ..iconscore.icon_score_context import IconScoreContext


Expand Down Expand Up @@ -192,7 +190,7 @@ def __init__(self, db: 'KeyValueDatabase', is_shared: bool = False) -> None:
:param db: KeyValueDatabase instance
"""
self.key_value_db = db
# True: this db is shared with all SCOREs
# True: this db is shared with all smart contracts
self._is_shared = is_shared

def get(self, context: Optional['IconScoreContext'], key: bytes) -> bytes:
Expand Down Expand Up @@ -358,200 +356,3 @@ def from_path(path: str,
create_if_missing: bool = True) -> 'MetaContextDatabase':
db = KeyValueDatabase.from_path(path, create_if_missing)
return MetaContextDatabase(db)


class IconScoreDatabase(ContextGetter):
"""It is used in IconScore
IconScore can access its states only through IconScoreDatabase
"""

def __init__(self,
address: 'Address',
context_db: 'ContextDatabase',
prefix: bytes = None) -> None:
"""Constructor
:param address: the address of SCORE which this db is assigned to
:param context_db: ContextDatabase
:param prefix:
"""
self.address = address
self._prefix = prefix
self._context_db = context_db
self._observer: Optional[DatabaseObserver] = None

self._prefix_hash_key: bytes = self._make_prefix_hash_key()

def _make_prefix_hash_key(self) -> bytes:
data = [self.address.to_bytes()]
if self._prefix is not None:
data.append(self._prefix)
return b'|'.join(data)

def get(self, key: bytes) -> bytes:
"""
Gets the value for the specified key
:param key: key to retrieve
:return: value for the specified key, or None if not found
"""
hashed_key = self._hash_key(key)
value = self._context_db.get(self._context, hashed_key)
if self._observer:
self._observer.on_get(self._context, key, value)
return value

def put(self, key: bytes, value: bytes):
"""
Sets a value for the specified key.
:param key: key to set
:param value: value to set
"""
self._validate_ownership()
hashed_key = self._hash_key(key)
if self._observer:
old_value = self._context_db.get(self._context, hashed_key)
if value:
self._observer.on_put(self._context, key, old_value, value)
elif old_value:
# If new value is None, then deletes the field
self._observer.on_delete(self._context, key, old_value)
self._context_db.put(self._context, hashed_key, value)

def get_sub_db(self, prefix: bytes) -> 'IconScoreSubDatabase':
"""
Returns sub db with a prefix
:param prefix: The prefix used by this sub db.
:return: sub db
"""
if prefix is None:
raise InvalidParamsException(
'Invalid params: '
'prefix is None in IconScoreDatabase.get_sub_db()')

if self._prefix is not None:
prefix = b'|'.join((self._prefix, prefix))

return IconScoreSubDatabase(self.address, self, prefix)

def delete(self, key: bytes):
"""
Deletes the key/value pair for the specified key.
:param key: key to delete
"""
self._validate_ownership()
hashed_key = self._hash_key(key)
if self._observer:
old_value = self._context_db.get(self._context, hashed_key)
# If old value is None, won't fire the callback
if old_value:
self._observer.on_delete(self._context, key, old_value)
self._context_db.delete(self._context, hashed_key)

def close(self):
self._context_db.close(self._context)

def set_observer(self, observer: 'DatabaseObserver'):
self._observer = observer

def _hash_key(self, key: bytes) -> bytes:
"""All key is hashed and stored
to StateDB to avoid key conflicts among SCOREs
:params key: key passed by SCORE
:return: key bytes
"""

return b'|'.join((self._prefix_hash_key, key))

def _validate_ownership(self):
"""Prevent a SCORE from accessing the database of another SCORE
"""
if self._context.current_address != self.address:
raise AccessDeniedException("Invalid database ownership")


class IconScoreSubDatabase(object):
def __init__(self, address: 'Address', score_db: 'IconScoreDatabase', prefix: bytes):
"""Constructor
:param address: the address of SCORE which this db is assigned to
:param score_db: IconScoreDatabase
:param prefix:
"""
if prefix is None or len(prefix) == 0:
raise InvalidParamsException("Invalid prefix")

self.address = address
self._prefix = prefix
self._score_db = score_db

self._prefix_hash_key: bytes = self._make_prefix_hash_key()

def _make_prefix_hash_key(self) -> bytes:
data = []
if self._prefix is not None:
data.append(self._prefix)
return b'|'.join(data)

def get(self, key: bytes) -> bytes:
"""
Gets the value for the specified key
:param key: key to retrieve
:return: value for the specified key, or None if not found
"""
hashed_key = self._hash_key(key)
return self._score_db.get(hashed_key)

def put(self, key: bytes, value: bytes):
"""
Sets a value for the specified key.
:param key: key to set
:param value: value to set
"""
hashed_key = self._hash_key(key)
self._score_db.put(hashed_key, value)

def get_sub_db(self, prefix: bytes) -> 'IconScoreSubDatabase':
"""
Returns sub db with a prefix
:param prefix: The prefix used by this sub db.
:return: sub db
"""
if prefix is None:
raise InvalidParamsException("Invalid prefix")

if self._prefix is not None:
prefix = b'|'.join((self._prefix, prefix))

return IconScoreSubDatabase(self.address, self._score_db, prefix)

def delete(self, key: bytes):
"""
Deletes the key/value pair for the specified key.
:param key: key to delete
"""
hashed_key = self._hash_key(key)
self._score_db.delete(hashed_key)

def close(self):
self._score_db.close()

def _hash_key(self, key: bytes) -> bytes:
"""All key is hashed and stored
to StateDB to avoid key conflicts among SCOREs
:params key: key passed by SCORE
:return: key bytes
"""

return b'|'.join((self._prefix_hash_key, key))
21 changes: 20 additions & 1 deletion iconservice/icon_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ class Revision(Enum):

FIX_BALANCE_BUG = 11

LATEST = 11
BURN_V2_ENABLED = 12
IMPROVED_PRE_VALIDATOR = 12
VERIFY_ASSET_INTEGRITY = 12
USE_RLP = 12

LATEST = 12


RC_DB_VERSION_0 = 0
Expand Down Expand Up @@ -473,3 +478,17 @@ class RPCMethod:
ICX_SEND_TRANSACTION = 'icx_sendTransaction'
DEBUG_ESTIMATE_STEP = "debug_estimateStep"
DEBUG_GET_ACCOUNT = "debug_getAccount"


class DataType:
CALL = "call"
DEPLOY = "deploy"
DEPOSIT = "deposit"
MESSAGE = "message"
NONE = None

_TYPES = {CALL, DEPLOY, DEPOSIT, MESSAGE, NONE}

@classmethod
def contains(cls, value: str) -> bool:
return value in cls._TYPES
21 changes: 8 additions & 13 deletions iconservice/icon_inner_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import asyncio
import json
from concurrent.futures.thread import ThreadPoolExecutor
from typing import Any, TYPE_CHECKING
from typing import Any, TYPE_CHECKING, Optional

from earlgrey import message_queue_task, MessageQueueStub, MessageQueueService

Expand Down Expand Up @@ -409,8 +409,9 @@ async def validate_transaction(self, request: dict):

def _validate_transaction(self, request: dict):
try:
Logger.info(tag=_TAG, msg=f'validate_transaction Request: {request}')
converted_request = TypeConverter.convert(request, ParamType.VALIDATE_TRANSACTION)
self._icon_service_engine.validate_transaction(converted_request)
self._icon_service_engine.validate_transaction(converted_request, request)
response = MakeResponse.make_response(ExceptionCode.OK)
except FatalException as e:
self._log_exception(e, _TAG)
Expand Down Expand Up @@ -448,12 +449,9 @@ def make_error_response(code: Any, message: str) -> dict:
class IconScoreInnerService(MessageQueueService[IconScoreInnerTask]):
TaskType = IconScoreInnerTask

def _callback_connection_lost_callback(self, connection: 'RobustConnection'):
Logger.error("MQ Connection lost. [Service]")
# self.clean_close()

def _callback_connection_reconnect_callback(self, connection: 'RobustConnection'):
Logger.error("MQ Connection reconnect. [Service]")
def _callback_connection_close(self, sender, exc: Optional[BaseException], *args, **kwargs):
Logger.error(tag=_TAG, msg=f"[Inner Service] connection closed. {exc}")
self.clean_close()

def clean_close(self):
Logger.debug(tag=_TAG, msg="icon service will be closed")
Expand All @@ -463,8 +461,5 @@ def clean_close(self):
class IconScoreInnerStub(MessageQueueStub[IconScoreInnerTask]):
TaskType = IconScoreInnerTask

def _callback_connection_lost_callback(self, connection: 'RobustConnection'):
Logger.error("MQ Connection lost. [Stub]")

def _callback_connection_reconnect_callback(self, connection: 'RobustConnection'):
Logger.error("MQ Connection reconnect. [Service]")
def _callback_connection_close(self, sender, exc: Optional[BaseException], *args, **kwargs):
Logger.error(tag=_TAG, msg=f"[Inner Stub] connection closed. {exc}")
6 changes: 3 additions & 3 deletions iconservice/icon_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
import signal
import sys

import aio_pika
import pkg_resources
import setproctitle
from earlgrey import MessageQueueService, aio_pika
from earlgrey import MessageQueueService

from iconcommons.icon_config import IconConfig
from iconcommons.logger import Logger

from iconservice.base.exception import FatalException
from iconservice.icon_config import default_icon_config, check_config, args_to_dict
from iconservice.icon_constant import ICON_SERVICE_PROCTITLE_FORMAT, ICON_SCORE_QUEUE_NAME_FORMAT, ConfigKey
Expand Down Expand Up @@ -200,7 +201,6 @@ async def _check_rabbitmq(amqp_target: str):
amqp_user_name = os.getenv("AMQP_USERNAME", "guest")
amqp_password = os.getenv("AMQP_PASSWORD", "guest")
connection = await aio_pika.connect(host=amqp_target, login=amqp_user_name, password=amqp_password)
connection.connect()
except ConnectionRefusedError:
Logger.error("rabbitmq-service disable", _TAG)
exit(0)
Expand Down

0 comments on commit 38c571c

Please sign in to comment.