def test_compute_root_multiple_hashes(self): expected_hash = hashlib.sha256(hashlib.sha256(binascii.unhexlify(b'aa' * 32 + b'bb' * 32)).digest()).digest() hash1 = types.UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = types.UInt256(data=binascii.unhexlify(b'bb' * 32)) hashes = [hash1, hash2] root = crypto.MerkleTree.compute_root(hashes) self.assertEqual(expected_hash, root.to_array())
def test_merkle_node_methods(self): hash1 = types.UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = types.UInt256(data=binascii.unhexlify(b'bb' * 32)) hashes = [hash1, hash2] m = crypto.MerkleTree(hashes) self.assertEqual(True, m.root.is_root()) self.assertEqual(False, m.root.is_leaf()) self.assertEqual(False, m.root.left_child.is_root()) self.assertEqual(True, m.root.left_child.is_leaf())
def deserialize_unsigned(self, reader: serialization.BinaryReader) -> None: (self.version, prev_hash, merkleroot, self.timestamp, self.index, self.primary_index, consensus) = struct.unpack("<I32s32sQIB20s", reader._stream.read(101)) if self.primary_index >= len(settings.standby_validators): raise ValueError(f"Deserialization error - primary index {self.primary_index} exceeds validator count " f"{len(settings.standby_validators)}") self.prev_hash = types.UInt256(prev_hash) self.merkle_root = types.UInt256(merkleroot) self.next_consensus = types.UInt160(consensus)
def hash(self) -> types.UInt256: with serialization.BinaryWriter() as bw: self.serialize_unsigned(bw) data_to_hash = bytearray(bw._stream.getvalue()) data = hashlib.sha256( hashlib.sha256(data_to_hash).digest()).digest() return types.UInt256(data=data)
def get_tx_from_block(self, snapshot: storage.Snapshot, block_index_or_hash: bytes, tx_index: int) -> Optional[payloads.Transaction]: """ Get a transaction from a specific block Args: snapshot: block_index_or_hash: the height or block hash of the block we wish to retrieve tx_index: the index into the blocks transaction list of the transaction we want to fetch. Returns: """ if len(block_index_or_hash) < types.UInt256._BYTE_LEN: height = vm.BigInteger(block_index_or_hash) if height < 0 or height > 4294967295: # uint.MaxValue raise ValueError("Invalid height") block = snapshot.blocks.try_get_by_height(height, read_only=True) elif len(block_index_or_hash) == types.UInt256._BYTE_LEN: block_hash = types.UInt256(block_index_or_hash) block = snapshot.blocks.try_get(block_hash, read_only=True) else: raise ValueError("Invalid data") if block and not self._is_traceable_block(snapshot, block.index): block = None if tx_index < 0 or tx_index >= len(block.transactions): raise ValueError("Transaction index out of range") return block.transactions[tx_index]
def get_block(self, snapshot: storage.Snapshot, index_or_hash: bytes) -> Optional[payloads.TrimmedBlock]: """ Fetch a block from storage. Args: snapshot: the snapshot to grab the data from. index_or_hash: the height or block hash of the block we wish to retrieve Raises: ValueError: if the height is invalid (negative or too large) ValueError: if the index_or_hash field could not be converted to a valid height or hash. """ if len(index_or_hash) < types.UInt256._BYTE_LEN: height = vm.BigInteger(index_or_hash) if height < 0 or height > 4294967295: # uint.MaxValue raise ValueError("Invalid height") block = snapshot.blocks.try_get_by_height(height, read_only=True) elif len(index_or_hash) == types.UInt256._BYTE_LEN: block_hash = types.UInt256(index_or_hash) block = snapshot.blocks.try_get(block_hash, read_only=True) else: raise ValueError("Invalid data") if block and not self._is_traceable_block(snapshot, block.index): block = None return block.trim()
def _build(leaves: List[_MerkleTreeNode]) -> _MerkleTreeNode: if len(leaves) == 0: raise ValueError('Leaves must have length') if len(leaves) == 1: return leaves[0] num_parents = int((len(leaves) + 1) / 2) parents = [_MerkleTreeNode() for i in range(0, num_parents)] for i in range(0, num_parents): node = parents[i] node.left_child = leaves[i * 2] leaves[i * 2].parent = node if (i * 2 + 1 == len(leaves)): node.right_child = node.left_child else: node.right_child = leaves[i * 2 + 1] leaves[i * 2 + 1].parent = node data = node.left_child.hash.to_array( ) + node.right_child.hash.to_array() hashed_data = hashlib.sha256( hashlib.sha256(data).digest()).digest() node.hash = types.UInt256(data=hashed_data) return MerkleTree._build(parents)
def test_to_hash_array(self): hash1 = types.UInt256(data=binascii.unhexlify(b'aa' * 32)) hash2 = types.UInt256(data=binascii.unhexlify(b'bb' * 32)) hash3 = types.UInt256(data=binascii.unhexlify(b'cc' * 32)) hash4 = types.UInt256(data=binascii.unhexlify(b'dd' * 32)) hash5 = types.UInt256(data=binascii.unhexlify(b'ee' * 32)) hashes = [hash1, hash2, hash3, hash4, hash5] m = crypto.MerkleTree(hashes) hash_array = m.to_hash_array() # sort the array hash_array = sorted(hash_array) for i, h in enumerate(hashes): self.assertEqual(h, hash_array[i])
def _convert(self, stack_item: vm.StackItem, class_type: Type[object]) -> object: """ convert VM type to native """ if class_type in [vm.StackItem, vm.PointerStackItem, vm.ArrayStackItem, vm.InteropStackItem]: return stack_item elif class_type in [int, vm.BigInteger]: return stack_item.to_biginteger() # mypy bug? https://github.com/python/mypy/issues/9756 elif class_type in [bytes, bytearray]: # type: ignore return stack_item.to_array() elif class_type == bool: return stack_item.to_boolean() elif class_type == types.UInt160: return types.UInt160(data=stack_item.to_array()) elif class_type == types.UInt256: return types.UInt256(data=stack_item.to_array()) elif class_type == str: return stack_item.to_array().decode() elif class_type == cryptography.ECPoint: return cryptography.ECPoint.deserialize_from_bytes(stack_item.to_array()) elif issubclass(class_type, enum.Enum): stack_item = cast(vm.IntegerStackItem, stack_item) # mypy seems to have trouble understanding types that support __int__ return class_type(int(stack_item)) # type: ignore else: raise ValueError(f"Unknown class type, don't know how to convert: {class_type}")
def hash(self) -> types.UInt256: """ Get a unique block identifier based on the unsigned data portion of the object. """ with serialization.BinaryWriter() as bw: self.serialize_unsigned(bw) data_to_hash = bytearray(bw._stream.getvalue()) data = hashlib.sha256(data_to_hash).digest() return types.UInt256(data=data)
def deserialize_unsigned(self, reader: serialization.BinaryReader) -> None: """ Deserialize the unsigned data part of the object from a binary stream. Args: reader: instance. Raises: ValueError: if the primary_index field is greater than the configured consensus validator count. """ (self.version, prev_hash, merkleroot, self.timestamp, self.index, self.primary_index, consensus) = struct.unpack("<I32s32sQIB20s", reader._stream.read(101)) if self.primary_index >= len(settings.standby_validators): raise ValueError(f"Deserialization error - primary index {self.primary_index} exceeds validator count " f"{len(settings.standby_validators)}") self.prev_hash = types.UInt256(prev_hash) self.merkle_root = types.UInt256(merkleroot) self.next_consensus = types.UInt160(consensus)
def get_block(self, snapshot: storage.Snapshot, index_or_hash: bytes) -> Optional[payloads.TrimmedBlock]: if len(index_or_hash) < types.UInt256._BYTE_LEN: height = vm.BigInteger(index_or_hash) if height < 0 or height > 4294967295: # uint.MaxValue raise ValueError("Invalid height") block = snapshot.blocks.try_get_by_height(height, read_only=True) elif len(index_or_hash) == types.UInt256._BYTE_LEN: block_hash = types.UInt256(index_or_hash) block = snapshot.blocks.try_get(block_hash, read_only=True) else: raise ValueError("Invalid data") if block and not self._is_traceable_block(snapshot, block.index): block = None return block.trim()
def _try_get_block(engine: contracts.ApplicationEngine, data: bytes) -> Optional[payloads.Block]: if len(data) < types.UInt256._BYTE_LEN: height = vm.BigInteger(data) if height < 0 or height > 4294967295: # uint.MaxValue raise ValueError("Invalid height") block = engine.snapshot.blocks.try_get_by_height(height) elif len(data) == types.UInt256._BYTE_LEN: block_hash = types.UInt256(data) block = engine.snapshot.blocks.try_get(block_hash) else: raise ValueError("Invalid data") if block and not _is_traceable_block(engine.snapshot, block.index): block = None # pragma: no cover (unreachable) return block
def get_tx_from_block(self, snapshot: storage.Snapshot, block_index_or_hash: bytes, tx_index: int) -> Optional[payloads.Transaction]: if len(block_index_or_hash) < types.UInt256._BYTE_LEN: height = vm.BigInteger(block_index_or_hash) if height < 0 or height > 4294967295: # uint.MaxValue raise ValueError("Invalid height") block = snapshot.blocks.try_get_by_height(height, read_only=True) elif len(block_index_or_hash) == types.UInt256._BYTE_LEN: block_hash = types.UInt256(block_index_or_hash) block = snapshot.blocks.try_get(block_hash, read_only=True) else: raise ValueError("Invalid data") if block and not self._is_traceable_block(snapshot, block.index): block = None if tx_index < 0 or tx_index >= len(block.transactions): raise ValueError("Transaction index out of range") return block.transactions[tx_index]
def hash(self) -> types.UInt256: data = hashlib.sha256(hashlib.sha256( self.to_array()).digest()).digest() return types.UInt256(data=data)
def test_compute_root_single_hash(self): data = binascii.unhexlify(b'aa' * 32) hash1 = types.UInt256(data=data) root = crypto.MerkleTree.compute_root([hash1]) self.assertEqual(data, root.to_array())