def test_duplicate_name(self): # Create a mock store store = MarketPlaceGlobalStore() # Because we have not "registered" any participants, the name # should not be a duplicate update = participant_update.UpdateName( update_type=participant_update.UpdateName.UpdateType, object_id='0000000000000000', creator_id='0000000000000000', name='participant') self.assertTrue(update.is_valid_name(store)) # Put a participant in the store participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because the participant name is in the store, trying to update the # name a valid name as it is a duplicate update = participant_update.UpdateName( update_type=participant_update.UpdateName.UpdateType, object_id=participant.ObjectID, creator_id=participant.ObjectID, name='participant') self.assertFalse(update.is_valid_name(store))
def test_duplicate_name(self): # Create a mock store and put a participant in it participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because we have not "registered" any asset types, the name # should not be a duplicate update = asset_type_update.Register( update_type=asset_type_update.Register.UpdateType, creator_id=participant.ObjectID, name='/assettype' ) self.assertTrue(market_place_object_update.global_is_valid_name( store, name='/assettype', object_type=update.ObjectType, creator_id=participant.ObjectID)) # Add an asset type to the store with the creator being the # participant we inserted initially asset_type = asset_type_update.AssetTypeObject( objectid='0000000000000001', minfo={ 'name': '//participant/assettype', 'creator': participant.ObjectID }) store[asset_type.ObjectID] = asset_type.dump() # Because the asset type name is in the store, trying to register # using a relative name based upon creator and a fully-qualified name # should not be a valid name as it is a duplicate asset_type = asset_type_update.Register( update_type=asset_type_update.Register.UpdateType, creator_id=participant.ObjectID, name='/assettype' ) self.assertFalse(market_place_object_update.global_is_valid_name( store, name='/assettype', object_type=asset_type.ObjectType, creator_id=participant.ObjectID)) update = asset_type_update.Register( update_type=asset_type_update.Register.UpdateType, creator_id=participant.ObjectID, name='//participant/assettype' ) self.assertFalse(market_place_object_update.global_is_valid_name( store, name='//participant/assettype', object_type=update.ObjectType, creator_id=participant.ObjectID))
def test_duplicate_name(self): # Create a mock store and put a participant in it participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because we have not "registered" any sell offers, the name # should not be a duplicate update = sell_offer_update.Register( input_id='**FAKE**', output_id='**FAKE**', update_type=sell_offer_update.Register.UpdateType, creator_id=participant.ObjectID, name='/selloffer') self.assertTrue( market_place_object_update.global_is_valid_name( store, '/selloffer', sell_offer_update.Register.ObjectType, participant.ObjectID)) # Add a sell offer to the store with the creator being the participant # we inserted initially sell_offer = sell_offer_update.SellOfferObject( objectid='0000000000000001', minfo={ 'name': '//participant/selloffer', 'creator': participant.ObjectID }) store[sell_offer.ObjectID] = sell_offer.dump() # Because the sell offer name is in the store, trying to register # using a relative name based upon creator and a fully-qualified name # should not be a valid name as it is a duplicate update = sell_offer_update.Register( input_id='**FAKE**', output_id='**FAKE**', update_type=sell_offer_update.Register, creator_id=participant.ObjectID, name='/selloffer') self.assertFalse( market_place_object_update.global_is_valid_name( store, '/selloffer', sell_offer_update.Register.ObjectType, participant.ObjectID)) update = sell_offer_update.Register( update_type=sell_offer_update.Register.UpdateType, input_id='**FAKE**', output_id='**FAKE**', creator_id=participant.ObjectID, name='//participant/selloffer') self.assertFalse( market_place_object_update.global_is_valid_name( store, '//participant/selloffer', sell_offer_update.Register.ObjectType, participant.ObjectID))
def test_duplicate_name(self): # Create a mock store and put a participant in it participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because we have not "registered" any liabilities, the name # should not be a duplicate update = liability_update.Register( update_type=liability_update.Register.UpdateType, creator_id=participant.ObjectID, name='/liability' ) self.assertTrue(global_is_valid_name( store, '/liability', liability_update.Register.ObjectType, participant.ObjectID)) # Add a liability to the store with the creator being the participant # we inserted initially liability = liability_update.LiabilityObject( objectid='0000000000000001', minfo={ 'name': '//participant/liability', 'creator': participant.ObjectID }) store[liability.ObjectID] = liability.dump() # Because the liability name is in the store, trying to register using # a relative name based upon creator and a fully-qualified name should # not be a valid name as it is a duplicate update = liability_update.Register( update_type=liability_update.Register.UpdateType, creator_id=participant.ObjectID, name='/liability' ) self.assertFalse(global_is_valid_name( store, '/liability', liability_update.Register.ObjectType, participant.ObjectID)) update = liability_update.Register( update_type=liability_update.Register.UpdateType, creator_id=participant.ObjectID, name='//participant/liability' ) self.assertFalse(global_is_valid_name( store, '//participant/liability', liability_update.Register.ObjectType, participant.ObjectID))
def fetch(self, store='MarketPlaceTransaction'): """ Retrieve the current state from the validator. Rebuild the name, type, and id maps for the resulting objects. :param str store: optional, the name of the marketplace store to retrieve """ logger.debug('fetch state from %s/%s/*', self.BaseURL, store) blockids = self.getmsg('/block?blockcount=10') blockid = blockids[0] if blockid == self.CurrentBlockID: return if self.CurrentBlockID in blockids: fetchlist = blockids[:blockids.index(self.CurrentBlockID)] for fetchid in reversed(fetchlist): logger.debug('only fetch delta of state for block %s', fetchid) delta = self.getmsg('/store/{0}/*?delta=1&blockid={1}'.format( store, fetchid)) self._state = self._state.clone_store(delta) else: logger.debug('full fetch of state for block %s', blockid) state = self.getmsg("/store/{0}/*?blockid={1}".format( store, blockid)) self._state = \ MarketPlaceGlobalStore(prevstore=None, storeinfo={ 'Store': state, 'DeletedKeys': [] } ) # State is actually a clone of the block state, this is a free # operation because of the copy on write implementation of the global # store. This way market clients can update the state speculatively # without corrupting the synchronized storage self.State = self._state.clone_store() self.CurrentBlockID = blockid
def test_duplicate_name(self): # Create a mock store and put a participant in it participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because we have not "registered" any exchange offers, the name # should not be a duplicate update = exchange_offer_update.UpdateName( update_type=exchange_offer_update.UpdateName.UpdateType, object_id='0000000000000001', creator_id=participant.ObjectID, name='/exchangeoffer' ) self.assertTrue(update.is_valid_name(store)) # Add an exchange offer to the store with the creator being the # participant we inserted initially exchange_offer = exchange_offer_update.ExchangeOfferObject( objectid='0000000000000001', minfo={ 'name': '//participant/exchangeoffer', 'creator': participant.ObjectID }) store[exchange_offer.ObjectID] = exchange_offer.dump() # Because the exchange offer name is in the store, trying to update # the name using a relative name based upon creator and a fully- # qualified name should not be a valid name as it is a duplicate update = exchange_offer_update.UpdateName( update_type=exchange_offer_update.UpdateName.UpdateType, object_id=exchange_offer.ObjectID, creator_id=participant.ObjectID, name='/exchangeoffer' ) self.assertFalse(update.is_valid_name(store)) update = exchange_offer_update.UpdateName( update_type=exchange_offer_update.UpdateName.UpdateType, object_id=exchange_offer.ObjectID, creator_id=participant.ObjectID, name='//participant/exchangeoffer' ) self.assertFalse(update.is_valid_name(store))
def test_duplicate_name(self): # Create a mock store and put a participant in it participant = participant_update.ParticipantObject( participantid='0000000000000000', minfo={ 'name': 'participant', }) store = MarketPlaceGlobalStore() store[participant.ObjectID] = participant.dump() # Because we have not "registered" any accounts, the name # should not be a duplicate update = account_update.UpdateName( update_type=account_update.UpdateName.UpdateType, object_id='0000000000000001', creator_id=participant.ObjectID, name='/account') self.assertTrue(update.is_valid_name(store)) # Add an account to the store with the creator being the participant # we inserted initially account = account_update.AccountObject(objectid='0000000000000001', minfo={ 'name': '//participant/account', 'creator': participant.ObjectID }) store[account.ObjectID] = account.dump() # Because the account name is in the store, trying to update the name # using a relative name based upon creator and a fully-qualified name # should not be a valid name as it is a duplicate update = account_update.UpdateName( update_type=account_update.UpdateName.UpdateType, object_id=account.ObjectID, creator_id=participant.ObjectID, name='/account') self.assertFalse(update.is_valid_name(store)) update = account_update.UpdateName( update_type=account_update.UpdateName.UpdateType, object_id=account.ObjectID, creator_id=participant.ObjectID, name='//participant/account') self.assertFalse(update.is_valid_name(store))
def fetch(self, store='MarketPlaceTransaction'): """ Retrieve the current state from the validator. Rebuild the name, type, and id maps for the resulting objects. :param str store: optional, the name of the marketplace store to retrieve """ logger.debug('fetch state from %s/%s/*', self.BaseURL, store) blockids = self.getmsg('/block?blockcount=10') blockid = blockids[0] if blockid == self.CurrentBlockID: return if self.CurrentBlockID in blockids: fetchlist = blockids[:blockids.index(self.CurrentBlockID)] for fetchid in reversed(fetchlist): logger.debug('only fetch delta of state for block %s', fetchid) delta = self.getmsg( '/store/{0}/*?delta=1&blockid={1}'.format(store, fetchid)) self._state = self._state.clone_store(delta) else: logger.debug('full fetch of state for block %s', blockid) state = self.getmsg( "/store/{0}/*?blockid={1}".format(store, blockid)) self._state = \ MarketPlaceGlobalStore(prevstore=None, storeinfo={ 'Store': state, 'DeletedKeys': [] } ) # State is actually a clone of the block state, this is a free # operation because of the copy on write implementation of the global # store. This way market clients can update the state speculatively # without corrupting the synchronized storage self.State = self._state.clone_store() self.CurrentBlockID = blockid
class MarketPlaceState(MarketPlaceCommunication): """ A class to wrap the current ledger state for a market place. Retrieves the state from a validator and builds maps for names and ids. Exports methods for querying state. :param url baseurl: the base URL for a Sawtooth Lake validator that supports an HTTP interface :param id creator: the identifier for the participant generating transactions :var dict State: The key/value store associated with the head of the ledger, for the MarketPlace store, keys are object identifiers and the values are the current state of each object. """ def __init__(self, baseurl, creator=None, creator_name=None): super(MarketPlaceState, self).__init__(baseurl) self._state = None self.State = None self.ScratchState = None self.CurrentBlockID = None self.fetch() self.CreatorID = creator if not self.CreatorID and creator_name: self.CreatorID = self.State.n2i('//' + creator_name, 'Participant') def n2i(self, name, obj_type): """ Convert a name into an identifier. The name can take one of these forms: @ -- resolves to the identifier for creator ///<IDENTIFIER> -- resolves to the identifier //<CREATOR>/<PATH> -- fully qualified name /<PATH> -- resolve relative to the current creator if specified :param str name: object name :return: object identifier :rtype: id """ if name == '@': return self.CreatorID if not name.startswith('//'): cname = self.CreatorID if not cname: return None name = '{0}{1}'.format(cname, name) else: def unpack_indeterminate(creator, *path): return creator, path creator, path = unpack_indeterminate(*name[2:].split('/')) creator_id = self.State.n2i("//" + creator, 'Participant') if path: name = '{}/{}'.format(creator_id, "/".join(path)) else: name = "//" + creator identifier = self.State.n2i(name, obj_type) if not identifier: logger.info('could not find identifier by name: %s', name) return identifier def i2n(self, objectid): """ Construct the fully qualified name for an object If the object does not have a name the format will be ///<IDENTIFIER> If the object is a participant the format will be //<NAME> Otherwise the format will be //<CREATOR NAME>/<NAME> :param id objectid: identifier for the object :return: the fully qualified name for the object :rtype: str """ return self.State.i2n(objectid) def fetch(self, store='MarketPlaceTransaction'): """ Retrieve the current state from the validator. Rebuild the name, type, and id maps for the resulting objects. :param str store: optional, the name of the marketplace store to retrieve """ logger.debug('fetch state from %s/%s/*', self.BaseURL, store) blockids = self.getmsg('/block?blockcount=10') blockid = blockids[0] if blockid == self.CurrentBlockID: return if self.CurrentBlockID in blockids: fetchlist = blockids[:blockids.index(self.CurrentBlockID)] for fetchid in reversed(fetchlist): logger.debug('only fetch delta of state for block %s', fetchid) delta = self.getmsg('/store/{0}/*?delta=1&blockid={1}'.format( store, fetchid)) self._state = self._state.clone_store(delta) else: logger.debug('full fetch of state for block %s', blockid) state = self.getmsg("/store/{0}/*?blockid={1}".format( store, blockid)) self._state = MarketPlaceGlobalStore(prevstore=None, storeinfo={ 'Store': state, 'DeletedKeys': [] }) # State is actually a clone of the block state, this is a free # operation because of the copy on write implementation of the global # store. This way market clients can update the state speculatively # without corrupting the synchronized storage self.State = self._state.clone_store() self.CurrentBlockID = blockid def path(self, path): """ Function to retrieve the value of a property of an object in the saved state Args: path -- '.' separate expression for extracting a value from state """ pathargs = path.split('.') value = self.State while pathargs: value = value.get(pathargs.pop(0)) return value def lambdafilter(self, *predicates): """ Apply a series of predicates to state objects. Predicates are lambda expressions on the data of an object. See the Filters class for tools for creating lambda expressions. :param predicates: a list of predicates used to filter the set of objects :type predicates: list of lambda functions :returns: a list of object identifiers :rtype: list of identifiers """ objlist = [] for objid, objinfo in self.State.iteritems(): match = True for predicate in predicates: if not predicate(objinfo): match = False break if match: objlist.append(objid) return objlist def list(self, objtype=None, creator=None, name=None, fields=None): """ Simple filter for common query operations on the current state """ result = [] for objid, objinfo in self.State.iteritems(): if objtype and objinfo.get('object-type') != objtype: continue if creator and objinfo.get('creator') != creator: continue if name and not objinfo.get('name').startswith(name): continue # provide a couple useful properties objinfo['id'] = objid objinfo['fqname'] = self.i2n(objid) if not objinfo['name']: objinfo['name'] = '/id/' + objid if not fields: result.append(objid) elif fields == '*': result.append(objinfo) else: result.append([objinfo.get(fld) for fld in fields]) return result
class MarketPlaceState(MarketPlaceCommunication): """ A class to wrap the current ledger state for a market place. Retrieves the state from a validator and builds maps for names and ids. Exports methods for querying state. :param url baseurl: the base URL for a Sawtooth Lake validator that supports an HTTP interface :param id creator: the identifier for the participant generating transactions :var dict State: The key/value store associated with the head of the ledger, for the MarketPlace store, keys are object identifiers and the values are the current state of each object. """ def __init__(self, baseurl, creator=None, creator_name=None): super(MarketPlaceState, self).__init__(baseurl) self._state = None self.State = None self.ScratchState = None self.CurrentBlockID = None self.fetch() self.CreatorID = creator if not self.CreatorID and creator_name: self.CreatorID = self.State.n2i('//' + creator_name) def bind(self, name, objectid): """ Add a binding between a fully qualified name and an objectid :param str name: fully qualified object name :param id objectid: object identifier """ return self.State.bind(name, objectid) def unbind(self, name): """ Drop the binding of a name to an identifier :param str name: object name """ return self.State.unbind(name) def n2i(self, name): """ Convert a name into an identifier. The name can take one of these forms: @ -- resolves to the identifier for creator ///<IDENTIFIER> -- resolves to the identifier //<CREATOR>/<PATH> -- fully qualified name /<PATH> -- resolve relative to the current creator if specified :param str name: object name :return: object identifier :rtype: id """ if name == '@': return self.CreatorID if not name.startswith('//'): cname = self.i2n(self.CreatorID) if not cname: return None name = '{0}{1}'.format(cname, name) return self.State.n2i(name) def i2n(self, objectid): """ Construct the fully qualified name for an object If the object does not have a name the format will be ///<IDENTIFIER> If the object is a participant the format will be //<NAME> Otherwise the format will be //<CREATOR NAME>/<NAME> :param id objectid: identifier for the object :return: the fully qualified name for the object :rtype: str """ return self.State.i2n(objectid) def fetch(self, store='MarketPlaceTransaction'): """ Retrieve the current state from the validator. Rebuild the name, type, and id maps for the resulting objects. :param str store: optional, the name of the marketplace store to retrieve """ logger.debug('fetch state from %s/%s/*', self.BaseURL, store) blockids = self.getmsg('/block?blockcount=10') blockid = blockids[0] if blockid == self.CurrentBlockID: return if self.CurrentBlockID in blockids: fetchlist = blockids[:blockids.index(self.CurrentBlockID)] for fetchid in reversed(fetchlist): logger.debug('only fetch delta of state for block %s', fetchid) delta = self.getmsg( '/store/{0}/*?delta=1&blockid={1}'.format(store, fetchid)) self._state = self._state.clone_store(delta) else: logger.debug('full fetch of state for block %s', blockid) state = self.getmsg( "/store/{0}/*?blockid={1}".format(store, blockid)) self._state = MarketPlaceGlobalStore(prevstore=None, storeinfo={'Store': state, 'DeletedKeys': []}) # State is actually a clone of the block state, this is a free # operation because of the copy on write implementation of the global # store. This way market clients can update the state speculatively # without corrupting the synchronized storage self.State = self._state.clone_store() self.CurrentBlockID = blockid def path(self, path): """ Function to retrieve the value of a property of an object in the saved state Args: path -- '.' separate expression for extracting a value from state """ pathargs = path.split('.') value = self.State while pathargs: value = value.get(pathargs.pop(0)) return value def lambdafilter(self, *predicates): """ Apply a series of predicates to state objects. Predicates are lambda expressions on the data of an object. See the Filters class for tools for creating lambda expressions. :param predicates: a list of predicates used to filter the set of objects :type predicates: list of lambda functions :returns: a list of object identifiers :rtype: list of identifiers """ objlist = [] for objid, objinfo in self.State.iteritems(): match = True for predicate in predicates: if not predicate(objinfo): match = False break if match: objlist.append(objid) return objlist def list(self, objtype=None, creator=None, name=None, fields=None): """ Simple filter for common query operations on the current state """ result = [] for objid, objinfo in self.State.iteritems(): if objtype and objinfo.get('object-type') != objtype: continue if creator and objinfo.get('creator') != creator: continue if name and not objinfo.get('name').startswith(name): continue # provide a couple useful properties objinfo['id'] = objid objinfo['fqname'] = self.i2n(objid) if not objinfo['name']: objinfo['name'] = '/id/' + objid if not fields: result.append(objid) elif fields == '*': result.append(objinfo) else: result.append([objinfo.get(fld) for fld in fields]) return result