Skip to content

Commit

Permalink
17 oct update. fds_swarm first draft added, bee-js -> bee-py initial …
Browse files Browse the repository at this point in the history
…class addded
  • Loading branch information
Aviksaikat committed Oct 18, 2023
1 parent 0fef0da commit ef73da5
Show file tree
Hide file tree
Showing 11 changed files with 382 additions and 25 deletions.
14 changes: 14 additions & 0 deletions src/fds/fds_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from binascii import hexlify, unhexlify
from os import urandom

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
Expand Down Expand Up @@ -118,3 +120,15 @@ def decrypt_string(encrypted_hex, password, iv):

# Return the decrypted string as a utf-8 string
return decrypted.decode("utf-8")

@staticmethod
def encrypt_buffer(buffer, password, iv):
cipher = AES.new(unhexlify(password[2:]), AES.MODE_CTR, iv=iv)
crypted = cipher.encrypt(pad(buffer, AES.block_size))
return crypted

@staticmethod
def decrypt_buffer(buffer, password, iv):
decipher = AES.new(unhexlify(password[2:]), AES.MODE_CTR, iv=iv)
dec = unpad(decipher.decrypt(buffer), AES.block_size)
return dec
9 changes: 9 additions & 0 deletions src/fds/fds_mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Dict

from ape.api.accounts import AccountAPI


class Mail:
def __init__(self, config: Dict, account: AccountAPI):
self.config = config
self.account = account
2 changes: 2 additions & 0 deletions src/fds/fds_mailbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Mail:
...
171 changes: 171 additions & 0 deletions src/fds/fds_swarm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"""
Copyright 2023 The FairDataSociety Authors
This file is part of the FairDataSociety library.
The FairDataSociety library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The FairDataSociety library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the FairDataSociety library. If not, see <http:www.gnu.org/licenses/>.
store files using swarm
"""
# import os
import time
# from binascii import unhexlify
from typing import Dict, List, Union

import requests
from ape.api.accounts import AccountAPI

from fds.fds_crypto import Crypto
from fds.utils.Bee import Bee

# from Crypto.Cipher import AES
# from Crypto.Util.Padding import unpad


POSTAGE_STAMP = "0000000000000000000000000000000000000000000000000000000000000000" # noqa: 501


class Hash:
def __init__(self, address, file, time, iv, meta, account):
self.address = address
self.file = file
self.time = time
self.iv = iv
self.meta = meta
self.account = account


# TODO: implement the full thing
class Swarm:
def __call__(self, config: Dict, account: AccountAPI):
self.config = config
self.account = account
self.gateway = config.get("beeGateway")
self.rawGateway = self.gateway + "/bytes" # type: ignore
self.bee = Bee(self.gateway) # type: ignore
self.crypto = Crypto()

def getSwarmDigest(self, string: str) -> Union[str, None]:
if string == "/shared/mail":
return "0x723beeeb4a880dc9432fdf3b1b53df942c4ec162ffda83037f2ad2ef94b22c23"
elif string == "/shared":
return "0x23e642b7242469a5e3184a6566020c815689149967703a98c0affc14b9ca9b28"
elif string == "/":
"0xc7f5bbf5fe95923f0691c94f666ac3dfed12456cd33bd018e7620c3d93edd5a6"
else:
try:
digest = self.bee.uploadData(POSTAGE_STAMP, string)
except Exception as e:
raise (e)

return "0x" + digest
return None

def getSwarmDigestValue(self, swarm_hash: str) -> str:
self.swarm_hash = swarm_hash.replace("0x", "")
if self.swarm_hash == "/shared/mail":
return "0x723beeeb4a880dc9432fdf3b1b53df942c4ec162ffda83037f2ad2ef94b22c23"
elif self.swarm_hash == "/shared":
return "0x23e642b7242469a5e3184a6566020c815689149967703a98c0affc14b9ca9b28"
elif self.swarm_hash == "/":
"0xc7f5bbf5fe95923f0691c94f666ac3dfed12456cd33bd018e7620c3d93edd5a6"
else:
try:
self.value = self.bee.downloadData(self.swarm_hash)
except Exception as e:
raise (e)
return self.value

def storeFilesUnencrypted(self, files: List, pin: bool = True) -> str:
"""
* Store unencrypted file to swarm
* @param {any} file to store
* @param {any} encryptedProgressCallback callback
* @param {any} uploadedProgressCallback callback
* @param {any} progressMessageCallback callback
* @returns {Hash} with address, file, time and iv
"""
self.file = files[0]

self.metadata = {"name": self.file.name, "type": self.file.type, "size": self.file.size}

return self.bee.uploadFiles(
POSTAGE_STAMP, self.file, {"indexDocument": self.metadata.get("name")}
)

def storeEncryptedFile(self, file, secret: str, pin: bool = True, metadata: Dict = {}) -> Hash:
"""
* Store encrypted file to swarm
* @param {any} file to store
* @param {any} secret to use
* @param {any} encryptedProgressCallback callback
* @param {any} uploadedProgressCallback callback
* @param {any} progressMessageCallback callback
* @returns {Hash} with address, file, time and iv
"""
if metadata is None:
self.metadata = {}

self.iv = self.crypto.generate_random_iv()
self.buffer = self.crypto.encrypt_buffer(file, secret, self.iv)

self.metadata = {"name": file.name, "type": file.type, "size": file.size, **metadata}

self.reference = self.bee.uploadFiles(POSTAGE_STAMP, self.buffer, self.metadata["name"])

return Hash(
address=self.reference,
file=file,
time=int(time.time() * 1000), # Convert to milliseconds
iv="0x" + self.iv.hex(),
meta=self.metadata,
account=self.account,
)

def getDataFromManifest(self, swarm_hash: str) -> Union[str, None]:
"""
* Get manifest from url
* @param {any} swarmHash hash
* @param {any} filename file at hash
* @returns {any} result of request (manifest)
"""
self.url = f"{self.gateway}/bzz/{swarm_hash}"
self.response = requests.get(self.url)
if self.response.status_code == 200:
return self.response.content.decode("utf-8")
else:
return None

# def getDecryptedFile(
# self, hash: List, secret: str, selected_mailbox: str, selected_wallet: str
# ):
# """
# * Get decrypted file
# * @param {any} hash location
# * @param {any} secret to decrypt
# * @param {any} selectedMailbox mailbox to use
# * @param {any} selectedWallet wallet to use
# * @returns {any} decrypted file
# """
# self.retrieved_file = self.getDataFromManifest(hash["address"], hash["file"]["name"])

# # Decrypt the file
# self.cipher = AES.new(unhexlify(secret[2:]), AES.MODE_CTR, iv=unhexlify(hash["iv"][2:34]))
# self.decrypted_buffer = unpad(self.cipher.decrypt(self.retrieved_file), AES.block_size)

# # Write the decrypted file to disk
# self.file_path = os.path.join(selected_mailbox, selected_wallet, hash["name"])
# with open(self.file_path, "wb") as f:
# f.write(self.decrypted_buffer)

# return self.file_path
5 changes: 3 additions & 2 deletions src/fds/fds_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
handles transcations mostly
"""
from typing import Optional, Tuple, Union
from typing import Dict, Optional, Tuple, Union

from ape import chain, networks
from ape.api.accounts import AccountAPI
Expand All @@ -34,9 +34,10 @@


class Tx:
def __init__(self, account: AccountAPI):
def __init__(self, config: Dict, account: AccountAPI):
self.account = account
self.contract_address = None
self.config = config

def getContract(self, address: AddressType, abi: AbiType) -> ContractInstance:
# self.account = account
Expand Down
79 changes: 79 additions & 0 deletions src/fds/utils/Bee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Copyright 2023 The FairDataSociety Authors
This file is part of the FairDataSociety library.
The FairDataSociety library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The FairDataSociety library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the FairDataSociety library. If not, see <http:www.gnu.org/licenses/>.
interact with bee API
"""
from typing import Dict, List, Union

import requests

from fds.utils.types import BatchId
from fds.utils.utils import (
extract_upload_headers,
make_collection_from_file_list,
wrap_bytes_with_helpers,
)


class Bee:
def __init__(self, url: str):
self.url = url
# self.headers = {"content-type": "application/octet-stream"}
# self.gateWay = gateway

def uploadData(
self, postage_batch_id: Union[str, BatchId], data: str, options: Dict = {}
) -> str:
self.headers = {
"content-type": "application/octet-stream",
**extract_upload_headers(postage_batch_id, options),
}
self.data = data
self.response = requests.post(self.url + "/bytes", data=data, headers=self.headers)

self.response_data = self.response.json()
return self.response_data["reference"]

def downloadData(self, swarmhash: str):
self.headers = {
"content-type": "application/octet-stream",
}
self.swarmhash = swarmhash

self.response = requests.get(self.url + f"/bytes/{self.swarmhash}", headers=self.headers)

if self.response.status_code == 200:
self.binary_data = self.response.content
return wrap_bytes_with_helpers(self.binary_data)
else:
print(f"Request failed with status code: {self.response.status_code}") # noqa: ignore
return

def uploadFiles(
self, postage_batch_id: Union[str, BatchId], file_list: List, options: Dict = {}
) -> str:
self.headers = {
"content-type": "application/octet-stream",
**extract_upload_headers(postage_batch_id, options),
}

self.data = make_collection_from_file_list(file_list) # type: ignore

self.response = requests.post(self.url + "/bzz", data=self.data, headers=self.headers)

self.response_data = self.response.json()
return self.response_data["reference"]
26 changes: 13 additions & 13 deletions src/fds/utils/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
"""

import os
from typing import Dict

import requests
from ape.api.accounts import AccountAPI


# TODO:
class Hash:
def __init__(self, attrs, account):
def __init__(self, config: Dict, attrs: Dict, account: AccountAPI):
if "address" not in attrs:
raise ValueError("address must be defined")
if "file" not in attrs:
Expand All @@ -39,6 +40,7 @@ def __init__(self, attrs, account):
self.iv = attrs.get("iv")
self.meta = attrs.get("meta", {})
self.account = account
self.config = config

def toJSON(self):
return {
Expand All @@ -53,31 +55,29 @@ def toJSON(self):
"meta": self.meta,
}

def getFile(self, decrypt_progress_callback=print, download_progress_callback=print):
def getFile(self):
# Implement your file retrieval logic here
# You can use requests to download the file
url = f"{self.account.Store.config['swarmGateway']}/bzz:/{self.address}/{self.file['name']}"
url = f"{self.config['swarmGateway']}/bzz:/{self.address}/{self.file['name']}"
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(self.file["name"], "wb") as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
download_progress_callback(len(chunk))
decrypt_progress_callback("File downloaded successfully.")
print(len(chunk)) # noqa: ignore
print("File downloaded successfully.") # noqa: ignore
return self.file["name"]
else:
raise ValueError("Failed to download the file.")

def saveAs(self, decrypt_progress_callback=print, download_progress_callback=print):
file_path = self.getFile(decrypt_progress_callback, download_progress_callback)
def saveAs(self):
file_path = self.getFile()
try:
os.rename(file_path, self.file["name"])
decrypt_progress_callback(f"File renamed to {self.file['name']}.")
print(f"File renamed to {self.file['name']}.") # noqa: ignore
except Exception as e:
decrypt_progress_callback(f"Error renaming the file: {str(e)}")
print(f"Error renaming the file: {str(e)}") # noqa: ignore

def gatewayLink(self):
return (
f"{self.account.Store.config['swarmGateway']}/bzz:/{self.address}/{self.file['name']}"
)
return f"{self.config['swarmGateway']}/bzz:/{self.address}/{self.file['name']}"
Loading

0 comments on commit ef73da5

Please sign in to comment.