''' Definitions of several useful types commonly seen on Partisia Blockchain. Follows the ABI specification quite closely. The full ABI specification can be found at: https://partisiablockchain.gitlab.io/documentation/abiv.html ''' import datetime import dataclasses from typing import Mapping, Collection, Optional, Union from enum import Enum import enforce_typing from .binaryreader import BinaryReader def hexformat(bts: bytes, sep=''): return sep.join('{:02X}'.format(c) for c in bts) class AddressType(Enum): ''' Type of a PBC BlockchainAddress. ''' ACCOUNT = 0x00 CONTRACT_SYSTEM = 0x01 CONTRACT_PUBLIC = 0x02 CONTRACT_ZK = 0x03 CONTRACT_GOVERANCE = 0x04 def human_name(self): ''' Produces a human-readable name for self. ''' return ADDRESS_TYPES[self] ADDRESS_TYPES = { AddressType.ACCOUNT: 'Account', AddressType.CONTRACT_SYSTEM: 'System Contract', AddressType.CONTRACT_PUBLIC: 'Public Contract', AddressType.CONTRACT_ZK: 'ZK Contract', AddressType.CONTRACT_GOVERANCE: 'Governance Contract', } @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class ByteData(object): ''' Supertype for several built-in PBC types. ''' data: bytes def to_hex(self): ''' Formats this byte data as a hex string. ''' return hexformat(self.data) def short_str(self): ''' Formats this byte data as a short hex string. ''' long = str(self) return '{}..{}'.format(long[:4], long[-4:]) @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class BlockchainAddress(ByteData): ''' Address on PBC. ''' BYTE_LEN = 21 def __post_init__(self): assert len(self.data) == BlockchainAddress.BYTE_LEN assert self.type() @staticmethod def read_from(reader): return BlockchainAddress(reader.readBytes(BlockchainAddress.BYTE_LEN)) @staticmethod def from_hex_hash(s): return BlockchainAddress(bytes.fromhex(s)) def type(self) -> AddressType: return AddressType(self.data[0]) def __repr__(self): return self.to_hex() def __str__(self): return repr(self) def shard_id(self, num_shards: int) -> str: reader = BinaryReader(self.data) reader.readBytes(17) shard = reader.readUInt32BigEndian() shard = abs(shard) % num_shards return f'Shard{shard}' @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class Hash(ByteData): ''' Hash. ''' data: bytes BYTE_LEN = 32 @staticmethod def read_from(reader): return Hash(reader.readBytes(Hash.BYTE_LEN)) @classmethod def from_hex(cls, s): return Hash(bytes.fromhex(s)) def __repr__(self): return self.to_hex() def __str__(self): return repr(self) @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class PublicKey(ByteData): ''' PBC public key. ''' data: bytes BYTE_LEN = 33 @staticmethod def read_from(reader): return PublicKey(reader.readBytes(PublicKey.BYTE_LEN)) def __repr__(self): return self.to_hex() def __str__(self): return repr(self) @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class Signature(ByteData): ''' Message signature on PBC. ''' data: bytes BYTE_LEN = 65 @staticmethod def read_from(reader): return Signature(reader.readBytes(Signature.BYTE_LEN)) def __repr__(self): return self.to_hex() def __str__(self): return repr(self) @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class BlsPublicKey(ByteData): ''' BLS Public Key. ''' data: bytes BYTE_LEN = 96 @staticmethod def read_from(reader): return BlsPublicKey(reader.readBytes(BlsPublicKey.BYTE_LEN)) def __repr__(self): return self.to_hex() def __str__(self): return repr(self) @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True, order=True) class BlsSignature(ByteData): ''' BLS signature. ''' data: bytes BYTE_LEN = 48 @staticmethod def read_from(reader): return BlsSignature(reader.readBytes(BlsSignature.BYTE_LEN)) def __repr__(self): return self.to_hex() def __str__(self): return repr(self)