Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Simple python example #9

Merged
merged 15 commits into from
Jun 7, 2024
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@
# mdBook
build/

# Python
__pycache__/
*.pyc

gdanezis marked this conversation as resolved.
Show resolved Hide resolved
# Misc
*.key
.env
config.yml
working_dir
*.log

# Walrus binary
examples/CONFIG/bin/walrus
3 changes: 3 additions & 0 deletions examples/CONFIG/bin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Configuration

Place the 'walrus' client binary for your system in this directory.
34 changes: 34 additions & 0 deletions examples/CONFIG/config_dir/client_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
system_pkg: 0x17108fb344dbea0d315e6f44cdd3af12a028cd568bafcbfb7f7a7b152719d15d
system_object: 0x3fb18e675ad41158f59acbdb7f574136a198cbd83be9b968c0fdeaa139c312f9
wallet_config: null
gdanezis marked this conversation as resolved.
Show resolved Hide resolved

# Default values for the client are commented out.
#
# There is no risk in playing around with these values.
# Worst case, you may not be able to store/read from Walrus.

# communication_config:
# max_concurrent_writes: null
# max_concurrent_sliver_reads: null
# max_concurrent_metadata_reads: 3
# reqwest_config:
# total_timeout:
# secs: 180
# nanos: 0
# pool_idle_timeout: null
# http2_keep_alive_timeout:
# secs: 5
# nanos: 0
# http2_keep_alive_interval:
# secs: 30
# nanos: 0
# http2_keep_alive_while_idle: true
# request_rate_config:
# max_node_connections: 10
# max_retries: 5
# min_backoff:
# secs: 2
# nanos: 0
# max_backoff:
# secs: 60
# nanos: 0
106 changes: 106 additions & 0 deletions examples/python/hello_walrus_jsonapi.py
gdanezis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Example of uploading and downloading a file to / from the Walrus service
# Using the walrus client json input & output facilities.
#
# Prerequisites:
#
# - Configure Sui Client to connect to testnet, and some testnet Sui tokens
# see: https://docs.sui.io/guides/developer/getting-started/connect
#
# - Configure Walrus
# see: TODO(#12)
#
# - Update the paths PATH_TO_WALRUS and PATH_TO_WALRUS_CONFIG below
#
gdanezis marked this conversation as resolved.
Show resolved Hide resolved

# Std lib imports
import os
import subprocess
import json
import tempfile
import base64

import requests

from utils import num_to_blob_id

PATH_TO_WALRUS = "../CONFIG/bin/walrus"
PATH_TO_WALRUS_CONFIG = "../CONFIG/config_dir/client_config.yaml"

try:

# Create a 1MB file of random data
random_data = os.urandom(1024 * 1024)
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.write(random_data)
tmp.close()

# Part 1. Upload the file to the Walrus service
store_json_command = f"""{{ "config" : "{PATH_TO_WALRUS_CONFIG}",
"command" : {{ "store" :
{{ "file" : "{tmp.name}", "epochs" : 2 }}}}
}}"""
result = subprocess.run(
[PATH_TO_WALRUS, "json"],
text=True,
capture_output=True,
input=store_json_command)
assert result.returncode == 0

# Parse the response and display key information
json_result_dict = json.loads(result.stdout.strip())
print(f"Upload Blob ID: {json_result_dict['blob_id']} Size {len(random_data)} bytes")
sui_object_id = json_result_dict['sui_object_id']
blob_id = json_result_dict['blob_id']
print(f"Certificate in Object ID: {sui_object_id}")

# Part 2. Download the file from the Walrus service
read_json_command = f"""{{ "config" : "{PATH_TO_WALRUS_CONFIG}",
"command" : {{ "read" :
{{ "blob_id" : "{json_result_dict['blob_id']}" }}}}
}}"""
result = subprocess.run(
[PATH_TO_WALRUS, "json"],
text=True,
capture_output=True,
input=read_json_command)
assert result.returncode == 0

# Parse the response and display key information
json_result_dict = json.loads(result.stdout.strip())
downloaded_data = base64.b64decode(json_result_dict['blob'])
assert downloaded_data == random_data

print(f"Download Blob ID: {json_result_dict['blob_id']} Size {len(downloaded_data)} bytes")

# Part 3. Check the availability of the blob
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getObject",
"params": [
sui_object_id,
{
"showType": True,
"showOwner": False,
"showPreviousTransaction": True,
"showDisplay": False,
"showContent": True,
"showBcs": False,
"showStorageRebate": False
}
]
}
response = requests.post("https://fullnode.testnet.sui.io:443", json=request)
object_content = response.json()["result"]["data"]["content"]
print("Object content:")
print(json.dumps(object_content, indent=4))

# Check that the blob ID matches the one we uploaded
blob_id_downloaded = int(object_content["fields"]["blob_id"])
if num_to_blob_id(blob_id_downloaded) == blob_id:
print("Blob ID matches certificate!")
else:
print("Blob ID does not match")

finally:
os.unlink(tmp.name)
48 changes: 48 additions & 0 deletions examples/python/hello_walrus_sui_system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Example of querying the Walrus system object on Sui
#
# Prerequisites:
#
# - Configure Walrus
# see: TODO(#12)
#
# - Update the paths PATH_TO_WALRUS_CONFIG below
#

# Std lib imports
import requests
import re

PATH_TO_WALRUS_CONFIG = "../CONFIG/config_dir/client_config.yaml"

system_object_id = re.findall(r"system_object:[ ]*(.*)", open(PATH_TO_WALRUS_CONFIG).read())[0]
print(f'System object ID: {system_object_id}')

# Query the Walrus system object on Sui
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getObject",
"params": [
system_object_id,
{
"showType": True,
"showOwner": False,
"showPreviousTransaction": True,
"showDisplay": False,
"showContent": True,
"showBcs": False,
"showStorageRebate": False
}
]
}
response = requests.post("https://fullnode.testnet.sui.io:443", json=request)
assert response.status_code == 200

system_object_content = response.json()["result"]["data"]["content"]["fields"]
committee = system_object_content["current_committee"]["fields"]["bls_committee"]["fields"]

print(f'Current walrus epoch: {system_object_content["current_committee"]["fields"]["epoch"]}')
print(f'Number of members: {len(committee["members"])} Number of shards: {committee["n_shards"]}')
print(f'Price per unit size: {system_object_content["price_per_unit_size"]} MIST')
print(f'Total capacity size: {system_object_content["total_capacity_size"]} bytes')
print(f'Used capacity size: {system_object_content["used_capacity_size"]} bytes')
68 changes: 68 additions & 0 deletions examples/python/hello_walrus_webapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Example of uploading and downloading a file to / from the Walrus service
# Using the walrus client web API facilities.
#
# Prerequisites:
#
# - Configure Sui Client to connect to testnet, and some testnet Sui tokens
# see: https://docs.sui.io/guides/developer/getting-started/connect
#
# - Configure Walrus
# see: TODO(#12)
#
# - Run the Walrus client in daemon mode:
# $ walrus --config config_dir/client_config.yaml daemon -b 127.0.0.1:8899
gdanezis marked this conversation as resolved.
Show resolved Hide resolved
#

# Std lib imports
import os
import time

# External requests HTTP library
import requests

ADDRESS = "127.0.0.1:8899"
EPOCHS = "5"

# Helper functions to upload a blob
def upload_blob(ADDRESS, EPOCHS, data):

# Upload the data to the Walrus service using a PUT request
store_url = f"http://{ADDRESS}/v1/store?epochs={EPOCHS}"
response = requests.put(store_url, data=data)

# Assert the response status code
assert response.status_code == 200
blob_id = response.text
return blob_id

# Helper functions to download a blob
def download_blob(ADDRESS, blob_id):

# Now read the same resource using the blob-id
read_url = f"http://{ADDRESS}/v1/{blob_id}"
response = requests.get(read_url)

# Assert the response status code
assert response.status_code == 200
return response.content

# Upload a random 1MB string then download it, and check it matches
if __name__ == "__main__":

# Generate a 1MB blob of random data
random_data = os.urandom(1024 * 1024)

# Upload the blob to the Walrus service
start_time = time.time()
blob_id = upload_blob(ADDRESS, EPOCHS, random_data)
upload_time = time.time()

# Now download the same blob using the blob-id
data = download_blob(ADDRESS, blob_id)
assert data == random_data
download_time = time.time()

# Print some information about the blob
print(f"Blob ID: {blob_id} Size {len(random_data)} bytes")
gdanezis marked this conversation as resolved.
Show resolved Hide resolved
print(f"Upload time: {upload_time - start_time:.2f}s")
print(f"Download time: {download_time - upload_time:.2f}s")
1 change: 1 addition & 0 deletions examples/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests>=2.22.0
17 changes: 17 additions & 0 deletions examples/python/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import base64

# Convert a numeric (u256) blob_id to a base64 encoded Blob ID
def num_to_blob_id(blob_id_num):
extracted_bytes = []
for i in range(32):
extracted_bytes += [ blob_id_num & 0xff ]
blob_id_num = blob_id_num >> 8
assert blob_id_num == 0
blob_id_bytes = bytes(extracted_bytes)
encoded = base64.urlsafe_b64encode(blob_id_bytes)
return encoded.decode("ascii").strip("=")

if __name__ == "__main__":
blob_id_num = 46269954626831698189342469164469112511517843773769981308926739591706762839432
blob_id_base64 = "iIWkkUTzPZx-d1E_A7LqUynnYFD-ztk39_tP8MLdS2Y"
assert num_to_blob_id(blob_id_num) == blob_id_base64