https://github.com/eliezhao/icp-py-core
Python Agent Library for the DFINITY Internet Computer
https://github.com/eliezhao/icp-py-core
dfinity ic-py ic-py-core icp internetcomputer internetcomputerprotocol python
Last synced: 17 days ago
JSON representation
Python Agent Library for the DFINITY Internet Computer
- Host: GitHub
- URL: https://github.com/eliezhao/icp-py-core
- Owner: eliezhao
- License: mit
- Created: 2025-09-17T08:05:45.000Z (9 months ago)
- Default Branch: master
- Last Pushed: 2026-05-13T09:54:50.000Z (17 days ago)
- Last Synced: 2026-05-13T10:37:50.145Z (17 days ago)
- Topics: dfinity, ic-py, ic-py-core, icp, internetcomputer, internetcomputerprotocol, python
- Language: Python
- Homepage: https://pypi.org/project/icp-py-core
- Size: 6.01 MB
- Stars: 7
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Roadmap: ROADMAP.md
Awesome Lists containing this project
- awesome-internet-computer - icp-py-core - Library for interfacing with the IC using Python. (Client Libraries (Agents) / Python)
README
# ๐ ICP-PY-CORE
---
## ๐ About This Project
**ICP-PY-CORE** is a maintained and extended fork of [ic-py](https://github.com/rocklabs-io/ic-py).
This version introduces a modular architecture, protocol upgrades, and new APIs while preserving compatibility with the IC ecosystem.
**Highlights:**
- โ
Modular structure under `src/` (`icp_agent`, `icp_identity`, `icp_candid`, etc.)
- โ
Updated boundary node endpoints (v3/v4: `/api/v3/canister/.../query`, `/api/v4/canister/.../call`)
- โ
**Certificate verification** enabled by default via `blst` (BLS12-381 signatures)
- โ
Type-safe Candid encoding/decoding with Rust-based parser (multiple times faster)
- โ
Pythonic high-level `Agent.update()` and `Agent.query()` methods
- โ
HTTP/2 support in async methods for improved performance
- โ
Comprehensive structured error handling hierarchy (11 error classes)
- โ
High-level wrappers for Ledger, Governance, Cycles Wallet, and Management canisters
๐ Special thanks to the original `ic-py` author for their foundational work.
### ๐ค Community & Contribution
- **[Contributing Guidelines](./CONTRIBUTING.md)** - How to contribute to the project
- **[Code of Conduct](./CODE_OF_CONDUCT.md)** - Community standards and expectations
- **[Security Policy](./SECURITY.md)** - How to report security vulnerabilities
---
## ๐ง Installation
```bash
pip install icp-py-core
```
> The Candid parser uses a Rust extension with pre-built binary wheels for all platforms.
> No Rust compiler is required for installation.
> For optional certificate verification, see the **blst** section below.
---
## ๐ Key Improvements
### โณ๏ธ Modular Codebase
Each component is isolated for clarity and extensibility:
```
src/
โโโ icp_agent/ # Agent & HTTP Client
โโโ icp_identity/ # ed25519 / secp256k1 identities
โโโ icp_candid/ # Candid encoder/decoder
โโโ icp_principal/ # Principal utilities
โโโ icp_certificate/ # Certificate validation
โโโ icp_core/ # Unified facade (one-line import)
```
### ๐ Unified Facade (`icp_core`)
Import everything from a single entrypoint:
```python
from icp_core import (
Agent, Client,
Identity, DelegateIdentity,
Principal, Certificate,
Canister, Ledger, Governance, Management, CyclesWallet,
encode, decode, Types,
)
```
### โก Endpoint Upgrade
All endpoints now target the latest **Boundary Node** versions:
- Query: `/api/v3/canister//query`
- Call: `/api/v4/canister//call`
- Read State: `/api/v3/canister//read_state`
- Read Subnet State: `/api/v3/subnet//read_state`
### ๐ Certificate Verification
Certificate verification is **enabled by default** for security. Verifies responses via **BLS12-381** signatures with `blst`:
**With Agent directly:**
```python
# Default: verification enabled
agent.update("canister-id", "method_name", [{'type': Types.Nat, 'value': 2}])
# To disable (for compatibility/testing):
agent.update("canister-id", "method_name", [{'type': Types.Nat, 'value': 2}], verify_certificate=False)
```
**With Canister wrapper:**
```python
# Default: verification enabled (matches Agent.update() behavior)
canister.set_value(42)
# Explicitly enable (same as default)
canister.set_value(42, verify_certificate=True)
# Disable verification (when blst is not installed)
canister.set_value(42, verify_certificate=False)
```
> **Note:** Both `Agent.update()` and `Canister` methods default to `verify_certificate=True` for security. If `blst` is not installed, you must explicitly pass `verify_certificate=False` to avoid errors.
---
## ๐งฉ Example Usage
### Identity
```python
from icp_core import Identity
# Example: well-known Ed25519 test vector (RFC 8032); use Identity() or from_seed() for real keys
iden = Identity(privkey="833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42")
print(iden.sender().to_str())
```
### Client & Agent
```python
from icp_core import Agent, Client, Identity
iden = Identity()
client = Client("https://ic0.app")
agent = Agent(iden, client)
```
### Update (auto-encode)
```python
from icp_core import Types
result = agent.update(
"wcrzb-2qaaa-aaaap-qhpgq-cai",
"set",
[{'type': Types.Nat, 'value': 2}],
return_type=[Types.Nat],
)
```
### Query (auto-encode empty args)
```python
reply = agent.query("wcrzb-2qaaa-aaaap-qhpgq-cai", "get", [])
print(reply)
```
### Canister Wrapper (Type-Safe Method Calls)
The `Canister` class provides a high-level, type-safe interface for interacting with canisters. It automatically parses Candid DID files and creates Python methods that match your canister's interface.
**Creating a Canister instance:**
```python
from icp_core import Agent, Client, Identity, Canister
# Setup agent
client = Client("https://ic0.app")
identity = Identity()
agent = Agent(identity, client)
# Define Candid interface
COUNTER_DID = """
service : {
get : () -> (nat) query;
set : (nat) -> (nat)
}
"""
# Create Canister wrapper
counter = Canister(agent, "wcrzb-2qaaa-aaaap-qhpgq-cai", COUNTER_DID)
```
**Calling canister methods:**
```python
# Query call (no arguments)
value = counter.get()
print(f"Current value: {value[0]['value']}")
# Update call (with positional argument)
result = counter.set(42)
print(f"Set to: {result[0]['value']}")
# Update call with keyword arguments (for record types)
# If your method takes a single record parameter, you can use kwargs:
# result = counter.update_profile(name="Alice", age=30)
```
**Certificate Verification with Canister:**
By default, `Canister` methods enable certificate verification (`verify_certificate=True`) to match `Agent.update()` behavior for security. You can control this per method call:
```python
# Default: certificate verification enabled (requires blst)
result = counter.set(42) # Uses verify_certificate=True by default
# Explicitly enable verification (same as default)
result = counter.set(42, verify_certificate=True)
# Disable verification (useful when blst is not installed)
result = counter.set(42, verify_certificate=False)
```
**Important Notes:**
- `verify_certificate` is a **control parameter**, not a method argument. It's extracted from kwargs before processing method arguments.
- Default value is `True` to match `Agent.update()` default behavior for security.
- If `blst` is not installed and you don't pass `verify_certificate=False`, update calls will fail.
- For query calls, certificate verification is not applicable (queries don't return certificates).
**Example: Complete Canister Usage**
```python
from icp_core import Agent, Client, Identity, Canister
# Setup
client = Client("https://ic0.app")
identity = Identity(anonymous=True)
agent = Agent(identity, client)
# Define interface
DID = """
service : {
get : () -> (nat) query;
set : (nat) -> (nat);
increment : () -> (nat)
}
"""
# Create canister wrapper
counter = Canister(agent, "wcrzb-2qaaa-aaaap-qhpgq-cai", DID)
# Query (no verification needed)
current = counter.get()
print(f"Current: {current[0]['value']}")
# Update with verification enabled (default, requires blst)
try:
result = counter.set(100)
print(f"Set to: {result[0]['value']}")
except Exception as e:
if "blst" in str(e).lower():
# Fallback: disable verification if blst not available
result = counter.set(100, verify_certificate=False)
print(f"Set to: {result[0]['value']} (verification disabled)")
else:
raise
# Update with verification explicitly disabled
result = counter.increment(verify_certificate=False)
print(f"Incremented: {result[0]['value']}")
```
---
## โ ๏ธ Error Handling
ICP-PY-CORE provides a structured error hierarchy for better error handling and debugging. All errors inherit from `ICError` and are categorized by type.
### Error Classes
```python
from icp_core import (
ICError, # Base class for all errors
TransportError, # HTTP/network errors
SecurityError, # Base class for security errors
SignatureVerificationFailed,
CertificateVerificationError,
ReplicaReject, # Canister rejection
PayloadEncodingError,
IngressExpiryError,
)
```
### Common Error Scenarios
**Transport Errors (Network Issues):**
```python
from icp_core import Client, TransportError
client = Client()
try:
data = client.query("canister-id", b"data")
except TransportError as e:
print(f"Failed to connect to {e.url}")
print(f"Error: {e.original_error}")
```
**Replica Rejections (Canister Errors):**
```python
from icp_core import Agent, Client, Identity, ReplicaReject
agent = Agent(identity, client)
try:
result = agent.update("canister-id", "method", args)
except ReplicaReject as e:
print(f"Rejected with code {e.reject_code}")
print(f"Message: {e.reject_message}")
if e.error_code:
print(f"Error code: {e.error_code}")
```
**Security Errors (Certificate Verification):**
```python
from icp_core import (
CertificateVerificationError,
SignatureVerificationFailed,
)
try:
certificate.assert_certificate_valid(canister_id)
except CertificateVerificationError as e:
print(f"Certificate verification failed: {e.reason}")
except SignatureVerificationFailed:
print("BLS signature verification failed")
```
### Error Hierarchy
```
ICError (base class)
โโโ TransportError (HTTP/network errors)
โโโ SecurityError (security errors)
โ โโโ SignatureVerificationFailed
โ โโโ CertificateVerificationError
โ โโโ LookupPathMissing
โ โโโ NodeKeyNotFoundError
โ โโโ ReplicaSignatureVerificationFailed
โโโ ReplicaReject (canister rejections)
โโโ PayloadEncodingError (CBOR encoding errors)
โโโ IngressExpiryError (expiry validation errors)
```
### Best Practices
1. **Catch specific errors** for better error handling:
```python
try:
result = agent.update("canister-id", "method", args)
except ReplicaReject as e:
# Handle canister rejection
handle_rejection(e)
except TransportError as e:
# Handle network issues
handle_network_error(e)
except SecurityError as e:
# Handle security issues
handle_security_error(e)
```
2. **Check error attributes** for detailed information:
```python
except ReplicaReject as e:
if e.reject_code == 3:
# Canister trapped
retry_with_different_args()
elif e.reject_code == 4:
# Canister did not reply
check_canister_status()
```
3. **Preserve error context** when re-raising:
```python
try:
result = agent.update("canister-id", "method", args)
except TransportError as e:
logger.error(f"Network error: {e.url}", exc_info=True)
raise # Re-raise to preserve stack trace
```
---
## ๐ Installing `blst` (optional)
`blst` is required for certificate verification (enabled by default). If `blst` is not installed, you can disable verification with `verify_certificate=False`.
### Prerequisites
**macOS:**
```bash
# Install Xcode Command Line Tools
xcode-select --install
# Install SWIG (required for Python bindings)
brew install swig
```
**Linux (Ubuntu/Debian):**
```bash
sudo apt-get update
sudo apt-get install build-essential swig python3-dev
```
**Linux (Fedora/RHEL):**
```bash
sudo dnf install gcc gcc-c++ make swig python3-devel
```
### macOS / Linux Installation
**Method 1: Build and add to PYTHONPATH (recommended for development)**
```bash
git clone https://github.com/supranational/blst
cd blst/bindings/python
# For Apple Silicon (M1/M2/M3) if you encounter ABI issues:
# export BLST_PORTABLE=1
python3 run.me
# Temporary (current session only):
export PYTHONPATH="$PWD:$PYTHONPATH"
# Permanent (add to ~/.bashrc or ~/.zshrc):
echo 'export PYTHONPATH="/path/to/blst/bindings/python:$PYTHONPATH"' >> ~/.bashrc
source ~/.bashrc
```
**Method 2: Install to site-packages (recommended for production)**
```bash
git clone https://github.com/supranational/blst
cd blst/bindings/python
# For Apple Silicon (M1/M2/M3) if needed:
# export BLST_PORTABLE=1
python3 run.me
# Copy to site-packages
BLST_SRC="$PWD"
PYBIN="python3"
SITE_PURE="$($PYBIN -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')"
SITE_PLAT="$($PYBIN -c 'import sysconfig; print(sysconfig.get_paths()["platlib"])')"
cp "$BLST_SRC/blst.py" "$SITE_PURE"/
cp "$BLST_SRC"/_blst*.so "$SITE_PLAT"/
```
**Method 3: Install in virtual environment**
```bash
# Activate your virtual environment first
source venv/bin/activate # or: source .venv/bin/activate
git clone https://github.com/supranational/blst
cd blst/bindings/python
# For Apple Silicon if needed:
# export BLST_PORTABLE=1
python3 run.me
# Copy to virtual environment's site-packages
BLST_SRC="$PWD"
SITE_PURE="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')"
SITE_PLAT="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["platlib"])')"
cp "$BLST_SRC/blst.py" "$SITE_PURE"/
cp "$BLST_SRC"/_blst*.so "$SITE_PLAT"/
```
### Windows Installation
**Option 1: WSL2 (Ubuntu) - Recommended**
1. Install WSL2 and Ubuntu from Microsoft Store
2. Follow the Linux installation instructions above in WSL2
**Option 2: Native Windows (Advanced)**
1. Install Visual Studio Build Tools with C++ support
2. Install SWIG for Windows from [swig.org](http://www.swig.org/download.html)
3. Install Python 3.8+ with development headers
4. Follow the Linux build steps in PowerShell or Command Prompt
5. Note: Windows support is experimental; WSL2 is recommended
### Verify Installation
Test if `blst` is correctly installed:
```python
try:
import blst
assert all(hasattr(blst, n) for n in ("P1_Affine", "P2_Affine", "Pairing", "BLST_SUCCESS"))
print("โ blst is installed and working correctly")
except (ModuleNotFoundError, AssertionError):
print("โ blst is not available or incomplete")
```
Or test with `icp-py-core`:
```python
from icp_certificate.certificate import ensure_blst_available
try:
ensure_blst_available()
print("โ blst is available for certificate verification")
except RuntimeError as e:
print(f"โ {e}")
```
### Troubleshooting
**Issue: "No module named 'blst'"**
- Ensure `blst.py` and `_blst*.so` are in your Python path
- Check `python3 -c "import sys; print(sys.path)"` to see search paths
- If using virtual environment, ensure it's activated
**Issue: "ABI mismatch" on Apple Silicon**
- Set `export BLST_PORTABLE=1` before running `python3 run.me`
- This builds a portable version compatible with all architectures
**Issue: "SWIG not found"**
- Install SWIG: `brew install swig` (macOS) or `sudo apt-get install swig` (Linux)
- Ensure SWIG is in your PATH: `which swig`
**Issue: Import succeeds but API is incomplete**
- Ensure you're using the official `supranational/blst` repository
- Rebuild: `cd blst/bindings/python && python3 run.me`
- Check that all required symbols exist: `P1_Affine`, `P2_Affine`, `Pairing`, `BLST_SUCCESS`
---
## ๐ง Features
1. ๐งฉ Candid encode & decode (Rust-based parser for high performance)
2. ๐ ed25519 & secp256k1 identities
3. ๐งพ Principal utilities (strict DER mode)
4. โ๏ธ High-level canister calls via Agent (`update()`, `query()`)
5. ๐ช Support for Ledger / Governance / Management / Cycles Wallet
6. ๐ Sync & async APIs (low-level methods)
7. ๐ Certificate verification enabled by default (BLS12-381)
8. โก HTTP/2 support in async methods
9. ๐ก๏ธ Structured error handling (11 error classes)
10. ๐ฆ Comprehensive example code library
---
## ๐งฐ Example โ End-to-End
```python
from icp_core import Agent, Client, Identity, Types
client = Client("https://ic0.app")
iden = Identity()
agent = Agent(iden, client)
# Update (auto-encode [42], certificate verification enabled by default)
agent.update("wcrzb-2qaaa-aaaap-qhpgq-cai", "set_value", [42])
# Query (auto-encode empty args)
res = agent.query("wcrzb-2qaaa-aaaap-qhpgq-cai", "get_value", None, return_type=[Types.Nat])
print(res)
```
---
## ๐ Migration
Migrating from **ic-py**? See **[MIGRATION.md](./MIGRATION.md)** for:
- New package layout (`icp_*` subpackages and the `icp_core` facade)
- Endpoint changes (v3 call)
- Argument auto-encoding in `Agent.update()` / `Agent.query()`
- Certificate verification flag
---
## ๐ Changelog
We maintain release notes on GitHub Releases:
**https://github.com/eliezhao/icp-py-core/releases**
---
## ๐บ Roadmap
See [ROADMAP.md](./ROADMAP.md)
โ
**Milestone 1**: v3/v4 endpoint migration, timeouts & error classification
โ
**Milestone 2**: Certificate verification with `blst` (enabled by default)
โ
**Milestone 3**: Candid type-system enhancements (Rust parser, DIDLoader, VarT support)
โ
**Milestone 4**: Expanded API surface (Ledger, Governance, Cycles Wallet, Management), code optimization, HTTP/2 support, structured error handling
โ
**Milestone 5**: Auto-fetch DID files, high-level async API methods (`update_async`, `query_async`), Canister async method support, replica-signed queries
---
## ๐ Version
- Current release: **v2.3.0**
---
## ๐ Acknowledgments
Special thanks to the IC community and contributors to the original **ic-py**.
**icp-py-core** continues this legacy with modern Python standards and long-term maintenance.
---
## ๐ Additional Resources
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** - Guidelines for contributing to the project
- **[CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)** - Community code of conduct
- **[SECURITY.md](./SECURITY.md)** - Security policy and vulnerability reporting
- **[MIGRATION.md](./MIGRATION.md)** - Migration guide from ic-py
- **[CHANGELOG.md](./CHANGELOG.md)** - Release notes and changelog
- **[ROADMAP.md](./ROADMAP.md)** - Project roadmap and future plans