ethernity-cloud-sdk-py 0.2.45__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {ethernity_cloud_sdk_py-0.2.45/ethernity_cloud_sdk_py.egg-info → ethernity_cloud_sdk_py-0.3.0}/PKG-INFO +1 -1
  2. ethernity_cloud_sdk_py-0.3.0/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/securelock.py.tmpl +468 -0
  3. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build.py +10 -10
  4. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/ipfs_client.py +2 -2
  5. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/templates/src/ethernity_task.py +1 -1
  6. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0/ethernity_cloud_sdk_py.egg-info}/PKG-INFO +1 -1
  7. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/setup.py +1 -1
  8. ethernity_cloud_sdk_py-0.2.45/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/securelock.py.tmpl +0 -429
  9. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/LICENSE +0 -0
  10. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/MANIFEST.in +0 -0
  11. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/README.md +0 -0
  12. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/__init__.py +0 -0
  13. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/cli.py +0 -0
  14. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/__init__.py +0 -0
  15. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/__pycache__/__init__.cpython-311.pyc +0 -0
  16. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/__pycache__/build.cpython-311.pyc +0 -0
  17. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/__pycache__/config.cpython-311.pyc +0 -0
  18. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/build.py +0 -0
  19. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/config.py +0 -0
  20. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/enums.py +0 -0
  21. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/init.py +0 -0
  22. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/private_key.py +0 -0
  23. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/publish.py +0 -0
  24. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/__init__.py +0 -0
  25. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/Dockerfile.base +0 -0
  26. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/Dockerfile.base.tpl +0 -0
  27. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/Dockerfile.tpl +0 -0
  28. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/scripts/binary-fs-build.sh +0 -0
  29. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/cert1-ca1-clean.crt +0 -0
  30. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/cert1-ca1-clean.key +0 -0
  31. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/enclave_pub_cert.pem +0 -0
  32. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/input.txt +0 -0
  33. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/payload.py +0 -0
  34. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/public-cert-clean.pem +0 -0
  35. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/result.txt +0 -0
  36. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/app/transaction.txt +0 -0
  37. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/etny_crypto.py +0 -0
  38. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/etny_exec.py +0 -0
  39. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/etny_exec_flask.py +0 -0
  40. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/etny_exec_serv.py +0 -0
  41. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/image_registry.abi +0 -0
  42. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/key_generation.py +0 -0
  43. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/models.py +0 -0
  44. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/pox.abi +0 -0
  45. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/build/securelock/src/swift_stream_service.py +0 -0
  46. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/publish.py +0 -0
  47. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/__init__.py +0 -0
  48. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/docker-compose-final.yml.tmpl +0 -0
  49. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/docker-compose-swift-stream.yml.tmpl +0 -0
  50. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/docker-compose.yml.tmpl +0 -0
  51. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/etny-securelock-test.yaml.tpl +0 -0
  52. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/image_registry.abi +0 -0
  53. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/image_registry.py +0 -0
  54. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/image_registry_runner.py +0 -0
  55. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/pynithy/run/public_key_service.py +0 -0
  56. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/commands/spinner.py +0 -0
  57. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/templates/src/serverless/Dockerfile.serverless +0 -0
  58. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/templates/src/serverless/__init__.py +0 -0
  59. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/templates/src/serverless/backend.py +0 -0
  60. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py/templates/src/serverless/requirements.txt +0 -0
  61. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py.egg-info/SOURCES.txt +0 -0
  62. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py.egg-info/dependency_links.txt +0 -0
  63. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py.egg-info/entry_points.txt +0 -0
  64. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py.egg-info/requires.txt +0 -0
  65. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/ethernity_cloud_sdk_py.egg-info/top_level.txt +0 -0
  66. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/setup.cfg +0 -0
  67. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/tests/__init__.py +0 -0
  68. {ethernity_cloud_sdk_py-0.2.45 → ethernity_cloud_sdk_py-0.3.0}/tests/test_example.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ethernity-cloud-sdk-py
3
- Version: 0.2.45
3
+ Version: 0.3.0
4
4
  Summary: Ethernity Cloud SDK Python
5
5
  Home-page: https://github.com/ethernity-cloud/ethernity-cloud-sdk-py
6
6
  Author: Ethernity Cloud Team
@@ -0,0 +1,468 @@
1
+ #!/usr/bin/python
2
+ # update 29.12.2022
3
+ import binascii
4
+ import hashlib
5
+ import os
6
+ import re
7
+ import string
8
+ import io
9
+ import time
10
+ import requests
11
+ from etny_crypto import etny_crypto as crypto
12
+ from web3 import Web3
13
+ from eth_account import Account
14
+ from web3.middleware import geth_poa_middleware
15
+ from key_generation import get_wallet_address
16
+ from etny_exec import execute_task_v3, TaskStatus
17
+ from etny_exec_flask import execute_task_v4, execute_server_v4
18
+ from models import *
19
+ from swift_stream_service import SwiftStreamService
20
+ from eth_account.messages import defunct_hash_message
21
+
22
+ """
23
+ EtnySecureLock: Secure task execution handler for Ethernity Cloud.
24
+
25
+ Ethernity Cloud is a decentralized cloud computing platform that leverages blockchain for secure, private computations.
26
+ This class runs within a trusted enclave (a secure, isolated environment) to handle task execution:
27
+ - Fetches and decrypts user-submitted code (payload) and input data.
28
+ - Validates integrity via checksums and signatures.
29
+ - Executes the task securely.
30
+ - Encrypts and stores results for retrieval.
31
+ It integrates with blockchain smart contracts for metadata and Web3 for interactions, and SwiftStream for encrypted storage.
32
+ Debug mode allows local testing; otherwise, it runs in production enclave setup.
33
+ """
34
+
35
+ class EtnySecureLock:
36
+ debug = False
37
+
38
+ def __init__(self, swiftStreamClient):
39
+ """
40
+ Initialize the EtnySecureLock instance.
41
+
42
+ Sets up file paths, environment variables, blockchain connections, and storage client.
43
+ Uploads the public certificate to storage for verification.
44
+
45
+ :param swiftStreamClient: Client instance for SwiftStream storage service.
46
+ """
47
+ self.swift_stream_service = swiftStreamClient
48
+ self.version = 'v3.1'
49
+ print(f'ETNY Pynithy [{self.version}]')
50
+ self.__set_initializers()
51
+ self.save_pub_cert()
52
+ self.__load_env()
53
+ self.read_contract_abi()
54
+ self.init_web3()
55
+ self.print_env()
56
+
57
+ @staticmethod
58
+ def read_env_str(env_str):
59
+ """
60
+ Parse and set environment variables from a string (e.g., content of .env file).
61
+
62
+ Each line is expected in 'KEY=VALUE' format.
63
+
64
+ :param env_str: String containing environment variables.
65
+ """
66
+ with io.StringIO(env_str) as f:
67
+ for line in f:
68
+ key, value = line.split('=', 1)
69
+ os.environ[key] = re.sub(r'\n', '', value)
70
+
71
+ def __extract_signer(self, checksum, signature):
72
+ """
73
+ Recover the Ethereum address that signed a given checksum message.
74
+
75
+ Uses Web3's recovery function for signature verification.
76
+
77
+ :param checksum: The message (checksum) that was signed.
78
+ :param signature: The signature to recover from.
79
+ :return: The signer's Ethereum address.
80
+ """
81
+ signer = self.w3.eth.account.recoverHash(defunct_hash_message(text=checksum), signature=signature)
82
+ return signer
83
+
84
+ def __set_initializers(self):
85
+ """
86
+ Set initial values for instance variables.
87
+
88
+ Configures file paths, contract addresses, and debug mode overrides.
89
+ In Ethernity Cloud, these paths point to secure enclave-mounted volumes.
90
+ """
91
+ self.is_valid_client_data = True
92
+ self.key_file = "/private/__SECURELOCK_SESSION__/key.pem"
93
+ self.cert_file = "/app/__SECURELOCK_SESSION__/cert.pem"
94
+ self.pub_cert_file = "/app/__SECURELOCK_SESSION__/enclave_pub_cert.pem"
95
+ self.payload = 'payload.etny.securelock'
96
+ self.input = 'input.txt.securelock'
97
+ self.result_file = '/app/result.txt'
98
+ self.transaction_file = '/app/transaction.txt'
99
+ self.smart_contract_address = '__SMART_CONTRACT_ADDRESS__'
100
+ self.image_registry_address = '__IMAGE_REGISTRY_ADDRESS__'
101
+ self.chain_id = __CHAIN_ID__
102
+ self.web3_provider = '__RPC_URL__'
103
+ self.etny_bucket = "__BUCKET_NAME__"
104
+ self.trusted_zone_public_key = ''
105
+ if EtnySecureLock.debug:
106
+ self.key_file = "./app/cert1-ca1-clean.key"
107
+ self.cert_file = "./app/cert1-ca1-clean.crt"
108
+ self.payload = 'payload.py'
109
+ self.input = 'input.txt'
110
+ self.result_file = './app/result.txt'
111
+ self.transaction_file = './app/transaction.txt'
112
+ self.pub_cert_file = "./app/enclave_pub_cert.pem"
113
+
114
+ def validate_client_payload(self, payload_data, input_data):
115
+ """
116
+ Validate the integrity of client-submitted payload and input.
117
+
118
+ Fetches task metadata from the blockchain, computes checksums, and verifies signatures.
119
+ Ensures the data matches what was submitted on-chain by the task owner.
120
+
121
+ :param payload_data: Decrypted payload (code to execute).
122
+ :param input_data: Decrypted input data for the task.
123
+ """
124
+ self.get_do_request_metadata()
125
+ if self._metadata.payload_metadata_obj.checksum is not None:
126
+ payload_checksum = self.compute_sha256_checksum(payload_data)
127
+ if self._metadata.payload_metadata_obj.checksum.startswith('0x'):
128
+ checksum_signer = self.__extract_signer(payload_checksum, self._metadata.payload_metadata_obj.checksum)
129
+ transaction_checksum = payload_checksum
130
+ else:
131
+ checksum_signer = self.order_metadata.do_owner
132
+ transaction_checksum = self._metadata.payload_metadata_obj.checksum
133
+ if checksum_signer.lower() != self.order_metadata.do_owner.lower() and transaction_checksum != payload_checksum:
134
+ self.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR
135
+ self.task_result = 'PAYLOAD CHECKSUM DOES NOT MATCH THE EXPECTED VALUE'
136
+ self.is_valid_client_data = False
137
+ return
138
+ if self._metadata.input_metadata_obj.checksum is not None:
139
+ input_checksum = self.compute_sha256_checksum(input_data)
140
+ if self._metadata.input_metadata_obj.checksum.startswith('0x'):
141
+ checksum_signer = self.__extract_signer(input_checksum, self._metadata.input_metadata_obj.checksum)
142
+ transaction_checksum = input_checksum
143
+ else:
144
+ checksum_signer = self.order_metadata.do_owner
145
+ transaction_checksum = self._metadata.input_metadata_obj.checksum
146
+ if checksum_signer.lower() != self.order_metadata.do_owner.lower() and transaction_checksum != input_checksum:
147
+ self.task_code = TaskStatus.INPUT_CHECKSUM_ERROR
148
+ self.task_result = 'INPUT CHECKSUM DOES NOT MATCH THE EXPECTED VALUE'
149
+ self.is_valid_client_data = False
150
+ return
151
+
152
+ def wait_for_payload_and_input(self):
153
+ """
154
+ Wait for payload and input files to become available in storage.
155
+
156
+ Polls the SwiftStream bucket until both files are present.
157
+ """
158
+ print('Waiting for payload and input')
159
+ self.wait_for_trustedzone(self.etny_bucket, self.payload)
160
+ self.wait_for_trustedzone(self.etny_bucket, self.input)
161
+
162
+ def wait_for_trustedzone(self, bucket_name, object_name, timeout=3600):
163
+ """
164
+ Poll for the existence of an object in a SwiftStream bucket.
165
+
166
+ Used to wait for client-uploaded files in the trusted zone.
167
+
168
+ :param bucket_name: Name of the storage bucket.
169
+ :param object_name: Name of the object to wait for.
170
+ :param timeout: Maximum wait time in seconds (default: 1 hour).
171
+ """
172
+ i = 0
173
+ print(f'Checking if object {object_name} exists in bucket {bucket_name}')
174
+ while True:
175
+ time.sleep(1)
176
+ i = i + 1
177
+ if i > timeout:
178
+ break
179
+ (status, result) = self.__retry_swift_call(self.swift_stream_service.is_object_in_bucket, bucket_name, object_name)
180
+ if status:
181
+ break
182
+ print('secure lock finished the execution')
183
+
184
+ def get_do_request_metadata(self):
185
+ """
186
+ Fetch metadata for the current task (Distributed Operation Request) from the blockchain.
187
+
188
+ Retrieves order details and metadata using the Ethernity smart contract.
189
+ """
190
+ order_data = self.__retry_web3_call(self.etny.caller()._getOrder, self.order_id)
191
+ order = Order(order_data, self.order_id)
192
+ metadata_data = self.__retry_web3_call(self.etny.caller()._getDORequestMetadata, order.do_req)
193
+ self._metadata = DOReqMetadata(metadata_data, order.do_req)
194
+ self.order_metadata = order
195
+
196
+ def compute_sha256_checksum(self, file_data):
197
+ """
198
+ Compute the SHA-256 checksum of given data.
199
+
200
+ Used for integrity validation of payload and input.
201
+
202
+ :param file_data: Data to checksum (string or bytes).
203
+ :return: Hex digest of the checksum.
204
+ """
205
+ if type(file_data) is str:
206
+ file_data = file_data.encode('utf-8')
207
+ return hashlib.sha256(file_data).hexdigest()
208
+
209
+ def print_env(self):
210
+ """
211
+ Print key environment and configuration values for logging/debugging.
212
+ """
213
+ print('ETNY_CHAIN_ID:', self.chain_id)
214
+ print('ETNY_PROTOCOL_CONTRACT_ADDRESS:', self.smart_contract_address)
215
+ print('ETNY_WEB3_PROVIDER:', self.web3_provider)
216
+ print('ETNY_CLIENT_CHALLENGE:', self.client_challenge)
217
+ print('ETNY_ORDER_ID:', self.order_id)
218
+
219
+ def init_web3(self):
220
+ """
221
+ Initialize Web3 connection to the Ethernity blockchain.
222
+
223
+ Connects to the RPC provider, injects PoA middleware, and sets up contract instances.
224
+ """
225
+ self.w3 = Web3(Web3.HTTPProvider(self.web3_provider))
226
+ self.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
227
+ self.etny = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.smart_contract_address),
228
+ abi=self.contract_abi)
229
+ self.image_registry = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.image_registry_address),
230
+ abi=self.image_registry_abi)
231
+
232
+ def read_contract_abi(self):
233
+ """
234
+ Load ABI (Application Binary Interface) for smart contracts.
235
+
236
+ Reads from local files for the main PoX contract and image registry.
237
+ """
238
+ self.contract_abi = self.__read_contract_abi('pox.abi')
239
+ self.image_registry_abi = self.__read_contract_abi('image_registry.abi')
240
+
241
+ def __read_contract_abi(self, contract_name):
242
+ """
243
+ Read ABI file content from the script's directory.
244
+
245
+ :param contract_name: Name of the ABI file.
246
+ :return: ABI string.
247
+ """
248
+ f = open(os.path.dirname(os.path.realpath(__file__)) + f'/{contract_name}')
249
+ contract_abi = f.read()
250
+ f.close()
251
+ return contract_abi
252
+
253
+ def __load_env(self):
254
+ """
255
+ Load configuration from environment variables.
256
+
257
+ Sets chain ID, contract addresses, RPC URL, client challenge, and order ID.
258
+ These are typically set via the .env file fetched from storage.
259
+ Validates types and sets error states if invalid.
260
+ """
261
+ try:
262
+ if os.getenv('ETNY_CHAIN_ID') is not None:
263
+ self.chain_id = int(os.getenv('ETNY_CHAIN_ID'))
264
+ if os.getenv('ETNY_SMART_CONTRACT_ADDRESS') is not None:
265
+ self.smart_contract_address = os.getenv('ETNY_SMART_CONTRACT_ADDRESS').rstrip()
266
+ if os.getenv('ETNY_WEB3_PROVIDER') is not None:
267
+ self.web3_provider = os.getenv('ETNY_WEB3_PROVIDER').rstrip()
268
+ self.client_challenge = os.getenv('ETNY_CLIENT_CHALLENGE')
269
+ self.order_id = int(os.getenv('ETNY_ORDER_ID'))
270
+ except ValueError as e:
271
+ print(f'Invalid environment variable type: {e}')
272
+ self.task_code = TaskStatus.SYSTEM_ERROR
273
+ self.task_result = 'INVALID ENVIRONMENT VARIABLES DETECTED DURING LOADING'
274
+ self.is_valid_client_data = False
275
+
276
+ def save_result(self):
277
+ """
278
+ Save task execution results to storage.
279
+
280
+ Fetches the latest trusted zone public key with retries, encrypts results,
281
+ and uploads them to SwiftStream.
282
+ """
283
+ self.encrypt_file_and_push_to_swifstream(str(self.task_result), "result.txt")
284
+ self.encrypt_file_and_push_to_swifstream(str(self.task_code), "result_code.txt")
285
+
286
+ def save_pub_cert(self):
287
+ """
288
+ Upload the enclave's public certificate to storage.
289
+
290
+ Creates the bucket if needed and stores the cert for client verification.
291
+ """
292
+ self.__ensure_bucket_exists()
293
+ self.__retry_swift_call(self.swift_stream_service.put_file_content, self.etny_bucket,
294
+ "cert.pem",
295
+ self.cert_file)
296
+
297
+ def execute(self):
298
+ """
299
+ Execute the client-submitted task.
300
+
301
+ Fetches and decrypts payload/input, validates them, and runs the task
302
+ using the appropriate executor based on metadata version.
303
+ Sets task_code and task_result accordingly.
304
+ """
305
+ payload_data = self.__get_file_content_and_decrypt(self.payload)
306
+ input_data = self.__get_file_content_and_decrypt(self.input)
307
+ print('Validate client payload and input')
308
+ self.validate_client_payload(payload_data, input_data)
309
+ if self.is_valid_client_data:
310
+ print('Client payload and input are valid')
311
+ else:
312
+ print('Client payload and input are NOT valid')
313
+ return
314
+ if self._metadata._payload_metadata_obj._version == 'v3':
315
+ task_result = execute_task_v3(payload_data, input_data)
316
+ else:
317
+ task_result = execute_server_v4(payload_data, input_data)
318
+ self.task_code = str(task_result[0])
319
+ self.task_result = task_result[1]
320
+
321
+ def __get_file_content_and_decrypt(self, object_name):
322
+ """
323
+ Fetch and decrypt a file from SwiftStream storage.
324
+
325
+ :param object_name: Name of the object to fetch.
326
+ :return: Decrypted content as string.
327
+ """
328
+ status, encrypted_base64 = self.__retry_swift_call(self.swift_stream_service.get_file_content, self.etny_bucket, object_name)
329
+ if not status:
330
+ print(f'Failed to get {object_name} file')
331
+ raise Exception(f'Failed to get {object_name} file')
332
+ encrypted_tuple = crypto.encrypted_data_from_base64_json(encrypted_base64.encode('utf-8'))
333
+ decrypted_result = crypto.decrypt(self.key_file, encrypted_tuple)
334
+ return decrypted_result.decode('utf-8')
335
+
336
+ def encrypt_file_and_push_to_swifstream(self, file_data, file_name):
337
+ """
338
+ Encrypt data with trusted zone public key and upload to SwiftStream.
339
+
340
+ Appends '.securelock' to filename for identification.
341
+
342
+ :param file_data: Data to encrypt and upload.
343
+ :param file_name: Base name for the uploaded file.
344
+ """
345
+ encrypted_input = crypto.encrypt_with_pub_key(self.trusted_zone_public_key, file_data.encode('utf-8'))
346
+ encrypted_input_base64 = crypto.encrypted_data_to_base64_json(encrypted_input)
347
+ file_name = file_name + '.securelock'
348
+ self.__ensure_bucket_exists()
349
+ data = io.BytesIO(encrypted_input_base64)
350
+ status, _ = self.__retry_swift_call(self.swift_stream_service.put_file_content, self.etny_bucket,
351
+ file_name,
352
+ "",
353
+ data)
354
+ if status:
355
+ print(f'File {file_name} encrypted and saved to swift stream successfully')
356
+
357
+ def get_latest_trusted_zone_public_key(self):
358
+ """
359
+ Fetch the latest public key for the trusted zone from the image registry contract.
360
+
361
+ Uses retry logic with exponential backoff to handle connection issues or timeouts.
362
+ In Ethernity Cloud, the trusted zone refers to secure enclave images registered on-chain.
363
+ Caches the key after successful fetch.
364
+ """
365
+ if self.trusted_zone_public_key:
366
+ return # Already cached
367
+ print('getting latest public key of the trusted zone enclave')
368
+ try:
369
+ result = self.__retry_web3_call(self.image_registry.caller().getLatestTrustedZoneImageCertPublicKey,
370
+ '__TRUSTED_ZONE_IMAGE__', 'v3')
371
+ self.trusted_zone_public_key = result[1]
372
+ except Exception as e:
373
+ print(f"Failed to get trusted zone public key: {e}")
374
+ self.task_code = TaskStatus.KEY_ERROR
375
+ self.task_result = 'FAILED TO FETCH TRUSTED ZONE PUBLIC KEY FROM IMAGE REGISTRY'
376
+
377
+ def __retry_web3_call(self, fn, *args, **kwargs):
378
+ """
379
+ Retry wrapper for Web3 calls with exponential backoff.
380
+
381
+ :param fn: The Web3 function to call.
382
+ :param args: Positional arguments for the function.
383
+ :param kwargs: Keyword arguments for the function.
384
+ :return: Result of the function call.
385
+ """
386
+ retries = 0
387
+ max_retries = 500
388
+ while retries < max_retries:
389
+ try:
390
+ return fn(*args, **kwargs)
391
+ except (requests.exceptions.ConnectionError, requests.exceptions.Timeout, Exception) as e: # Catch broad for resilience
392
+ print(f"Web3 call failed: {e}. Retrying in 1 seconds...")
393
+ time.sleep(1)
394
+ retries += 1
395
+ raise Exception(f"Web3 call failed after {max_retries} retries")
396
+
397
+ def __retry_swift_call(self, fn, *args, **kwargs):
398
+ """
399
+ Retry wrapper for SwiftStream calls with exponential backoff.
400
+
401
+ :param fn: The SwiftStream function to call.
402
+ :param args: Positional arguments for the function.
403
+ :param kwargs: Keyword arguments for the function.
404
+ :return: Result of the function call.
405
+ """
406
+ retries = 0
407
+ max_retries = 500
408
+ while retries < max_retries:
409
+ try:
410
+ return fn(*args, **kwargs)
411
+ except Exception as e: # Catch broad for resilience
412
+ print(f"SwiftStream call failed: {e}. Retrying in 1 seconds...")
413
+ time.sleep(1)
414
+ retries += 1
415
+ raise Exception(f"SwiftStream call failed after {max_retries} retries")
416
+
417
+ def __ensure_bucket_exists(self):
418
+ """
419
+ Ensure the bucket exists in SwiftStream, creating it if necessary.
420
+
421
+ Makes the operation idempotent by checking existence first.
422
+ Assumes swift_stream_service has bucket_exists; if not, wraps create in try-except.
423
+ """
424
+ try:
425
+ # Assuming SwiftStreamService has bucket_exists method; if not, implement via try-create
426
+ if not self.swift_stream_service.bucket_exists(self.etny_bucket):
427
+ self.__retry_swift_call(self.swift_stream_service.create_bucket, self.etny_bucket)
428
+ except AttributeError:
429
+ # Fallback if bucket_exists not available: try to create and ignore if exists
430
+ try:
431
+ self.__retry_swift_call(self.swift_stream_service.create_bucket, self.etny_bucket)
432
+ except Exception as e:
433
+ if 'already exists' not in str(e).lower(): # Check for existence error
434
+ raise
435
+
436
+ if __name__ == '__main__':
437
+ print('[SecureLock] Loading env variables..')
438
+ try:
439
+ if EtnySecureLock.debug:
440
+ swiftStreamClient = SwiftStreamService("localhost:9000",
441
+ "swiftstreamadmin",
442
+ "swiftstreamadmin")
443
+ else:
444
+ swiftStreamClient = SwiftStreamService("etny-swift-stream:9000",
445
+ "swiftstreamadmin",
446
+ "swiftstreamadmin")
447
+ status, env_content = swiftStreamClient.get_file_content("__BUCKET_NAME__", ".env")
448
+ if not status:
449
+ print("Failed to get .env file")
450
+ raise Exception("Failed to get .env file")
451
+ EtnySecureLock.read_env_str(env_content)
452
+ except:
453
+ cert_file = "/app/__SECURELOCK_SESSION__/cert.pem"
454
+ with open(cert_file, 'r') as f:
455
+ print("PUBLIC_CERT:", f.read())
456
+ exit(1)
457
+ print('Initializing..')
458
+ app = EtnySecureLock(swiftStreamClient)
459
+ print('Validate client payload and input')
460
+ app.get_latest_trusted_zone_public_key()
461
+ app.wait_for_payload_and_input()
462
+ if app.is_valid_client_data:
463
+ print('Executing client code..')
464
+ app.execute()
465
+ else:
466
+ print('Failed client payload and input validation')
467
+ app.save_result()
468
+ print('Finished the execution')
@@ -111,36 +111,36 @@ def clean_up_registry():
111
111
 
112
112
  def copy_backend_to_build_dir(build_dir):
113
113
  # Copy serverless source code (including subdirectories) to the build directory
114
-
114
+
115
115
  src_dir = Path.cwd() / "src" / "serverless"
116
116
  dest_dir = Path(build_dir) / "securelock" / "src" / "serverless"
117
-
117
+
118
118
  # Remove destination directory if it exists to avoid conflicts
119
119
  if dest_dir.exists():
120
120
  shutil.rmtree(dest_dir)
121
121
 
122
122
  # Copy entire directory tree
123
123
  shutil.copytree(src_dir, dest_dir)
124
-
124
+
125
125
  return True
126
126
 
127
127
 
128
128
  def copy_from_module_to_build_dir(build_dir):
129
129
  # Copy module files from module dir to build dir
130
130
  module_dir = Path(__file__).resolve().parent
131
-
131
+
132
132
  build_dir.mkdir(parents=True, exist_ok=True)
133
133
 
134
134
  scripts_dir = build_dir / "securelock" / "scripts"
135
135
  scripts_dir.mkdir(parents=True, exist_ok=True)
136
136
 
137
137
 
138
- src_file = module_dir / "build" / "securelock" / "Dockerfile.base.tpl"
138
+ src_file = module_dir / "build" / "securelock" / "Dockerfile.base.tpl"
139
139
  dest_file = build_dir / "securelock" / "Dockerfile.base.tpl"
140
140
  shutil.copy(src_file, dest_file)
141
141
 
142
-
143
- src_file = module_dir / "build" / "securelock" / "Dockerfile.tpl"
142
+
143
+ src_file = module_dir / "build" / "securelock" / "Dockerfile.tpl"
144
144
  dest_file = build_dir / "securelock" / "Dockerfile.tpl"
145
145
  shutil.copy(src_file, dest_file)
146
146
 
@@ -148,14 +148,14 @@ def copy_from_module_to_build_dir(build_dir):
148
148
  dest_file = build_dir / "securelock" / "scripts" / "binary-fs-build.sh"
149
149
  shutil.copy(src_file, dest_file)
150
150
 
151
- src_file = module_dir / "build" / "securelock" / "src"
151
+ src_file = module_dir / "build" / "securelock" / "src"
152
152
  dest_file = build_dir / "securelock" / "src"
153
153
  # Remove dest if it exists (since copytree fails if dest exists)
154
154
 
155
155
  if dest_file.exists():
156
156
  shutil.rmtree(dest_file)
157
157
  shutil.copytree(src_file, dest_file)
158
-
158
+
159
159
  return True
160
160
 
161
161
  def update_dockerfile():
@@ -326,7 +326,7 @@ def main():
326
326
 
327
327
  # Change directory to the build directory
328
328
  os.chdir(build_dir)
329
-
329
+
330
330
  spinner.spin_till_done("Update dockerfile ", update_dockerfile)
331
331
 
332
332
  SECURELOCK_SESSION = config.read("SECURELOCK_SESSION")
@@ -5,7 +5,7 @@ import json
5
5
  import functools
6
6
  import random
7
7
  from tqdm import tqdm
8
- from requests.exceptions import RequestException, SSLError
8
+ from requests.exceptions import RequestException, SSLError
9
9
  from requests_toolbelt.multipart.encoder import (
10
10
  MultipartEncoder,
11
11
  MultipartEncoderMonitor,
@@ -37,7 +37,7 @@ def retry_on_failure(max_attempts=RETRY_COUNT, initial_delay=10, backoff_factor=
37
37
  return None
38
38
  return wrapper
39
39
  return decorator_retry
40
-
40
+
41
41
 
42
42
  class IPFSClient:
43
43
 
@@ -45,7 +45,7 @@ def execute_task(code) -> None:
45
45
  PASSWORD = getpass.getpass("Enter your private key password:")
46
46
  ENC_PRIVATE_KEY = os.getenv("ENC_PRIVATE_KEY")
47
47
  pkm = PrivateKeyManager(PASSWORD)
48
- PRIVATE_KEY = pkm.decrypt_private_key(ENC_PRIVATE_KEY)
48
+ PRIVATE_KEY = '0x' + pkm.decrypt_private_key(ENC_PRIVATE_KEY)
49
49
  break
50
50
  except Exception as e:
51
51
  print("Incorrect password. Please try again.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ethernity-cloud-sdk-py
3
- Version: 0.2.45
3
+ Version: 0.3.0
4
4
  Summary: Ethernity Cloud SDK Python
5
5
  Home-page: https://github.com/ethernity-cloud/ethernity-cloud-sdk-py
6
6
  Author: Ethernity Cloud Team
@@ -5,7 +5,7 @@ this_directory = Path(__file__).parent
5
5
  long_description = (this_directory / "README.md").read_text()
6
6
  setup(
7
7
  name="ethernity-cloud-sdk-py",
8
- version="0.2.45",
8
+ version="0.3.0",
9
9
  url="https://github.com/ethernity-cloud/ethernity-cloud-sdk-py",
10
10
  author="Ethernity Cloud Team",
11
11
  author_email="contact@ethernity.cloud",
@@ -1,429 +0,0 @@
1
- #!/usr/bin/python
2
-
3
- # update 29.12.2022
4
- import binascii
5
- import hashlib
6
- import os
7
- import re
8
- import string
9
- import random
10
- import io
11
- import time
12
-
13
- from etny_crypto import etny_crypto as crypto
14
- from web3 import Web3
15
- from eth_account import Account
16
- from web3.middleware import geth_poa_middleware
17
- from key_generation import get_wallet_address
18
- from etny_exec import execute_task_v3, TaskStatus
19
- from etny_exec_flask import execute_task_v4, execute_server_v4
20
- from models import *
21
- from swift_stream_service import SwiftStreamService
22
- from eth_account.messages import defunct_hash_message
23
-
24
-
25
- # todo: remove EtnyResultPoc.debug
26
- class EtnySecureLock:
27
- debug = False
28
-
29
- def __init__(self, swiftStreamClient):
30
- self.swift_stream_service = swiftStreamClient
31
- self.version = 'v3'
32
- print(f'ETNY Pynithy [{self.version}]')
33
- self.__set_initializers()
34
- self.save_pub_cert()
35
- self.__load_env()
36
- # self.read_client_challenge()
37
- # self.generate_challenge()
38
- # self.generate_eth_compatible_wallet()
39
- # print('wallet = ', self.publickey)
40
- # print('private = ', self.privatekey)
41
- self.read_contract_abi()
42
- self.init_web3()
43
- self.print_env()
44
- # if not EtnySecureLock.debug:
45
- # self.reset_cert_file()
46
-
47
- @staticmethod
48
- def read_env(env_file):
49
- with open(env_file, 'r') as f:
50
- # Read each line of the file
51
- for line in f:
52
- # Split the line at the first '=' character
53
- key, value = line.split('=', 1)
54
- # Set the environment variable
55
- os.environ[key] = value
56
-
57
- @staticmethod
58
- def read_env_str(env_str):
59
- with io.StringIO(env_str) as f:
60
- # Read each line of the string
61
- for line in f:
62
- # Split the line at the first '=' character
63
- key, value = line.split('=', 1)
64
- # Set the environment variable and remove new line
65
- os.environ[key] = re.sub(r'\n', '', value)
66
-
67
- def __extract_signer(self, checksum, signature):
68
- signer = self.w3.eth.account.recoverHash(defunct_hash_message(text=checksum), signature=signature)
69
-
70
- return signer
71
-
72
- def __set_initializers(self):
73
- self.is_valid_client_data = True
74
- self.key_file = "/private/__SECURELOCK_SESSION__/key.pem"
75
- self.cert_file = "/app/__SECURELOCK_SESSION__/cert.pem"
76
- self.pub_cert_file = "/app/__SECURELOCK_SESSION__/enclave_pub_cert.pem"
77
- self.payload = 'payload.etny.securelock'
78
- self.input = 'input.txt.securelock'
79
- self.result_file = '/app/result.txt'
80
- self.transaction_file = '/app/transaction.txt'
81
- self.smart_contract_address = '__SMART_CONTRACT_ADDRESS__'
82
- self.image_registry_address = '__IMAGE_REGISTRY_ADDRESS__'
83
- self.chain_id = __CHAIN_ID__
84
- self.web3_provider = '__RPC_URL__'
85
- self.etny_bucket = "__BUCKET_NAME__"
86
- self.trusted_zone_public_key = ''
87
- if EtnySecureLock.debug:
88
- self.key_file = "./app/cert1-ca1-clean.key"
89
- self.cert_file = "./app/cert1-ca1-clean.crt"
90
- self.payload = 'payload.py'
91
- self.input = 'input.txt'
92
- self.result_file = './app/result.txt'
93
- self.transaction_file = './app/transaction.txt'
94
- self.pub_cert_file = "./app/enclave_pub_cert.pem"
95
- # self.trustedzone_public_key = "./app/enclave_pub_cert.pem"
96
-
97
- def validate_client_payload(self, payload_data, input_data):
98
- self.get_do_request_metadata()
99
- if self._metadata.payload_metadata_obj.checksum is not None:
100
- #print('Computing payload checksum: ')
101
- payload_checksum = self.compute_sha256_checksum(payload_data)
102
- #print('payload checksum: ', payload_checksum)
103
- # check the wallet address that signed payload checksum is the one from order metadata
104
- if self._metadata.payload_metadata_obj.checksum.startswith('0x'):
105
- checksum_signer = self.__extract_signer(payload_checksum, self._metadata.payload_metadata_obj.checksum)
106
- transaction_checksum = payload_checksum
107
- else:
108
- checksum_signer = self.order_metadata.do_owner
109
- transaction_checksum = self._metadata.payload_metadata_obj.checksum
110
-
111
-
112
- if checksum_signer.lower() != self.order_metadata.do_owner.lower() and transaction_checksum != payload_checksum:
113
- self.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR
114
- self.task_result = 'PAYLOAD CHECKSUM DOESN\'T MATCH'
115
- self.is_valid_client_data = False
116
- return
117
-
118
- if self._metadata.input_metadata_obj.checksum is not None:
119
- input_checksum = self.compute_sha256_checksum(input_data)
120
- #print('input checksum: ', input_checksum)
121
- # check the wallet address that signed input checksum is the one from order metadata
122
- if self._metadata.input_metadata_obj.checksum.startswith('0x'):
123
- checksum_signer = self.__extract_signer(input_checksum, self._metadata.input_metadata_obj.checksum)
124
- transaction_checksum = input_checksum
125
- else:
126
- checksum_signer = self.order_metadata.do_owner
127
- transaction_checksum = self._metadata.input_metadata_obj.checksum
128
-
129
- if checksum_signer.lower() != self.order_metadata.do_owner.lower() and transaction_checksum != input_checksum:
130
- self.task_code = TaskStatus.INPUT_CHECKSUM_ERROR
131
- self.task_result = 'INPUT CHECKSUM DOESN\'T MATCH'
132
- self.is_valid_client_data = False
133
- return
134
-
135
- def wait_for_payload_and_input(self):
136
- print('Waiting for payload and input')
137
- self.wait_for_trustedzone(self.etny_bucket, self.payload)
138
- self.wait_for_trustedzone(self.etny_bucket, self.input)
139
-
140
- def wait_for_trustedzone(self, bucket_name, object_name, timeout=3600):
141
- i = 0
142
- print(f'Checking if object {object_name} exists in bucket {bucket_name}')
143
- while True:
144
- time.sleep(1)
145
- i = i + 1
146
- if i > timeout:
147
- break
148
- (status, result) = self.swift_stream_service.is_object_in_bucket(bucket_name, object_name)
149
- if status:
150
- break
151
-
152
- print('secure lock finished the execution')
153
-
154
- def get_do_request_metadata(self):
155
- order = Order(self.etny.caller()._getOrder(self.order_id), self.order_id)
156
- #print('order', order)
157
- self._metadata = DOReqMetadata(self.etny.caller()._getDORequestMetadata(order.do_req), order.do_req)
158
- self.order_metadata = order
159
- #print('metadata', self._metadata)
160
-
161
- def compute_sha256_checksum(self, file_data):
162
- if type(file_data) is str:
163
- file_data = file_data.encode('utf-8')
164
-
165
- return hashlib.sha256(file_data).hexdigest()
166
-
167
- # h = hashlib.sha256()
168
- # file = io.BytesIO(file_data)
169
- # while True:
170
- # Reading is buffered, so we can read smaller chunks.
171
- # chunk = file.read(h.block_size)
172
- # if not chunk:
173
- # break
174
- # h.update(chunk)
175
- # return h.hexdigest()
176
-
177
- def reset_cert_file(self):
178
- #print('Resetting the cert file')
179
- try:
180
- with open(self.key_file, 'r+') as f:
181
- f.truncate(0)
182
- except Exception as e:
183
- print('Error while removing the private key file content', e)
184
-
185
- #with open(self.key_file, 'r') as f:
186
- # print('cert file content', f.read(), '##################')
187
-
188
- def generate_eth_compatible_wallet(self):
189
- decrypted_string = crypto.decrypt(
190
- private_key_file=self.key_file,
191
- encrypted_msg=self.encrypted_tuple
192
- )
193
- #print('decrypted_string = ', decrypted_string)
194
- (public, private) = get_wallet_address(decrypted_string.decode(), self.enclave_challenge)
195
- self.publickey = public
196
- self.privatekey = private
197
-
198
- def print_env(self):
199
- print('chain id:', self.chain_id)
200
- print('smart contract address:', self.smart_contract_address)
201
- print('web3 provider:', self.web3_provider)
202
- print('encrypted challenge:', self.client_challenge)
203
- print('order id:', self.order_id)
204
-
205
- def init_web3(self):
206
- self.w3 = Web3(Web3.HTTPProvider(self.web3_provider))
207
- self.w3.middleware_onion.inject(geth_poa_middleware, layer=0)
208
-
209
- # self.acct = Account.privateKeyToAccount(self.privatekey)
210
- self.etny = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.smart_contract_address),
211
- abi=self.contract_abi)
212
- self.image_registry = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.image_registry_address),
213
- abi=self.image_registry_abi)
214
-
215
- def read_contract_abi(self):
216
- self.contract_abi = self.__read_contract_abi('pox.abi')
217
- self.image_registry_abi = self.__read_contract_abi('image_registry.abi')
218
-
219
- def __read_contract_abi(self, contract_name):
220
- f = open(os.path.dirname(os.path.realpath(__file__)) + f'/{contract_name}')
221
- contract_abi = f.read()
222
- f.close()
223
- return contract_abi
224
-
225
- def __load_env(self):
226
- if os.getenv('ETNY_CHAIN_ID') is not None:
227
- self.chain_id = int(os.getenv('ETNY_CHAIN_ID'))
228
-
229
- if os.getenv('ETNY_SMART_CONTRACT_ADDRESS') is not None:
230
- self.smart_contract_address = os.getenv('ETNY_SMART_CONTRACT_ADDRESS').rstrip()
231
-
232
- if os.getenv('ETNY_WEB3_PROVIDER') is not None:
233
- self.web3_provider = os.getenv('ETNY_WEB3_PROVIDER').rstrip()
234
-
235
- self.client_challenge = os.getenv('ETNY_CLIENT_CHALLENGE')
236
- self.order_id = int(os.getenv('ETNY_ORDER_ID'))
237
-
238
- def read_client_challenge(self):
239
- self.encrypted_tuple = crypto.encrypted_data_from_base64_json(self.client_challenge)
240
-
241
- def generate_challenge(self, length=20):
242
- lowercase_only = ''.join(
243
- random.choice(string.ascii_lowercase) for _ in range(length)
244
- )
245
- #print('random challenge:', lowercase_only)
246
- self.enclave_challenge = lowercase_only
247
- return self.enclave_challenge
248
-
249
- def build_result(self):
250
- self.result = (f'{self.version}:{self.task_code}:{self.task_result_checksum}:{self.enclave_challenge}:')
251
-
252
- def addResult(self):
253
- self.build_result()
254
- print(f'adding result to order {self.order_id}')
255
- nonce = self.w3.eth.getTransactionCount(self.publickey)
256
- unicorn_txn = self.etny.functions._addResultToOrder(
257
- self.order_id, self.result
258
- ).buildTransaction({
259
- 'gas': 1000000,
260
- 'chainId': self.chain_id,
261
- 'nonce': nonce,
262
- 'gasPrice': self.w3.toWei("1", "mwei"),
263
- })
264
-
265
- signed_txn = self.w3.eth.account.sign_transaction(unicorn_txn, private_key=self.acct.key)
266
- self.w3.eth.sendRawTransaction(signed_txn.rawTransaction)
267
- # self.transaction = unicorn_tx signed
268
- hash = self.w3.toHex(self.w3.sha3(signed_txn.rawTransaction))
269
-
270
- try:
271
- receipt = self.w3.eth.waitForTransactionReceipt(hash)
272
- except Exception as e:
273
- print(f'An error occurred while sending transaction result {self.order_id}: ', e)
274
- raise
275
-
276
- #print('transaction status: ', receipt.status)
277
- #print('transaction receipt: ', receipt)
278
- if receipt.status == 1:
279
- print("Result transaction was successful!")
280
- else:
281
- print("Result transaction was UNSUCCESSFUL!")
282
-
283
- #print("Result payload: %s" % self.result)
284
- #print("TX Raw: %s" % signed_txn.rawTransaction)
285
- #print("TX Hash: %s" % hash)
286
-
287
- def build_transaction(self):
288
- self.build_result()
289
- print(f'adding result to order {self.order_id}')
290
- nonce = self.w3.eth.getTransactionCount(self.publickey)
291
- unicorn_txn = self.etny.functions._addResultToOrder(
292
- self.order_id, self.result
293
- ).buildTransaction({
294
- 'gas': 1000000,
295
- 'chainId': self.chain_id,
296
- 'nonce': nonce,
297
- 'gasPrice': self.w3.toWei("1", "mwei"),
298
- })
299
-
300
- signed_txn = self.w3.eth.account.sign_transaction(unicorn_txn, private_key=self.acct.key)
301
- signed_tx_as_bytes = binascii.hexlify(signed_txn.rawTransaction)
302
- self.signed_tx_as_bytes = signed_tx_as_bytes
303
-
304
- def get_result_checksum(self):
305
- self.task_result_checksum = hashlib.sha256(self.task_result.encode("utf-8")).hexdigest()
306
-
307
- def save_result(self):
308
- self.__get_latest_trusted_zone_public_key()
309
- self.encrypt_file_and_push_to_swifstream(str(self.task_result), "result.txt")
310
- self.encrypt_file_and_push_to_swifstream(str(self.task_code), "result_code.txt")
311
-
312
- def save_pub_cert(self):
313
- self.swift_stream_service.create_bucket(self.etny_bucket)
314
- self.swift_stream_service.put_file_content(self.etny_bucket,
315
- "cert.pem",
316
- self.cert_file)
317
-
318
- def save_transaction(self):
319
- self.swift_stream_service.create_bucket(self.etny_bucket)
320
- transaction = io.BytesIO(self.signed_tx_as_bytes)
321
- self.swift_stream_service.put_file_content(self.etny_bucket,
322
- "transaction.txt",
323
- self.transaction_file,
324
- transaction)
325
-
326
- def execute(self):
327
- payload_data = self.__get_file_content_and_decrypt(self.payload)
328
- input_data = self.__get_file_content_and_decrypt(self.input)
329
- print('Validate client payload and input')
330
- self.validate_client_payload(payload_data, input_data)
331
- if app.is_valid_client_data:
332
- print('Client payload and input are valid')
333
- else:
334
- print('Client payload and input are NOT valid')
335
- return
336
-
337
- if self._metadata._payload_metadata_obj._version == 'v3':
338
- task_result = execute_task_v3(payload_data, input_data)
339
- else:
340
- task_result = execute_server_v4(payload_data, input_data)
341
-
342
- self.task_code = str(task_result[0])
343
- self.task_result = task_result[1]
344
-
345
- def __get_file_content_and_decrypt(self, object_name):
346
- status, encrypted_base64 = self.swift_stream_service.get_file_content(self.etny_bucket, object_name)
347
- if not status:
348
- print(f'Failed to get {object_name} file')
349
- raise Exception(f'Failed to get {object_name} file')
350
-
351
- encrypted_tuple = crypto.encrypted_data_from_base64_json(encrypted_base64.encode('utf-8'))
352
- decrypted_result = crypto.decrypt(self.key_file, encrypted_tuple)
353
- return decrypted_result.decode('utf-8')
354
-
355
- def encrypt_file_and_push_to_swifstream(self, file_data, file_name):
356
- encrypted_input = crypto.encrypt_with_pub_key(self.trusted_zone_public_key, file_data.encode('utf-8'))
357
- encrypted_input_base64 = crypto.encrypted_data_to_base64_json(encrypted_input)
358
-
359
- # file name should end with secure lock '.securelock' extension
360
- file_name = file_name + '.securelock'
361
- self.swift_stream_service.create_bucket(self.etny_bucket)
362
- data = io.BytesIO(encrypted_input_base64)
363
- status, _ = self.swift_stream_service.put_file_content(self.etny_bucket,
364
- file_name,
365
- "",
366
- data)
367
- if status:
368
- print(f'File {file_name} encrypted and saved to swift stream successfully')
369
-
370
- def __get_trusted_zone_public_key(self):
371
- print('getting the public key of the trusted zone enclave')
372
- image_hash = self._metadata.image_hash
373
- if EtnySecureLock.debug:
374
- image_hash = 'v3-hash-1'
375
- self.trusted_zone_public_key = self.image_registry.caller().getTrustedZoneImageCertPublicKey(
376
- image_hash)
377
-
378
- def __get_latest_trusted_zone_public_key(self):
379
- print('getting latest public key of the trusted zone enclave')
380
- self.trusted_zone_public_key = self.image_registry.caller().getLatestTrustedZoneImageCertPublicKey(
381
- '__TRUSTED_ZONE_IMAGE__', 'v3')[1]
382
-
383
-
384
- if __name__ == '__main__':
385
- print('[SecureLock] Loading env variables..')
386
- try:
387
- if EtnySecureLock.debug:
388
- swiftStreamClient = SwiftStreamService("localhost:9000",
389
- "swiftstreamadmin",
390
- "swiftstreamadmin")
391
- else:
392
- swiftStreamClient = SwiftStreamService("etny-swift-stream:9000",
393
- "swiftstreamadmin",
394
- "swiftstreamadmin")
395
-
396
- status, env_content = swiftStreamClient.get_file_content("__BUCKET_NAME__", ".env")
397
- if not status:
398
- print("Failed to get .env file")
399
- raise Exception("Failed to get .env file")
400
- EtnySecureLock.read_env_str(env_content)
401
- except:
402
- cert_file = "/app/__SECURELOCK_SESSION__/cert.pem"
403
- with open(cert_file, 'r') as f:
404
- print("PUBLIC_CERT:", f.read())
405
- exit(1)
406
-
407
- #if not EtnySecureLock.debug:
408
- # with open("/app/__SECURELOCK_SESSION__/cert.pem", 'r') as f:
409
- # print("PUBLIC_CERT:", f.read())
410
-
411
- print('Initializing..')
412
- app = EtnySecureLock(swiftStreamClient)
413
- print('Validate client payload and input')
414
- app.wait_for_payload_and_input()
415
-
416
- if app.is_valid_client_data:
417
- print('Executing client code..')
418
- app.execute()
419
- else:
420
- print('Failed client payload and input validation')
421
- # print('Generating task result checksum..')
422
- # app.get_result_checksum()
423
- # print('Building transaction..')
424
- # app.build_transaction()
425
- # print('Saving transaction...')
426
- # app.save_transaction()
427
- # print('Saving the result..')
428
- app.save_result()
429
- print('Finished the execution')