Fix Block model to match server: nest header with camelCase keys

The block endpoint returns block id / prev / timestamp nested under a
"header" object with camelCase keys (blockId, dcId, prevId, prevProof,
timestamp) and a proof of just {proof}. The previous flat snake_case
Block fields never matched the response and always deserialized empty.
Add a BlockHeader dataclass, nest it in Block, make proof.scheme
optional, and cover it with a unit test. Verified live against a dev
chain.
This commit is contained in:
2026-05-29 17:09:02 -04:00
parent 4a7d8b875a
commit 8b007dcbab
3 changed files with 53 additions and 10 deletions

View File

@@ -45,6 +45,7 @@ from .contract import ContractClient
from .errors import DragonchainAPIError, DragonchainError from .errors import DragonchainAPIError, DragonchainError
from .models import ( from .models import (
Block, Block,
BlockHeader,
BlockProof, BlockProof,
GrpcConnectionInfo, GrpcConnectionInfo,
ListResponse, ListResponse,
@@ -121,6 +122,7 @@ __all__ = [
"TransactionTypeClient", "TransactionTypeClient",
# models # models
"Block", "Block",
"BlockHeader",
"BlockProof", "BlockProof",
"GrpcConnectionInfo", "GrpcConnectionInfo",
"ListResponse", "ListResponse",

View File

@@ -301,26 +301,42 @@ class SmartContract:
@dataclass @dataclass
class BlockProof: class BlockProof:
scheme: str = ""
proof: str = "" proof: str = ""
scheme: str = "" # absent on trust-scheme chains; present on PoW
nonce: int = 0 nonce: int = 0
@classmethod @classmethod
def from_dict(cls, d: Dict[str, Any]) -> "BlockProof": def from_dict(cls, d: Dict[str, Any]) -> "BlockProof":
return cls( return cls(
scheme=d.get("scheme", ""),
proof=d.get("proof", ""), proof=d.get("proof", ""),
scheme=d.get("scheme", ""),
nonce=d.get("nonce", 0), nonce=d.get("nonce", 0),
) )
@dataclass
class BlockHeader:
block_id: str = ""
dc_id: str = ""
prev_id: str = ""
prev_proof: str = ""
timestamp: str = ""
@classmethod
def from_dict(cls, d: Dict[str, Any]) -> "BlockHeader":
return cls(
block_id=d.get("blockId", ""),
dc_id=d.get("dcId", ""),
prev_id=d.get("prevId", ""),
prev_proof=d.get("prevProof", ""),
timestamp=d.get("timestamp", ""),
)
@dataclass @dataclass
class Block: class Block:
version: str = "" version: str = ""
id: str = "" header: BlockHeader = field(default_factory=BlockHeader)
timestamp: str = ""
prev_id: str = ""
prev_proof: str = ""
transactions: List[str] = field(default_factory=list) transactions: List[str] = field(default_factory=list)
proof: BlockProof = field(default_factory=BlockProof) proof: BlockProof = field(default_factory=BlockProof)
@@ -328,10 +344,7 @@ class Block:
def from_dict(cls, d: Dict[str, Any]) -> "Block": def from_dict(cls, d: Dict[str, Any]) -> "Block":
return cls( return cls(
version=d.get("version", ""), version=d.get("version", ""),
id=d.get("block_id", ""), header=BlockHeader.from_dict(d.get("header") or {}),
timestamp=d.get("timestamp", ""),
prev_id=d.get("prev_id", ""),
prev_proof=d.get("prev_proof", ""),
transactions=d.get("transactions") or [], transactions=d.get("transactions") or [],
proof=BlockProof.from_dict(d.get("proof") or {}), proof=BlockProof.from_dict(d.get("proof") or {}),
) )

View File

@@ -13,6 +13,7 @@ import json
import pytest import pytest
from prime_sdk import ( from prime_sdk import (
Block,
DragonchainAPIError, DragonchainAPIError,
DragonchainSDK, DragonchainSDK,
SmartContractCreateRequest, SmartContractCreateRequest,
@@ -163,6 +164,33 @@ def test_transaction_from_dict_nested():
assert txn.payload == "{}" assert txn.payload == "{}"
def test_block_from_dict_nested_header():
# Real server shape: id/prev/timestamp nested under "header" with camelCase
# keys; proof carries only "proof" on a trust-scheme chain.
raw = {
"version": "1",
"header": {
"blockId": "69569983",
"dcId": "chain-xyz",
"prevId": "69186326",
"prevProof": "MEUCIQ...",
"timestamp": "1780088135",
},
"proof": {"proof": "MEQCIF..."},
"transactions": ["{...}", "{...}", "{...}"],
}
blk = Block.from_dict(raw)
assert blk.version == "1"
assert blk.header.block_id == "69569983"
assert blk.header.dc_id == "chain-xyz"
assert blk.header.prev_id == "69186326"
assert blk.header.prev_proof == "MEUCIQ..."
assert blk.header.timestamp == "1780088135"
assert blk.proof.proof == "MEQCIF..."
assert blk.proof.scheme == "" # absent on trust-scheme chain
assert len(blk.transactions) == 3
def test_contract_create_request_omitempty(): def test_contract_create_request_omitempty():
req = SmartContractCreateRequest( req = SmartContractCreateRequest(
environment="python3.8", environment="python3.8",