def create(self, api: ContractsApiLike, owner: Entity, fee: int): # Set contract owner (required for resource prefix) self.owner = owner if self._init is None: raise RuntimeError("Contract has no initialisation function") # Generate resource addresses used by persistent globals try: resource_addresses = [ 'fetch.contract.state.{}'.format(self.digest.to_hex()) ] resource_addresses.extend( ShardMask.state_to_address(address, self) for address in self._parser.used_globals_to_addresses( self._init, [self._owner])) except (UnparsableAddress, UseWildcardShardMask): logging.warning( "Couldn't auto-detect used shards, using wildcard shard mask") shard_mask = BitVector() else: # Generate shard mask from resource addresses shard_mask = ShardMask.resources_to_shard_mask( resource_addresses, api.server.num_lanes()) return self._api(api).create(owner, self, fee, shard_mask=shard_mask)
def _build_shard_mask(self, num_lanes: int, name: Optional[str], arguments: List[Any]) -> BitVector: try: resource_addresses = [ 'fetch.contract.state.{}'.format(str(self.address)), ] # only process the init functions resources if this function is actually present if name is not None: variables = self._parser.used_globals_to_addresses( name, arguments) for variable in variables: resource_addresses.append( ShardMask.state_to_address(str(self.address), variable)) shard_mask = ShardMask.resources_to_shard_mask( resource_addresses, num_lanes) except (UnparsableAddress, UseWildcardShardMask, EtchParserError, AssertionError) as ex: logging.debug('Parser Error: {}'.format(ex)) logging.warning( "Couldn't auto-detect used shards, using wildcard shard mask") shard_mask = BitVector() return shard_mask
def test_resource_to_shard(self): # Test known addresses addresses = ['abc', 'def', 'XYZ'] shards = [2, 3, 1] for add, sh in zip(addresses, shards): self.assertEqual(ShardMask.resource_to_shard(add, 4), sh) # Repeat for different lane number, expect different results addresses = ['abc', 'def', 'XYZ'] shards = [10, 11, 13] for add, sh in zip(addresses, shards): self.assertEqual(ShardMask.resource_to_shard(add, 16), sh)
def test_state_to_address(self): contract = mock.Mock() contract.digest.to_hex.side_effect = ['abc'] contract.owner = 'def' address = ShardMask.state_to_address('xyz', contract) self.assertEqual(address, 'abc.def.state.xyz')
def test_resource_to_shard(self): # Test rejection of invalid lane number with self.assertRaises(AssertionError): ShardMask.resource_to_shard('abc', 3) # Test known addresses addresses = ['abc', 'def', 'XYZ'] shards = [2, 3, 1] for add, sh in zip(addresses, shards): self.assertEqual(ShardMask.resource_to_shard(add, 4), sh) # Repeat for different lane number, expect different results addresses = ['abc', 'def', 'XYZ'] shards = [10, 11, 13] for add, sh in zip(addresses, shards): self.assertEqual(ShardMask.resource_to_shard(add, 16), sh)
def action(self, api: ContractsApiLike, name: str, fee: int, signers: List[Entity], *args): if self._owner is None: raise RuntimeError( 'Contract has no owner, unable to perform any actions. Did you deploy it?' ) if name not in self._actions: raise RuntimeError( '{} is not an valid action name. Valid options are: {}'.format( name, ','.join(list(self._actions)))) try: # Generate resource addresses used by persistent globals resource_addresses = [ ShardMask.state_to_address(address, self) for address in self._parser.used_globals_to_addresses( name, list(args)) ] except (UnparsableAddress, UseWildcardShardMask): logging.warning( "Couldn't auto-detect used shards, using wildcard shard mask") shard_mask = BitVector() else: # Generate shard mask from resource addresses shard_mask = ShardMask.resources_to_shard_mask( resource_addresses, api.server.num_lanes()) return self._api(api).action(self._digest, self.address, name, fee, self.owner, signers, *args, shard_mask=shard_mask)
def test_resource_to_shard_mask(self): num_lanes = 4 bv = ShardMask.resources_to_shard_mask(['abc', 'def', 'XYZ'], num_lanes) self.assertEqual(bv._size, num_lanes) self.assertEqual(bv.as_binary(), '00001110')
def test_state_to_address(self): address = ShardMask.state_to_address('foo.bar', 'xyz') self.assertEqual(address, 'foo.bar.state.xyz')
def test_resource_to_shard_invalid_lane(self): # Test rejection of invalid lane number with self.assertRaises(AssertionError): ShardMask.resource_to_shard('abc', 3)