diff --git a/packages-python/cactus_validator_socketio/README.md b/packages-python/cactus_validator_socketio/README.md new file mode 100644 index 0000000000..cf44f725c5 --- /dev/null +++ b/packages-python/cactus_validator_socketio/README.md @@ -0,0 +1,42 @@ +# Starting Indy Valdiator + +## Starting + +### Build and run containers + +go to cactus/tools/docker/indy-testnet and + +``` +$ cd cactus/tools/docker/indy-testnet +$ docker-compose -f ./under-construction-docker-compose.yaml up +Starting nginx ... done +Starting validator ... done +Starting indy_pool ... done +``` + +Type Ctrl + C to stop these containers. + +### Verify that containers are actually up + +Type following in the host environemnt: + +``` + $ docker ps | grep -e indy_pool -e nginx -e valipy +``` +Three containers `indy_pool`, `nginx` and `valipy` must be printed. + +### Start Indy validator server + +Run following commands (from host environment) to log into validator container: + +``` +$ sudo docker exec -it validator /bin/bash +``` + and run the server using these commands: + +``` +$ cd /root/validator +$ pip install pyyaml +$ TEST_POOL_IP=172.16.0.2 python -m main +``` + diff --git a/packages-python/cactus_validator_socketio/etc/cactus/node-settings.yaml b/packages-python/cactus_validator_socketio/etc/cactus/node-settings.yaml new file mode 100644 index 0000000000..ae36e343f0 --- /dev/null +++ b/packages-python/cactus_validator_socketio/etc/cactus/node-settings.yaml @@ -0,0 +1,2 @@ +port: 8000 +logging_dir: "" \ No newline at end of file diff --git a/packages-python/cactus_validator_socketio/etc/cactus/node-validator-registry.yaml b/packages-python/cactus_validator_socketio/etc/cactus/node-validator-registry.yaml new file mode 100644 index 0000000000..9e87144846 --- /dev/null +++ b/packages-python/cactus_validator_socketio/etc/cactus/node-validator-registry.yaml @@ -0,0 +1,3 @@ +proto: "" +url: "" +publickey: "" \ No newline at end of file diff --git a/packages-python/cactus_validator_socketio/etc/cactus/validator-001-secrets.yaml b/packages-python/cactus_validator_socketio/etc/cactus/validator-001-secrets.yaml new file mode 100644 index 0000000000..6ce11b1ec5 --- /dev/null +++ b/packages-python/cactus_validator_socketio/etc/cactus/validator-001-secrets.yaml @@ -0,0 +1,2 @@ +sign_key: "" +auth_credential: "" \ No newline at end of file diff --git a/packages-python/cactus_validator_socketio/etc/cactus/validator-001-settings.yaml b/packages-python/cactus_validator_socketio/etc/cactus/validator-001-settings.yaml new file mode 100644 index 0000000000..dcb824b19a --- /dev/null +++ b/packages-python/cactus_validator_socketio/etc/cactus/validator-001-settings.yaml @@ -0,0 +1 @@ +port: 8000 \ No newline at end of file diff --git a/packages-python/cactus_validator_socketio/testcli/package.json b/packages-python/cactus_validator_socketio/testcli/package.json new file mode 100644 index 0000000000..e5025d4429 --- /dev/null +++ b/packages-python/cactus_validator_socketio/testcli/package.json @@ -0,0 +1,8 @@ +{ + "name": "testcli", + "version": "0.0.0", + "private": true, + "dependencies": { + "socket.io-client": "^4.1.2" + } +} diff --git a/packages-python/cactus_validator_socketio/testcli/testsock.js b/packages-python/cactus_validator_socketio/testcli/testsock.js new file mode 100644 index 0000000000..d52cf54105 --- /dev/null +++ b/packages-python/cactus_validator_socketio/testcli/testsock.js @@ -0,0 +1,54 @@ +// NOTE: Run pip install socket.io-client. +const io = require('socket.io-client'); +const url = "http://localhost:8000"; +const socket = io(url, { + //transports: [ 'websocket', 'polling'] +}); + +socket.on('connect', () => { + console.log('connect'); + console.log(socket.id); + const transport = socket.io.engine.transport.name; + console.log(transport); + //socket.emit('mymessage', 'hoge'); +}); + +socket.on('mymessage', () => { + console.log('received mymessage'); +}); + +socket.on('response', (respData) => { + console.log('received response from Validator'); + // console.log(`##response : ${JSON.stringify(respData)}`); + const respObj = JSON.parse(respData); + console.log(`response(Obj) is ${JSON.stringify(respObj)}`); +}); + +// socket.emit('nop'); +socket.emit('startMonitor'); + +socket.emit('test-event'); + +setTimeout(() => { + console.log('call request2!'); + + args_schema = "{\"reqId\":1624420883330729255,\"identifier\":\"8TAyhNonMhWGNRiRHRhs5C\",\"operation\":{\"type\":\"107\",\"dest\":\"HxuDdJmNSxboTCwLf8FN1j\",\"data\":{\"name\":\"Job-Certificate\",\"version\":\"0.2\"}},\"protocolVersion\":2}"; + args_credential_definition = "{\"reqId\":1624437652192258330,\"identifier\":\"JbYRZdQeSkjxYKW61NReHN\",\"operation\":{\"type\":\"108\",\"ref\":14,\"signature_type\":\"CL\",\"origin\":\"DnZczRFF5iyiYgLMeFdHpk\",\"tag\":\"TAG1\"},\"protocolVersion\":2}"; + + // Call Validator + const requestData = { + contract: {"channelName": "mychannel", "contractName": "indysomething"}, + method: {type: "evaluateTransaction", command: "indy_ledger_submit_request"}, + args: args_credential_definition, + reqId: "reqID_test" + }; + + socket.emit('request2', requestData); + +}, 2000); + + +setTimeout(() => { + console.log('call nop!'); + socket.emit('nop'); +}, 2000); diff --git a/packages-python/cactus_validator_socketio/validator-python/.gitignore b/packages-python/cactus_validator_socketio/validator-python/.gitignore new file mode 100644 index 0000000000..d9d719b440 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.log diff --git a/packages-python/cactus_validator_socketio/validator-python/main.py b/packages-python/cactus_validator_socketio/validator-python/main.py new file mode 100644 index 0000000000..331da8f990 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/main.py @@ -0,0 +1,6 @@ +from validator_socketio_module.SocketIoValidator import SocketIoValidator + +if __name__ == '__main__': + validator = SocketIoValidator() + validator.init_indy() + validator.run() diff --git a/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/AbstractConnector.py b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/AbstractConnector.py new file mode 100644 index 0000000000..ef77d5f646 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/AbstractConnector.py @@ -0,0 +1,46 @@ +from abc import ABCMeta, abstractmethod + +class AbstractConnector: + @abstractmethod + def __init__(self): + pass + + @abstractmethod + def getValidatorInformation(self, validatorURL): + """Get the validator information including version, name, ID, and other information""" + pass + + @abstractmethod + def sendSignedTransaction(self, signedTransaction): + """Request a verifier to execute a ledger operation""" + pass + + @abstractmethod + def getBalance(self, address): + """Get balance of an account for native token on a leder""" + pass + + @abstractmethod + def execSyncFunction(self, address, funcName, args): + """Execute a synchronous function held by a smart contract""" + pass + + @abstractmethod + def startMonitor(self, clientId, cb): + """Request a validator to start monitoring ledger""" + pass + + @abstractmethod + def stopMonitor(self, clientId): + """Request a validator to stop monitoring ledger""" + pass + + @abstractmethod + def cb(self, callbackData): + """Callback function to call when receiving data from Ledger""" + pass + + @abstractmethod + def nop(self): + """Nop function for testing""" + pass diff --git a/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/IndyConnector.py b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/IndyConnector.py new file mode 100644 index 0000000000..af8b391816 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/IndyConnector.py @@ -0,0 +1,81 @@ +from abc import ABCMeta, abstractmethod + +import json +import time +from indy import ledger +import asyncio + +from .AbstractConnector import AbstractConnector + +class IndyConnector(AbstractConnector): + def __init__(self, socketio, sessionid, indy_dic): + self.moduleName = "IndyConnector" + self.indy_dic = indy_dic + print(f"##{self.moduleName}.__init__") + + def getValidatorInformation(self, validatorURL): + """Get the validator information including version, name, ID, and other information""" + print(f"##{self.moduleName}.getValidatorInformation()") + + def sendSignedTransaction(self, signedTransaction): + """Request a verifier to execute a ledger operation""" + print(f"##{self.moduleName}.sendSignedTransaction()") + + def getBalance(self, address): + """Get balance of an account for native token on a leder""" + print(f"##{self.moduleName}.getBalance()") + + def execSyncFunction(self, address, funcName, args): + """Execute a synchronous function held by a smart contract""" + print(f"##{self.moduleName}.execSyncFunction()") + + command = args['method']['command'] + if command== 'indy_ledger_submit_request': + return self.load_schema_or_credential_definition(args['args']) + + print(f"##{self.moduleName} unknown command : {command}") + return "unknown command." + + + def load_schema_or_credential_definition(self, args): + """Execute a synchronous function held by a smart contract""" + print(f"##{self.moduleName}.load_schema_or_credential_definition()") + + pool_handle = self.indy_dic['pool_handle'] + responseStr = self.run_coroutine_ensure_previous_request_applied(pool_handle, args, lambda response: response['result']['data'] is not None) + + response = json.loads(responseStr) + + return response + + def startMonitor(self, clientId, cb): + """Request a validator to start monitoring ledger""" + print(f"##{self.moduleName}.startMonitor()") + + def stopMonitor(self, clientId): + """Request a validator to stop monitoring ledger""" + print(f"##{self.moduleName}.stopMonitor()") + + def cb(self, callbackData): + """Callback function to call when receiving data from Ledger""" + print(f"##{self.moduleName}.cb()") + + def nop(self): + """Nop function for testing""" + print(f"##{self.moduleName}.nop()") + + async def ensure_previous_request_applied(self, pool_handle, checker_request, checker): + for _ in range(3): + response = json.loads(await ledger.submit_request(pool_handle, checker_request)) + try: + if checker(response): + return json.dumps(response) + except TypeError: + pass + time.sleep(5) + + def run_coroutine_ensure_previous_request_applied(self, pool_handle, checker_request, checker, loop=None): + if loop is None: + loop = asyncio.get_event_loop() + results = loop.run_until_complete(self.ensure_previous_request_applied(pool_handle, checker_request, checker)) + return results diff --git a/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/Settings.py b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/Settings.py new file mode 100644 index 0000000000..2e02003d29 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/Settings.py @@ -0,0 +1,60 @@ +import dataclasses +import yaml + +rootPath = "/" + +pathNodeSettings = rootPath + "etc/cactus/node-settings.yaml" +pathNodeValidatorRegistry = rootPath + "etc/cactus/node-validator-registry.yaml" +pathValidatorSettings = rootPath + "etc/cactus/validator-001-settings.yaml" +pathValidatorSecrets = rootPath + "etc/cactus/validator-001-secrets.yaml" + +#dataclass for validator--settings.yml +#data members should be equal to yml +@dataclasses.dataclass +class NodeSettings: + port: int + logging_dir: str + +#dataclass for validator--settings.yml +#data members should be equal to yml +@dataclasses.dataclass +class NodeValidatorRegistry: + proto: str + url: str + publickey: str + +#dataclass for validator--settings.yml +#data members should be equal to yml +@dataclasses.dataclass +class ValidatorSettings: + port: int + +#dataclass for validator--settings.yml +#data members should be equal to yml +@dataclasses.dataclass +class ValidatorSecrets: + sign_key: str + auth_credential: str + +@dataclasses.dataclass +class Settings: + nodeSettings: NodeSettings = None + nodeValidatorRegistry: NodeValidatorRegistry = None + validatorSettings: ValidatorSettings = None + validatorSecrets: ValidatorSecrets = None + + # this method is automatically implemented after generate object + def __post_init__(self): + + self.validatorSettings = ValidatorSettings(**(self.loadYaml(pathNodeSettings))) + self.validatorSettings = ValidatorSettings(**(self.loadYaml(pathNodeValidatorRegistry))) + self.validatorSettings = ValidatorSettings(**(self.loadYaml(pathValidatorSettings))) + self.validatorSettings = ValidatorSettings(**(self.loadYaml(pathValidatorSecrets))) + + + def loadYaml(self, yamlFilePath): + # load usersettings file + with open(pathValidatorSettings) as yamlFile: + yamlObj = yaml.safe_load(yamlFile) + + return yamlObj diff --git a/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/SocketIoValidator.py b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/SocketIoValidator.py new file mode 100644 index 0000000000..305e38ace3 --- /dev/null +++ b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/SocketIoValidator.py @@ -0,0 +1,119 @@ +from flask import Flask, render_template, request +from flask_socketio import SocketIO +import json +import time +import asyncio + +# from indy import ledger, pool +from indy import pool + +from .Settings import Settings +from .IndyConnector import IndyConnector + +class SocketIoValidator: + def __init__(self): + self.moduleName = "SocketIoValidator" + self.the_cb = None + self.indy_dic = {} + + # load settings + self.settings = Settings() + + self.app = Flask(__name__) + self.app.config['SECRET_KEY'] = 'secret!' + # socketio = SocketIO(app) + self.socketio = SocketIO(self.app, host='0.0.0.0', port=self.settings.validatorSettings.port, logger=True, engineio_logger=True) + + + @self.socketio.on('connect') + def handle_connect(): + # print('on connect (sessionid: ' + request.sid + ')') + print(f'on connect (sessionid: {request.sid})') + self.session_dict[request.sid] = self.getValidatorInstance() + + @self.socketio.on('disconnect') + def handle_disconnect(): + print('on disconnect') + del self.session_dict[request.sid] + + @self.socketio.on('startMonitor') + def handle_startMonitor(): + print('on startMonitor') + clientId = None + cb = None + self.session_dict[request.sid].startMonitor(clientId, cb) + def the_cb(resp): return self.cb_helper(request.sid, resp) + + @self.socketio.on('stopMonitor') + def handle_stopMonitor(): + print('on startMonitor') + self.session_dict[request.sid].stopMonitor() + + @self.socketio.on('nop') + def handle_nop(): + print('received nop') + self.session_dict[request.sid].nop() + + @self.socketio.on('test-event') + def handle_event(): + self.session_dict[request.sid].cb('data-from-blockchain') + + @self.socketio.on('request2') + def handle_execSyncFunction(requestData): + print('received request2') + print(f"##requestData: {requestData}") + + result = self.session_dict[request.sid].execSyncFunction(None, None, requestData) + + resp_obj = self.build_res_obj(200, requestData["reqId"], result) + respJson = json.dumps(resp_obj) + self.socketio.emit("response", respJson); + + self.session_dict = {} + + def build_res_obj(self, status_code, req_id, result): + ## signed_results = ValidatorAuthentication.sign(result) + signed_results = result + + res_obj = {} + res_obj["status"] = status_code + res_obj["data"] = signed_results + if req_id is not None: + res_obj["id"] = req_id + + return res_obj + + def run(self): + """Run Validator""" + # self.init_indy(); + self.socketio.run(self.app, host='0.0.0.0', port=self.settings.validatorSettings.port) + + def cb_helper(self, sessionid, answer): + print(f'cb helper: {self.session_dict[request.sid]}') + return self.session_dict[sessionid].cb(answer) + + def getValidatorInstance(self): + print(f'##called getValidatorInstance()') + return IndyConnector(self.socketio, request.sid, self.indy_dic) + + # for INDY + def init_indy(self): + """ Initialization process for INDY """ + print(f'##called init_indy()') + self.run_coroutine(self.open_pool) + time.sleep(1) + + # for INDY + async def open_pool(self): + # open the pool and get handler + # self.pool_handle = await pool.open_pool_ledger('pool1', None) + self.indy_dic['pool_handle'] = await pool.open_pool_ledger('pool1', None) + print(f"##{self.moduleName} set pool_handle.") + + # for INDY + def run_coroutine(self, coroutine, loop=None): + print(f'##called run_coroutine()') + if loop is None: + loop = asyncio.get_event_loop() + loop.run_until_complete(coroutine()) + diff --git a/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/__init__.py b/packages-python/cactus_validator_socketio/validator-python/validator_socketio_module/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/docker/indy-testnet/.env b/tools/docker/indy-testnet/.env index 268bff08d1..8e6bd81462 100644 --- a/tools/docker/indy-testnet/.env +++ b/tools/docker/indy-testnet/.env @@ -1,4 +1,4 @@ -INDY_SDK_ROOT=../../indy-sdk/ci/ -HTTP_PROXY= -NO_PROXY= +INDY_SDK_ROOT=../../../../indy-sdk/ci +HTTP_PROXY=$HTTP_PROXY +NO_PROXY=$NO_PROXY diff --git a/tools/docker/indy-testnet/docker-compose.yaml b/tools/docker/indy-testnet/docker-compose.yaml index 084909c332..13368fb879 100644 --- a/tools/docker/indy-testnet/docker-compose.yaml +++ b/tools/docker/indy-testnet/docker-compose.yaml @@ -5,6 +5,8 @@ services: container_name: indy_pool image: indy_pool build: + # context: ./indy_pool + # dockerfile: indy_pool.dockerfile context: ${INDY_SDK_ROOT} dockerfile: indy-pool.dockerfile args: @@ -13,18 +15,18 @@ services: - HTTPS_PROXY=$HTTP_PROXY - https_proxy=$HTTP_PROXY - FTP_PROXY=$HTTP_PROXY - - ftp_proxy=$HTTP_proxy + - ftp_proxy=$HTTP_PROXY - NO_PROXY=$NO_PROXY - - no_proxy=$no_proxy + - no_proxy=$NO_PROXY environment: - HTTP_PROXY=$HTTP_PROXY - http_proxy=$HTTP_PROXY - HTTPS_PROXY=$HTTP_PROXY - https_proxy=$HTTP_PROXY - FTP_PROXY=$HTTP_PROXY - - ftp_proxy=$HTTP_proxy + - ftp_proxy=$HTTP_PROXY - NO_PROXY=$NO_PROXY - - no_proxy=$no_proxy + - no_proxy=$NO_PROXY ports: - "9701:9701" - "9702:9702" @@ -42,10 +44,14 @@ services: nginx: container_name: nginx - build: - context: ./nginx - dockerfile: custom-nginx.dockerfile - image: sfuji822/nginx + # build: + # context: ./nginx + # dockerfile: custom-nginx.dockerfile + image: nginx:stable-alpine + volumes: + - type: bind + source: "./nginx/nginx.conf" + target: "/etc/nginx/nginx.conf" ports: - "10080:80" networks: @@ -55,16 +61,43 @@ services: validator: container_name: validator build: - context: ./validator - dockerfile: valipy.dockerfile + context: ../../../packages-python/cactus_validator_socketio/ + dockerfile: build/valipy.dockerfile + args: + - pool_ip=172.16.0.2 + - HTTP_PROXY=$HTTP_PROXY + - http_proxy=$HTTP_PROXY + - HTTPS_PROXY=$HTTP_PROXY + - https_proxy=$HTTP_PROXY + - FTP_PROXY=$HTTP_PROXY + - ftp_proxy=$HTTP_proxy + - NO_PROXY=$NO_PROXY + - no_proxy=$no_proxy + environment: + - HTTP_PROXY=$HTTP_PROXY + - http_proxy=$HTTP_PROXY + - HTTPS_PROXY=$HTTP_PROXY + - https_proxy=$HTTP_PROXY + - FTP_PROXY=$HTTP_PROXY + - ftp_proxy=$HTTP_proxy + - NO_PROXY=$NO_PROXY + - no_proxy=$no_proxy image: valipy ports: - - "80:80" + - "8000:8000" networks: indy_net: ipv4_address: 172.16.0.4 - - + volumes: + - type: bind + source: "../../../packages-python/cactus_validator_socketio/validator-python/" + target: "/root/validator" + - type: bind + source: "../../../packages-python/cactus_validator_socketio/etc/cactus/" + target: "/etc/cactus" + - type: bind + source: "../../../../indy-sdk/" + target: "/root/indy-sdk" networks: indy_net: name: indy_net diff --git a/tools/docker/indy-testnet/nginx/nginx.conf b/tools/docker/indy-testnet/nginx/nginx.conf new file mode 100644 index 0000000000..9850278be5 --- /dev/null +++ b/tools/docker/indy-testnet/nginx/nginx.conf @@ -0,0 +1,50 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + charset utf-8; + + location / { + proxy_pass http://validator:8000; + include uwsgi_params; + client_max_body_size 20M; + } + + location /socket.io/ { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_pass http://validator:8000; + } + } + + +} diff --git a/tools/docker/indy-testnet/validator/valipy.dockerfile b/tools/docker/indy-testnet/validator/valipy.dockerfile deleted file mode 100644 index 9c8d07433d..0000000000 --- a/tools/docker/indy-testnet/validator/valipy.dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -FROM ubuntu:xenial - -RUN apt-get update && apt-get install -y -RUN apt-get install -y \ - apt-utils \ - apt-transport-https \ - software-properties-common \ - wget \ - curl \ - telnet \ - sudo \ - build-essential \ - libffi-dev \ - libssl-dev \ - zlib1g-dev \ - liblzma-dev \ - libbz2-dev \ - libreadline-dev \ - libsqlite3-dev \ - git \ - gnupg - -# install python 3.9.5 -#RUN apt-get install -y python3 python3-pip -RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv -RUN echo 'export PATH="$HOME"/.pyenv/bin:$PATH' >> ~/.rc -RUN . ~/.rc && pyenv install 3.9.5 -RUN echo 'eval "$(pyenv init -)"' >> ~/.rc -RUN echo 'eval "$(pyenv init --path)"' >> ~/.rc -RUN cat ~/.rc >> ~/.bashrc -RUN . ~/.rc && pyenv global 3.9.5 - -# install libindy. reference: https://github.com/hyperledger/indy-sdk#ubuntu-based-distributions-ubuntu-1604-and-1804 -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 -RUN add-apt-repository "deb https://repo.sovrin.org/sdk/deb xenial stable" -RUN apt-get update -RUN apt-get install -y libindy libnullpay libvcx indy-cli - -# indy wrapper for python3 -RUN . ~/.rc && pip install python3-indy - -CMD [ "tail", "-f", "/dev/null" ]