Skip to content

refactor: centralize SCALE codec primitives in utils/scale.py#52

Merged
LandynDev merged 2 commits intoentrius:testfrom
fansilas:refactor/scale-codec-module
Apr 17, 2026
Merged

refactor: centralize SCALE codec primitives in utils/scale.py#52
LandynDev merged 2 commits intoentrius:testfrom
fansilas:refactor/scale-codec-module

Conversation

@fansilas
Copy link
Copy Markdown
Contributor

Summary

SCALE primitive encoders and decoders were duplicated across three modules: contract_client.py, validator/event_watcher.py, and validator/axon_handlers.py.

Each copy carried its own magic byte widths, hex-prefix stripping, and compact-length encoders. This collapses them into a single allways/utils/scale.py that all three modules now import from.

No behavior change for well-formed contract or RPC data. Where the three copies disagreed on error handling (out-of-bounds decode_string, mode-2 compact address length, stray 0x substrings), the extracted module picks the more defensive variant so malformed input fails gracefully rather than silently corrupting output.

Changes

New module: allways/utils/scale.py

  • Constants: SS58_PREFIX, U32_BYTES, U64_BYTES, U128_BYTES, ACCOUNT_ID_BYTES
  • Helpers: strip_hex_prefix, compact_encode_len, encode_bytes, encode_str, encode_u128
  • Streaming decoders returning (value, new_offset): decode_u32, decode_u64, decode_u128, decode_bool, decode_account_id, decode_string

allways/contract_client.py

  • Dropped the local compact_encode_len definition and inline hex-prefix / u128 / byte-width logic
  • encode_value, extract_*, decode_swap_data, get_reservation_data, and raw_contract_read now delegate to the shared helpers
  • Removed the one-line decode_string method that just forwarded to the utility
  • Byte-width constants replace magic 4, 8, 16, 32 literals

allways/validator/event_watcher.py

  • Dropped the parallel copies of decode_u32/u64/u128/bool/account_id/string in favor of imports from utils/scale
  • topic_account_id delegates to decode_account_id, and the hex-prefix strip in to_bytes uses strip_hex_prefix
  • struct and ss58_encode imports removed (no longer needed locally)

allways/validator/axon_handlers.py

  • The three scale_encode_*_hash_input functions now compose encode_bytes, encode_str, and encode_u128 instead of inlining compact_encode_len(len(...)) + ... and .to_bytes(16, 'little')
  • The local encode_str lambda inside scale_encode_initiate_hash_input is gone

tests/test_scale.py

  • TestDecodeString exercises decode_string from the utility directly now that the client shim is gone
  • compact_encode_len imported from its new home

Testing

  • All 267 tests pass (pytest)
  • ruff check allways/ tests/ clean
  • ruff format --check allways/ tests/ clean
  • u128 encoding verified bit-identical against struct.pack('<QQ', lo, hi) across the range [0, 2**128 - 1] including boundaries at 2**64 and 2**127
  • String round-trip passes in all three SCALE compact-length modes (lengths 0, 5, 64, 16383, 16384)
  • SS58 encoding via ss58_encode(bytes, 42) produces the same output as the previous substrate.ss58_encode(bytes.hex()) path on Bittensor

@LandynDev
Copy link
Copy Markdown
Collaborator

Hi @fansilas , thanks for the contribution. I think this could make the codebase cleaner from a quick skim, can you fix conflicts before I look more into it?

@fansilas
Copy link
Copy Markdown
Contributor Author

Hi @LandynDev
Thanks for your review. I've fixed the conflict. Can you please review again?

@LandynDev LandynDev merged commit 5fb5d83 into entrius:test Apr 17, 2026
2 checks passed
plind-junior added a commit to plind-junior/allways that referenced this pull request Apr 18, 2026
…ak assertions

- test_subtensor.py: remove TestDecodeCompact and TestIsValidAddress (already
  in test_scale.py from entrius#52); keep only TestProviderBasics
- test_axon_handlers.py: TestRejectSynapse — patch bt.logging.debug and
  assert (no_context → not called; with_context → called once with
  '{context}: {reason}'); drop the three keccak property tests that
  exercise pycryptodome rather than our wrapper
- test_misc.py: drop test_preserves_function_metadata (functools.wraps)
  and test_returns_current_block_from_subtensor (mock pass-through)
- test_logging.py: drop test_returns_logger_at_events_level,
  test_registers_event_level_name, test_adds_handler — all just verify
  stdlib logging plumbing we passed through
- test_config.py: drop pure-default argparse tests; keep override tests
  which actually exercise our flag-name + type wiring
- test_metadata.py: deleted (only checks pathlib behavior on a
  Path(__file__).parent constant)

363 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants