get_interchain: per_chain + chains options (default first anchor per chain); bump 0.5.0
All checks were successful
Publish to PyPI Registry / publish (release) Successful in 23s
All checks were successful
Publish to PyPI Registry / publish (release) Successful in 23s
transaction.get_interchain / block.get_interchain take per_chain= and chains= kwargs mapping to prime-node's ?perChain=&chains= params. Default (no kwargs) returns one anchor per chain. Shared interchain_query() helper, exported; pytest.
This commit is contained in:
18
README.md
18
README.md
@@ -151,6 +151,24 @@ client = DragonchainSDK(
|
|||||||
- `proof_measure.report(req)` — Per-transaction "securedBy" report over interchain anchors.
|
- `proof_measure.report(req)` — Per-transaction "securedBy" report over interchain anchors.
|
||||||
- `proof_measure.health()` — Service liveness.
|
- `proof_measure.health()` — Service liveness.
|
||||||
|
|
||||||
|
## Interchain trace
|
||||||
|
|
||||||
|
`transaction.get_interchain` / `block.get_interchain` trace a prime block to the
|
||||||
|
public-chain anchors covering it. By default they return the **first anchor per
|
||||||
|
chain** (anchor proofs are chained, so the earliest per chain is the meaningful
|
||||||
|
one):
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Default: first anchor per chain (ETH, BTC, …)
|
||||||
|
trace = client.block.get_interchain("42")
|
||||||
|
|
||||||
|
# Up to 3 anchors per chain
|
||||||
|
trace = client.block.get_interchain("42", per_chain=3)
|
||||||
|
|
||||||
|
# All anchors, only the ETH-mainnet chain ("1"); "0" = BTC
|
||||||
|
trace = client.block.get_interchain("42", per_chain=0, chains=["1"])
|
||||||
|
```
|
||||||
|
|
||||||
## Proof Measure
|
## Proof Measure
|
||||||
|
|
||||||
`proof-measure` is a separate, **public, unauthenticated** Dragonchain service
|
`proof-measure` is a separate, **public, unauthenticated** Dragonchain service
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ from .models import (
|
|||||||
SecurityResult,
|
SecurityResult,
|
||||||
TransactionReport,
|
TransactionReport,
|
||||||
)
|
)
|
||||||
|
from .interchain import interchain_query
|
||||||
from .proof_measure import DEFAULT_BASE_URL as PROOF_MEASURE_DEFAULT_BASE_URL
|
from .proof_measure import DEFAULT_BASE_URL as PROOF_MEASURE_DEFAULT_BASE_URL
|
||||||
from .proof_measure import ProofMeasureClient
|
from .proof_measure import ProofMeasureClient
|
||||||
from .system import SystemClient
|
from .system import SystemClient
|
||||||
@@ -88,7 +89,7 @@ from .transaction import TransactionClient
|
|||||||
from .transaction_type import TransactionTypeClient
|
from .transaction_type import TransactionTypeClient
|
||||||
from .unauthenticated_client import UnauthenticatedClient
|
from .unauthenticated_client import UnauthenticatedClient
|
||||||
|
|
||||||
__version__ = "0.4.0"
|
__version__ = "0.5.0"
|
||||||
|
|
||||||
|
|
||||||
class DragonchainSDK:
|
class DragonchainSDK:
|
||||||
@@ -148,6 +149,7 @@ __all__ = [
|
|||||||
"ProofMeasureClient",
|
"ProofMeasureClient",
|
||||||
"UnauthenticatedClient",
|
"UnauthenticatedClient",
|
||||||
"PROOF_MEASURE_DEFAULT_BASE_URL",
|
"PROOF_MEASURE_DEFAULT_BASE_URL",
|
||||||
|
"interchain_query",
|
||||||
# models
|
# models
|
||||||
"Block",
|
"Block",
|
||||||
"BlockHeader",
|
"BlockHeader",
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
"""Block endpoints."""
|
"""Block endpoints."""
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from .client import Client
|
from .client import Client
|
||||||
|
from .interchain import interchain_query
|
||||||
from .models import Block, InterchainTrace
|
from .models import Block, InterchainTrace
|
||||||
|
|
||||||
|
|
||||||
@@ -11,10 +14,21 @@ class BlockClient:
|
|||||||
def get(self, block_id: str) -> Block:
|
def get(self, block_id: str) -> Block:
|
||||||
return self._client.get(f"/api/v1/block/{block_id}", Block)
|
return self._client.get(f"/api/v1/block/{block_id}", Block)
|
||||||
|
|
||||||
def get_interchain(self, block_id: str) -> InterchainTrace:
|
def get_interchain(
|
||||||
|
self,
|
||||||
|
block_id: str,
|
||||||
|
per_chain: Optional[int] = None,
|
||||||
|
chains: Optional[List[str]] = None,
|
||||||
|
) -> InterchainTrace:
|
||||||
"""Trace a block to the validator (verification) blocks that validated it
|
"""Trace a block to the validator (verification) blocks that validated it
|
||||||
and the public-chain interchain anchors those validator blocks were
|
and the public-chain interchain anchors those validator blocks were
|
||||||
bundled into."""
|
bundled into.
|
||||||
|
|
||||||
|
By default returns the first anchor per public chain (anchor proofs are
|
||||||
|
chained, so the earliest per chain is the meaningful one). ``per_chain``
|
||||||
|
caps anchors per chain (1 = first per chain; 0 = all); ``chains``
|
||||||
|
restricts to specific chain ids."""
|
||||||
return self._client.get(
|
return self._client.get(
|
||||||
f"/api/v1/block/{block_id}/interchain", InterchainTrace
|
f"/api/v1/block/{block_id}/interchain{interchain_query(per_chain, chains)}",
|
||||||
|
InterchainTrace,
|
||||||
)
|
)
|
||||||
|
|||||||
27
prime_sdk/interchain.py
Normal file
27
prime_sdk/interchain.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
"""Query builder for the interchain-trace endpoints.
|
||||||
|
|
||||||
|
Anchor proofs are chained, so by default the trace returns the first anchor per
|
||||||
|
public chain; these options change how many and which chains are returned.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
||||||
|
def interchain_query(
|
||||||
|
per_chain: Optional[int] = None, chains: Optional[List[str]] = None
|
||||||
|
) -> str:
|
||||||
|
"""Build the "?perChain=...&chains=..." suffix for the interchain trace
|
||||||
|
endpoints.
|
||||||
|
|
||||||
|
Returns "" when nothing is set (the server then applies its defaults: one
|
||||||
|
anchor per chain, all chains). ``per_chain`` caps anchors per public chain,
|
||||||
|
earliest-first (1 = first per chain; 0 = all). ``chains`` restricts to the
|
||||||
|
given chain ids ("1" = ETH mainnet, "0" = BTC; testnet ids differ).
|
||||||
|
"""
|
||||||
|
parts: List[str] = []
|
||||||
|
if per_chain is not None:
|
||||||
|
parts.append(f"perChain={int(per_chain)}")
|
||||||
|
if chains:
|
||||||
|
parts.append("chains=" + ",".join(quote(str(c), safe="") for c in chains))
|
||||||
|
return "?" + "&".join(parts) if parts else ""
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
"""Transaction endpoints."""
|
"""Transaction endpoints."""
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from .client import CONTENT_TYPE_JSON, Client
|
from .client import CONTENT_TYPE_JSON, Client
|
||||||
|
from .interchain import interchain_query
|
||||||
from .models import (
|
from .models import (
|
||||||
InterchainTrace,
|
InterchainTrace,
|
||||||
ListTransactionsResponse,
|
ListTransactionsResponse,
|
||||||
@@ -40,13 +43,25 @@ class TransactionClient:
|
|||||||
def get(self, transaction_id: str) -> Transaction:
|
def get(self, transaction_id: str) -> Transaction:
|
||||||
return self._client.get(f"/api/v1/transaction/{transaction_id}", Transaction)
|
return self._client.get(f"/api/v1/transaction/{transaction_id}", Transaction)
|
||||||
|
|
||||||
def get_interchain(self, transaction_id: str) -> InterchainTrace:
|
def get_interchain(
|
||||||
|
self,
|
||||||
|
transaction_id: str,
|
||||||
|
per_chain: Optional[int] = None,
|
||||||
|
chains: Optional[List[str]] = None,
|
||||||
|
) -> InterchainTrace:
|
||||||
"""Trace a transaction to the validator (verification) blocks that
|
"""Trace a transaction to the validator (verification) blocks that
|
||||||
validated its prime block and the public-chain interchain anchors those
|
validated its prime block and the public-chain interchain anchors those
|
||||||
validator blocks were bundled into. If the transaction is still pending
|
validator blocks were bundled into. If the transaction is still pending
|
||||||
(not yet in a block) the trace's lists are empty."""
|
(not yet in a block) the trace's lists are empty.
|
||||||
|
|
||||||
|
By default returns the first anchor per public chain (anchor proofs are
|
||||||
|
chained, so the earliest per chain is the meaningful one). ``per_chain``
|
||||||
|
caps anchors per chain (1 = first per chain; 0 = all); ``chains``
|
||||||
|
restricts to specific chain ids."""
|
||||||
return self._client.get(
|
return self._client.get(
|
||||||
f"/api/v1/transaction/{transaction_id}/interchain", InterchainTrace
|
f"/api/v1/transaction/{transaction_id}/interchain"
|
||||||
|
f"{interchain_query(per_chain, chains)}",
|
||||||
|
InterchainTrace,
|
||||||
)
|
)
|
||||||
|
|
||||||
def list(self) -> ListTransactionsResponse:
|
def list(self) -> ListTransactionsResponse:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "prime-sdk-python"
|
name = "prime-sdk-python"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
description = "A self-contained Python SDK for interacting with Dragonchain nodes."
|
description = "A self-contained Python SDK for interacting with Dragonchain nodes."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
|
|||||||
22
tests/test_interchain.py
Normal file
22
tests/test_interchain.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"""Offline tests for the interchain-trace query builder."""
|
||||||
|
|
||||||
|
from prime_sdk import interchain_query
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_when_nothing_set():
|
||||||
|
assert interchain_query() == ""
|
||||||
|
assert interchain_query(None, None) == ""
|
||||||
|
assert interchain_query(chains=[]) == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_per_chain_including_zero():
|
||||||
|
assert interchain_query(per_chain=1) == "?perChain=1"
|
||||||
|
assert interchain_query(per_chain=0) == "?perChain=0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_chains_joined_with_commas():
|
||||||
|
assert interchain_query(chains=["1", "0"]) == "?chains=1,0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_combined():
|
||||||
|
assert interchain_query(per_chain=2, chains=["1", "0"]) == "?perChain=2&chains=1,0"
|
||||||
Reference in New Issue
Block a user