Skip to content

Commit

Permalink
feat: separate CSM frame log publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
madlabman committed Sep 16, 2024
1 parent f6ab2af commit 9d18291
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 36 deletions.
2 changes: 1 addition & 1 deletion assets/CSFeeOracle.json

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions src/modules/csm/csm.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,16 @@ def build_report(self, blockstamp: ReferenceBlockStamp) -> tuple:
tree = self.make_tree(shares)
tree_cid: CID | None = None

log_cid = self.publish_log(log)
if tree:
tree_cid = self.publish_tree(tree, log)
tree_cid = self.publish_tree(tree)

return ReportData(
self.report_contract.get_consensus_version(blockstamp.block_hash),
blockstamp.ref_slot,
tree_root=tree.root if tree else prev_root,
tree_cid=tree_cid or prev_cid or "",
log_cid=log_cid,
distributed=distributed,
).as_tuple()

Expand Down Expand Up @@ -312,13 +314,16 @@ def make_tree(self, shares: dict[NodeOperatorId, Shares]) -> Tree | None:
logger.info({"msg": "New tree built for the report", "root": repr(tree.root)})
return tree

def publish_tree(self, tree: Tree, log: FramePerfLog) -> CID:
log_cid = self.w3.ipfs.publish(log.encode())
logger.info({"msg": "Frame log uploaded to IPFS", "cid": repr(log_cid)})
tree_cid = self.w3.ipfs.publish(tree.encode({"logCID": log_cid}))
def publish_tree(self, tree: Tree) -> CID:
tree_cid = self.w3.ipfs.publish(tree.encode())
logger.info({"msg": "Tree dump uploaded to IPFS", "cid": repr(tree_cid)})
return tree_cid

def publish_log(self, log: FramePerfLog) -> CID:
log_cid = self.w3.ipfs.publish(log.encode())
logger.info({"msg": "Frame log uploaded to IPFS", "cid": repr(log_cid)})
return log_cid

@lru_cache(maxsize=1)
def current_frame_range(self, blockstamp: BlockStamp) -> tuple[EpochNumber, EpochNumber]:
converter = self.converter(blockstamp)
Expand Down
18 changes: 5 additions & 13 deletions src/modules/csm/tree.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
from dataclasses import dataclass
from typing import Self, Sequence, TypedDict
from typing import Self, Sequence

from hexbytes import HexBytes
from oz_merkle_tree import Dump, StandardMerkleTree
Expand All @@ -9,14 +9,6 @@
from src.providers.ipfs.cid import CID


class TreeMeta(TypedDict):
logCID: CID


class TreeDump(Dump[RewardTreeLeaf]):
metadata: TreeMeta


class TreeJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, bytes):
Expand Down Expand Up @@ -45,7 +37,7 @@ def decode(cls, content: bytes) -> Self:
except json.JSONDecodeError as e:
raise ValueError("Unsupported tree format") from e

def encode(self, metadata: TreeMeta) -> bytes:
def encode(self) -> bytes:
"""Convert the underlying StandardMerkleTree to a binary representation"""

return (
Expand All @@ -54,12 +46,12 @@ def encode(self, metadata: TreeMeta) -> bytes:
separators=(',', ':'),
sort_keys=True,
)
.encode(self.dump(metadata))
.encode(self.dump())
.encode()
)

def dump(self, metadata: TreeMeta) -> TreeDump:
return {**self.tree.dump(), "metadata": metadata}
def dump(self) -> Dump[RewardTreeLeaf]:
return self.tree.dump()

@classmethod
def new(cls, values: Sequence[RewardTreeLeaf]) -> Self:
Expand Down
1 change: 1 addition & 0 deletions src/modules/csm/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ReportData:
ref_slot: SlotNumber
tree_root: HexBytes
tree_cid: CID | Literal[""]
log_cid: CID | Literal[""]
distributed: int

def as_tuple(self):
Expand Down
22 changes: 5 additions & 17 deletions tests/modules/csm/test_tree.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import pytest

from src.constants import UINT64_MAX
from src.modules.csm.tree import StandardMerkleTree, Tree, TreeJSONEncoder, TreeMeta
from src.providers.ipfs.cid import CIDv0
from src.modules.csm.tree import StandardMerkleTree, Tree, TreeJSONEncoder
from src.types import NodeOperatorId


Expand All @@ -18,31 +17,20 @@ def tree():
)


@pytest.fixture()
def meta() -> TreeMeta:
return {"logCID": CIDv0("Qm...")}


def test_non_null_root(tree: Tree):
assert tree.root


def test_encode_decode(tree: Tree, meta: TreeMeta):
decoded = Tree.decode(tree.encode(meta))
def test_encode_decode(tree: Tree):
decoded = Tree.decode(tree.encode())
assert decoded.root == tree.root


def test_metadata_in_dump(tree: Tree, meta: TreeMeta):
dump = tree.dump(meta)
assert "metadata" in dump
assert dump["metadata"] == meta


def test_decode_plain_tree_dump(tree: Tree):
decoded = Tree.decode(TreeJSONEncoder().encode(tree.tree.dump()).encode())
assert decoded.root == tree.root


def test_dump_compatibility(tree: Tree, meta: TreeMeta):
loaded = StandardMerkleTree.load(tree.dump(meta))
def test_dump_compatibility(tree: Tree):
loaded = StandardMerkleTree.load(tree.dump())
assert loaded.root == tree.root

0 comments on commit 9d18291

Please sign in to comment.