def test_mixed_uint_rshifts(): assert uint64(0xff00_0000_0000_0000) >> uint8(8 * 7) == uint64(0xff) assert uint16(0xff) >> uint8(8) == uint16(0) assert uint16(0xff) >> uint8(6) == uint16(3) assert uint256(1 << 255) >> uint8(255) == uint256(1) assert uint256(1 << 255) >> uint16(255) == uint256(1) assert uint8(42) << uint64(3) == uint8((42 << 3) & 0xff)
def deserialize(cls: Type[V], stream: BinaryIO, scope: int) -> V: if scope < 1: raise Exception("cannot have empty scope for bitlist, need at least a delimiting bit") if scope > cls.max_byte_length(): raise Exception(f"scope is too large: {scope}, max bitlist byte length is: {cls.max_byte_length()}") chunks: PyList[Node] = [] bytelen = scope - 1 # excluding the last byte (which contains the delimiting bit) while scope > 32: chunks.append(RootNode(Root(stream.read(32)))) scope -= 32 # scope is [1, 32] here last_chunk_part = stream.read(scope) last_byte = int(last_chunk_part[scope-1]) if last_byte == 0: raise Exception("last byte must not be 0: bitlist requires delimiting bit") last_byte_bitlen = last_byte.bit_length() - 1 # excluding the delimiting bit bitlen = bytelen * 8 + last_byte_bitlen if bitlen % 256 != 0: last_chunk = last_chunk_part[:scope-1] +\ (last_byte ^ (1 << last_byte_bitlen)).to_bytes(length=1, byteorder='little') last_chunk += b"\x00" * (32 - len(last_chunk)) chunks.append(RootNode(Root(last_chunk))) if bitlen > cls.limit(): raise Exception(f"bitlist too long: {bitlen}, delimiting bit is over limit ({cls.limit()})") contents = subtree_fill_to_contents(chunks, cls.contents_depth()) backing = PairNode(contents, uint256(bitlen).get_backing()) return cast(Bitlist, cls.view_from_backing(backing))
def pop(self): ll = self.length() if ll == 0: raise Exception("list is empty, cannot pop") i = ll - 1 chunk_i = i // 256 target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i & 0xff == 0: set_last = self.get_backing().setter(target) next_backing = set_last(zero_node(0)) else: set_last = self.get_backing().setter(target) chunk = self.get_backing().getter(target) next_backing = set_last(_new_chunk_with_bit(chunk, ll & 0xff, boolean(False))) # if possible, summarize can_summarize = (target & 1) == 0 if can_summarize: # summarize to the highest node possible. # I.e. the resulting target must be a right-hand, unless it's the only content node. while (target & 1) == 0 and target != 0b10: target >>= 1 summary_fn = next_backing.summarize_into(target) next_backing = summary_fn() set_length = next_backing.rebind_right new_length = uint256(ll - 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def test_mixed_uint_lshifts(): assert uint64(0xff) << uint8(8 * 7) == uint64(0xff00_0000_0000_0000) assert uint16(0xff) << uint8(8 * 7) == uint16(0) assert uint256(1) << uint8(255) == uint256(1 << 255) assert uint256(1) << uint16(255) == uint256(1 << 255) assert uint256(1) << uint16(256) == uint256(0) assert uint8(42) >> uint64(3) == uint8(42 >> 3)
def __new__(cls, *args, **kwargs): vals = list(args) if len(vals) > 0: if len(vals) == 1 and isinstance(vals[0], (GeneratorType, list, tuple)): vals = list(vals[0]) limit = cls.limit() if len(vals) > limit: raise Exception(f"too many bitlist inputs: {len(vals)}, limit is: {limit}") input_bits = list(map(bool, vals)) input_nodes = pack_bits_to_chunks(input_bits) contents = subtree_fill_to_contents(input_nodes, cls.contents_depth()) kwargs['backing'] = PairNode(contents, uint256(len(input_bits)).get_backing()) return super().__new__(cls, **kwargs)
def pop(self): ll = self.length() if ll == 0: raise Exception("list is empty, cannot pop") i = ll - 1 target: Gindex can_summarize: bool if self.__class__.is_packed(): next_backing = self.get_backing() elem_type: Type[View] = self.__class__.element_cls() if isinstance(elem_type, BasicTypeDef): elems_per_chunk = 32 // elem_type.type_byte_length() chunk_i = i // elems_per_chunk target = to_gindex(chunk_i, self.__class__.tree_depth()) if i % elems_per_chunk == 0: chunk = zero_node(0) else: chunk = next_backing.getter(target) set_last = next_backing.setter(target) chunk = cast(BasicView, elem_type.default(None)).backing_from_base( chunk, i % elems_per_chunk) next_backing = set_last(chunk) can_summarize = (target & 1) == 0 and i % elems_per_chunk == 0 else: raise Exception( "cannot pop a packed element that is not a basic type") else: target = to_gindex(i, self.__class__.tree_depth()) set_last = self.get_backing().setter(target) next_backing = set_last(zero_node(0)) can_summarize = (target & 1) == 0 # if possible, summarize if can_summarize: # summarize to the highest node possible. # I.e. the resulting target must be a right-hand, unless it's the only content node. while (target & 1) == 0 and target != 0b10: target >>= 1 summary_fn = next_backing.summarize_into(target) next_backing = summary_fn() set_length = next_backing.rebind_right new_length = uint256(ll - 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def append(self, v: boolean): ll = self.length() if ll >= self.__class__.limit(): raise Exception("list is maximum capacity, cannot append") i = ll chunk_i = i // 256 target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i & 0xff == 0: set_last = self.get_backing().setter(target, expand=True) next_backing = set_last(_new_chunk_with_bit(zero_node(0), 0, v)) else: set_last = self.get_backing().setter(target) chunk = self.get_backing().getter(target) next_backing = set_last(_new_chunk_with_bit(chunk, i & 0xff, v)) set_length = next_backing.rebind_right new_length = uint256(ll + 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def __new__(cls, *args, backing: Optional[Node] = None, hook: Optional[ViewHook] = None, **kwargs): if backing is not None: if len(args) != 0: raise Exception( "cannot have both a backing and elements to init List") return super().__new__(cls, backing=backing, hook=hook, **kwargs) elem_cls = cls.element_cls() vals = list(args) if len(vals) == 1: val = vals[0] if isinstance(val, (GeneratorType, list, tuple)): vals = list(val) if issubclass(elem_cls, uint8): if isinstance(val, bytes): vals = list(val) if isinstance(val, str): if val[:2] == '0x': val = val[2:] vals = list(bytes.fromhex(val)) if len(vals) > 0: limit = cls.limit() if len(vals) > limit: raise Exception( f"too many list inputs: {len(vals)}, limit is: {limit}") input_views = [] for el in vals: if isinstance(el, View): input_views.append(el) else: input_views.append(elem_cls.coerce_view(el)) input_nodes = cls.views_into_chunks(input_views) contents = subtree_fill_to_contents(input_nodes, cls.contents_depth()) backing = PairNode(contents, uint256(len(input_views)).get_backing()) return super().__new__(cls, backing=backing, hook=hook, **kwargs)
def append(self, v: View): ll = self.length() if ll >= self.__class__.limit(): raise Exception("list is maximum capacity, cannot append") i = ll elem_type: Type[View] = self.__class__.element_cls() if not isinstance(v, elem_type): v = elem_type.coerce_view(v) if self.__class__.is_packed(): next_backing = self.get_backing() if isinstance(elem_type, BasicTypeDef): if not isinstance(v, BasicView): raise Exception("input element is not a basic view") basic_v: BasicView = v elems_per_chunk = 32 // elem_type.type_byte_length() chunk_i = i // elems_per_chunk target: Gindex = to_gindex(chunk_i, self.__class__.tree_depth()) if i % elems_per_chunk == 0: set_last = next_backing.setter(target, expand=True) chunk = zero_node(0) else: set_last = next_backing.setter(target) chunk = next_backing.getter(target) chunk = basic_v.backing_from_base(chunk, i % elems_per_chunk) next_backing = set_last(chunk) else: raise Exception( "cannot append a packed element that is not a basic type") else: target: Gindex = to_gindex(i, self.__class__.tree_depth()) set_last = self.get_backing().setter(target, expand=True) next_backing = set_last(v.get_backing()) set_length = next_backing.rebind_right new_length = uint256(ll + 1).get_backing() next_backing = set_length(new_length) self.set_backing(next_backing)
def get_backing(self) -> Node: return PairNode( subtree_fill_to_contents(pack_bytes_to_chunks(self), self.__class__.contents_depth()), uint256(len(self)).get_backing())
List[uint16, 32](uint16(0xaabb), uint16(0xc0ad), uint16(0xeeff)), "bbaaadc0ffee", h(h(chunk("bbaaadc0ffee"), chunk("")), chunk("03000000")), # max length: 32 * 2 = 64 bytes = 2 chunks [0xaabb, 0xc0ad, 0xeeff]), ( "uint32 list", List[uint32, 128], List[uint32, 128](uint32(0xaabb), uint32(0xc0ad), uint32(0xeeff)), "bbaa0000adc00000ffee0000", # max length: 128 * 4 = 512 bytes = 16 chunks h(merge(chunk("bbaa0000adc00000ffee0000"), zero_hashes[0:4]), chunk("03")), [0xaabb, 0xc0ad, 0xeeff] # still the same, no padding, just literals ), ("uint256 list", List[uint256, 32], List[uint256, 32](uint256(0xaabb), uint256(0xc0ad), uint256(0xeeff)), "bbaa000000000000000000000000000000000000000000000000000000000000" "adc0000000000000000000000000000000000000000000000000000000000000" "ffee000000000000000000000000000000000000000000000000000000000000", h( merge(h(h(chunk("bbaa"), chunk("adc0")), h(chunk("ffee"), chunk(""))), zero_hashes[2:5]), chunk("03")), [ "0xbbaa000000000000000000000000000000000000000000000000000000000000", "0xadc0000000000000000000000000000000000000000000000000000000000000", "0xffee000000000000000000000000000000000000000000000000000000000000", ]), ( "uint256 list long",