Official Python SDK for 0G Storage - A decentralized storage network with merkle tree verification
Project description
0G Storage — Python SDK
Python SDK for the 0G Storage Network — a decentralized storage layer that spreads files across sharded nodes and anchors integrity with on-chain merkle proofs.
A direct port of the official TypeScript SDK @0glabs/0g-ts-sdk. Files uploaded with either SDK produce byte-exact identical merkle roots.
📚 Full developer documentation: og-py.vercel.app
What you get
- Merkle tree generation — Keccak256, 256-byte chunks, 256 KB segments — output identical to the TS SDK
- File upload — Flow contract submission, parallel segment uploads, automatic shard routing, retry on transient failures
- File download — by root hash, with optional proof verification per segment
- Splitable upload — for files larger than 4 GB, with batched fragment processing
- Fragment download — reassemble multi-fragment files in order
- KV storage — append-only key-value streams with version-aware reads, batched writes, and per-key access control
- Storage node RPC — 14-method client for direct node interaction (get_status, upload/download segments, shard configs, sector proofs)
Installation
pip install 0g-storage-sdk
Requires Python 3.8+.
The package name 0g-storage-sdk isn't a valid Python identifier, so the SDK ships its top-level modules directly:
from core.indexer import Indexer
from core.file import ZgFile
from core.merkle import MerkleTree
from core.kv import KvClient, StreamDataBuilder, Batcher
Dependencies
Auto-installed:
pycryptodome— Keccak256 hashingweb3— blockchain RPC and contract callseth-account— transaction signingrequests— HTTP client
Quickstart — upload & download
from core.indexer import Indexer
from core.file import ZgFile
from eth_account import Account
INDEXER_RPC = "https://indexer-storage-testnet-turbo.0g.ai"
BLOCKCHAIN_RPC = "https://evmrpc-testnet.0g.ai"
PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"
indexer = Indexer(INDEXER_RPC)
account = Account.from_key(PRIVATE_KEY)
# Upload
file = ZgFile.from_file_path("./data.txt")
result, err = indexer.upload(
file,
BLOCKCHAIN_RPC,
account,
{
"tags": b"\x00",
"finalityRequired": True,
"expectedReplica": 1,
"account": account,
},
)
file.close()
if err is None:
print(f"Uploaded: {result['rootHash']}")
print(f"Tx: {result['txHash']}")
# Download (wait 3–5 minutes for shard propagation first)
err = indexer.download(result["rootHash"], "./output.txt")
Client-side encryption
Files can be encrypted before upload using AES-256-CTR (v1) or ECIES on secp256k1 (v2). Both schemes are wire-compatible with the TS SDK and the Go storage client.
import os
from core.indexer import Indexer
indexer = Indexer(INDEXER_URL)
file = ZgFile.from_file_path("./secret.txt")
# v1 — symmetric (you generate and manage a 32-byte key out-of-band)
key = os.urandom(32)
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
"encryption": {"type": "aes256", "key": key},
})
# Download + auto-decrypt (best-effort; raw bytes on disk if no match)
indexer.download(result["rootHash"], "./secret.txt", symmetric_key=key)
# v2 — ECIES (anyone with `recipient_pub` can encrypt; only the holder of
# the matching private key can decrypt)
recipient_pub = "0x031b84c5...078f" # 33-byte compressed secp256k1 pubkey
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
"encryption": {"type": "ecies", "recipient_pub_key": recipient_pub},
})
indexer.download(result["rootHash"], "./secret.txt", private_key=PRIVATE_KEY_HEX)
To detect whether a stored file is encrypted before downloading:
header, err = indexer.peek_header(root_hash)
if header is None:
print("Plaintext file")
elif header.version == 1:
print("AES-256 — supply symmetric_key=...")
elif header.version == 2:
print("ECIES — supply private_key=...")
Compute a merkle root locally
No network calls — pure local hashing:
from core.file import ZgFile
file = ZgFile.from_bytes(b"hello, 0G Storage")
tree, err = file.merkle_tree()
print(tree.root_hash())
file.close()
Large files (>4 GB)
from core.uploader import Uploader
# After selecting nodes via indexer.select_nodes(...)
uploader = Uploader(clients, BLOCKCHAIN_RPC, flow)
result, err = uploader.splitable_upload(
file,
{
"account": account,
"fragmentSize": 4 * 1024 * 1024 * 1024, # 4 GB per fragment
"finalityRequired": True,
},
)
# result["rootHashes"] is a list — one hash per fragment, in order
Download with Downloader.download_fragments(roots, filename) — fragments are concatenated in the order you pass them.
KV storage
from core.kv import Batcher
batcher = Batcher(version=1, clients=clients, flow=flow, provider=BLOCKCHAIN_RPC)
stream_id = "0x" + "11" * 32
batcher.set(stream_id, b"user:42", b'{"name": "Alice"}')
batcher.set(stream_id, b"user:101", b'{"name": "Bob"}')
result, err = batcher.exec({"account": account})
Read back via KvClient:
from core.kv import KvClient
kv = KvClient("http://storage-node.example.com:5678")
value = kv.get_value(stream_id, b"user:42")
# value.data is base64-encoded
Generate proofs
proof = tree.proof_at(2) # proof for the leaf at index 2
err = proof.validate(
root_hash = tree.root_hash(),
content = b"chunk 3 data",
position = 2,
num_leaf_nodes = len(tree.leaves),
)
if err is None:
print("Valid proof")
Public API surface
Everything you need is exported at module level:
| Symbol | Purpose |
|---|---|
Indexer(url) |
Indexer RPC client — upload, download, select_nodes, get_file_locations |
ZgFile.from_file_path(path) / .from_bytes(data) |
File abstraction with merkle tree generation |
MerkleTree, Proof, LeafNode |
Direct merkle tree construction and verification |
Uploader, Downloader |
Lower-level upload/download with retry options |
StorageNode(url) |
Direct RPC to a storage node (14 methods) |
KvClient(rpc) |
KV reads — get_value, get_first/last/next/prev, iterators |
StreamDataBuilder(version) |
Build StreamData blobs (writes + access control) |
Batcher(version, clients, flow, provider) |
High-level batched KV writes |
KvIterator |
Cursor-style traversal over a stream |
select_nodes, check_replica, is_valid_config |
Node selection helpers |
RetryOpts, tx_with_gas_adjustment, submit_with_gas_adjustment |
Retry and gas utilities |
Exceptions: StorageError, UploadError, DownloadError, MerkleTreeError, ContractError, RetryableError, ... |
Structured exception hierarchy with error_code, context, cause |
For the full per-class API and worked examples, see the Storage SDK docs.
Network configuration
| Network | Chain ID | Blockchain RPC | Indexer RPC |
|---|---|---|---|
| Mainnet | 16661 |
https://evmrpc.0g.ai |
https://indexer-storage-turbo.0g.ai |
| Testnet (Galileo) | 16602 |
https://evmrpc-testnet.0g.ai |
https://indexer-storage-testnet-turbo.0g.ai |
Get testnet tokens at faucet.0g.ai.
Contract addresses
The SDK reads the Flow contract address from each storage node's networkIdentity automatically — direct addressing is rarely needed.
| Contract | Testnet | Mainnet |
|---|---|---|
| Flow | 0x22E03a6A89B950F1c82ec5e74F8eCa321a105296 |
0x62D4144dB0F0a6fBBaeb6296c785C71B3D57C526 |
Constants
DEFAULT_CHUNK_SIZE = 256 # bytes per merkle leaf
DEFAULT_SEGMENT_SIZE = 262144 # 256 KB per segment (1024 chunks)
SMALL_FILE_SIZE_THRESHOLD = 256 * 1024
DEFAULT_FRAGMENT_SIZE = 4 * 1024 * 1024 * 1024 # 4 GB
Testing
cd 0g_py_storage
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pytest tests/ -v
Verifying parity with the TypeScript SDK
from core.file import ZgFile
tree, _ = ZgFile.from_bytes(b"0G Storage parity check").merkle_tree()
print(tree.root_hash())
Run the equivalent in the TypeScript SDK — the printed hashes match byte-for-byte.
Error handling
from utils.error_handler import is_retryable
result, err = indexer.upload(file, BLOCKCHAIN_RPC, account, opts)
if err is not None:
if is_retryable(err):
# transient — retry with backoff
...
else:
raise err
For a tour of the full exception hierarchy and ErrorContext / wrap_with_context helpers, see the Error Handling docs.
Contributing
Contributions should preserve TypeScript SDK parity. Each PR should:
- Match the TS SDK behavior — verify outputs match byte-for-byte
- Include tests for new functionality
- Update the README and docs as needed
- Reference TS SDK line numbers in code comments where applicable
License
MIT
Links
- 📚 Documentation: og-py.vercel.app
- 🌐 0G Labs: 0g.ai
- 📖 Official docs: docs.0g.ai
- 📦 TypeScript SDK: @0glabs/0g-ts-sdk
- 🚰 Testnet faucet: faucet.0g.ai
- 🔍 Block explorer: chainscan-galileo.0g.ai
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file 0g_storage_sdk-0.4.0.tar.gz.
File metadata
- Download URL: 0g_storage_sdk-0.4.0.tar.gz
- Upload date:
- Size: 75.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3b73eed383d6439af037683431f637b9c5d15c45ae0d9627d3189b8b124907b
|
|
| MD5 |
9c42ae72035f1512348760e37a8baf57
|
|
| BLAKE2b-256 |
646456834c6c27452410cb5f275ab33ef6153c1c5b6e47fdcbc14230ca06e73f
|
File details
Details for the file 0g_storage_sdk-0.4.0-py3-none-any.whl.
File metadata
- Download URL: 0g_storage_sdk-0.4.0-py3-none-any.whl
- Upload date:
- Size: 74.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
342e753b221d7dddf3f1856d7f8240a4f4d8a34d7e44e0fb3b4fa7158abef416
|
|
| MD5 |
c01ee54ce0211a9f2769a09244e88c88
|
|
| BLAKE2b-256 |
b02f237a02b0c8aea83beeb9611024be823c2c9fd33c638410f4a25f5552063a
|