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:
@@ -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",
|
||||||
|
|||||||
@@ -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 {}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user