class TransferTicket(TicketModelBase):
    methods = {
        "public_key": PubkeyField(),
        "recipient": PubkeyField(),
        "imagedata_hash": SHA3512Field(),
        "copies": IntegerField(minsize=0, maxsize=1000),
    }

    def validate(self, chainwrapper, artregistry):
        # make sure artwork is properly registered
        artregistry.get_ticket_for_artwork(self.imagedata_hash)
class Signature(TicketModelBase):
    methods = {
        "signature": SignatureField(),
        "pubkey": PubkeyField(),
    }

    def validate(self, ticket):
        if not pastel_id_verify_signature_with_public_key_func(ticket.serialize(), self.signature, self.pubkey):
            raise ValueError("Invalid signature")
class ActivationTicket(TicketModelBase):
    methods = {
        # mandatory fields for Final Ticket
        "author": PubkeyField(),
        "order_block_txid": TXIDField(),

        "registration_ticket_txid": TXIDField(),
    }

    def validate(self, chainwrapper, image):
        # TODO:
        # X validate that this ticket references a valid regticket
        #   X regticket is on chain
        #   X regticket is not yet activated
        #   X regticket signatures are valid
        # X validate metadata:
        #   X fingerprints matches image
        #   X lubyhashes matches image
        #   X thumbnailhash matches image
        # X validate image
        #   X image actually hashes to imagedata_hash in the regticket
        #   X image is sfw
        #   X luby chunks generate the image

        # TODO: check that final_regticket ticket is not activated yet

        # validate image
        image.validate()

        # get registration ticket
        final_regticket = chainwrapper.retrieve_ticket(self.registration_ticket_txid)

        # validate final ticket
        final_regticket.validate(chainwrapper)

        # validate registration ticket
        regticket = final_regticket.ticket

        # validate that the authors match
        require_true(regticket.author == self.author)

        # validate that imagehash, fingerprints, lubyhashes and thumbnailhash indeed belong to the image
        require_true(regticket.fingerprints == image.generate_fingerprints())  # TODO: is this deterministic?
        require_true(regticket.lubyhashes == image.get_luby_hashes())
        require_true(regticket.lubyseeds == image.get_luby_seeds())
        require_true(regticket.thumbnailhash == image.get_thumbnail_hash())

        # validate that MN order matches between registration ticket and activation ticket
        require_true(regticket.order_block_txid == self.order_block_txid)

        # image hash matches regticket hash
        require_true(regticket.imagedata_hash == image.get_artwork_hash())

        # run nsfw check
        if NSFWDetector.is_nsfw(image.image):
            raise ValueError("Image is NSFW, score: %s" % NSFWDetector.get_score(image.image))
class IDTicket(TicketModelBase):
    methods = {
        # mandatory fields for Final Ticket
        "blockchain_address": BlockChainAddressField(),
        "public_key": PubkeyField(),
        "ticket_submission_time": UnixTimeField(),
    }

    def validate(self):
        # TODO: finish
        # Validate:
        #  o x time hasn't passed
        #  o x blocks hasn't passed
        #  o blockchain address is legit
        pass
Example #5
0
class TestPubkeyField(unittest.TestCase):
    def setUp(self):
        self.v = PubkeyField()

    def test_type(self):
        with self.assertRaises(TypeError):
            self.v.validate('test')

    def test_min(self):
        with self.assertRaises(ValueError):
            self.v.validate(b'W' * 65)

    def test_max(self):
        with self.assertRaises(ValueError):
            self.v.validate(b'Z' * 67)

    def test_valid(self):
        self.v.validate(b'Y' * 66)
class TradeTicket(TicketModelBase):
    methods = {
        "public_key": PubkeyField(),
        "imagedata_hash": SHA3512Field(),
        "type": StringChoiceField(choices=["ask", "bid"]),
        "copies": IntegerField(minsize=0, maxsize=1000),
        "price": IntegerField(minsize=0, maxsize=2 ** 32 - 1),
        "watched_address": BlockChainAddressField(),
        "collateral_txid": TXIDField(),
        "expiration": IntegerField(minsize=0, maxsize=1000),  # x==0 means never expire, x > 0 mean X blocks
    }

    def validate(self, blockchain, chainwrapper, artregistry):
        # make sure artwork is properly registered
        artregistry.get_ticket_for_artwork(self.imagedata_hash)

        # if this is a bid, validate collateral
        if self.type == "bid":
            # does the collateral utxo exist?
            transaction = blockchain.getrawtransaction(self.collateral_txid, 1)

            # is the utxo a new one we are not currently watching? - this is to prevent a user reusing a collateral
            _, listen_utxos = artregistry.get_listen_addresses_and_utxos()
            require_true(self.collateral_txid not in listen_utxos)

            # validate collateral
            valid = False
            for vout in transaction["vout"]:
                if len(vout["scriptPubKey"]["addresses"]) > 1:
                    continue

                # validate address and amount of collateral
                value = vout["value"]
                address = vout["scriptPubKey"]["addresses"][0]
                if address == self.watched_address and value == self.copies * self.price:
                    valid = True
                    break

            if not valid:
                raise ValueError("UTXO does not contain valid address as vout")
class RegistrationTicket(TicketModelBase):
    methods = {
        # mandatory fields for Final Ticket
        "author": PubkeyField(),
        # "author_wallet": BlockChainAddressField(),
        "order_block_txid": TXIDField(),
        "blocknum": IntegerField(minsize=0, maxsize=9999999999999),
        "imagedata_hash": SHA3512Field(),

        "artist_name": StringField(minsize=0, maxsize=120),
        "artist_website": StringField(minsize=0, maxsize=120),
        "artist_written_statement": StringField(minsize=0, maxsize=120),
        "artwork_title": StringField(minsize=0, maxsize=120),
        "artwork_series_name": StringField(minsize=0, maxsize=120),
        "artwork_creation_video_youtube_url": StringField(minsize=0, maxsize=120),
        "artwork_keyword_set": StringField(minsize=0, maxsize=120),
        "total_copies": IntegerField(minsize=0, maxsize=120),

        "fingerprints": FingerprintField(),
        "lubyhashes": LubyChunkHashField(),
        "lubyseeds": LubySeedField(),
        "thumbnailhash": SHA3512Field(),
    }

    def validate(self, chainwrapper):
        # we have no way to check these but will do so on activation:
        #  o fingerprints
        #  o lubyhashes
        #  o thumbnailhash
        #
        # after these checks are done we know that fingerprints are not dupes and there is no race

        # validate that lubyhashes and lubychunks are the same length
        require_true(len(self.lubyhashes) == len(self.lubyseeds))

        # validate that order txid is not too old
        block_distance = chainwrapper.get_block_distance(chainwrapper.get_last_block_hash(), self.order_block_txid)
        if block_distance > NetWorkSettings.MAX_REGISTRATION_BLOCK_DISTANCE:
            raise ValueError("Block distance between order_block_height and current block is too large!")
        # validate that art hash doesn't exist:
        # TODO: move this artwork index logic into chainwrapper
        fingerprint_db = {}
        if NetWorkSettings.LONG_REGTICKET_VALIDATION_ENABLED:
            for txid, ticket in chainwrapper.all_ticket_iterator():
                if type(ticket) == FinalRegistrationTicket:
                    ticket.validate(chainwrapper)
                else:
                    continue

                regticket = ticket.ticket

                # collect fingerprints
                # TODO: only collect this for activated regtickets and tickets not older than X blocks
                fingerprint_db[regticket.imagedata_hash] = ("DUMMY_PATH", regticket.fingerprints)  # TODO: do we need this?

                # validate that this art hash does not yet exist on the blockchain
                # TODO: only prohibit registration when this was registered in the past X blocks
                # TODO: if regticket is activated: prohibit registration forever
                require_true(regticket.imagedata_hash != self.imagedata_hash)

        # validate that fingerprints are not dupes
        if len(fingerprint_db) > 0:
            # TODO: check for fingerprint dupes
            if NetWorkSettings.DUPE_DETECTION_ENABLED:
                pandas_table = assemble_fingerprints_for_pandas([(k, v) for k, v in fingerprint_db.items()])
                is_duplicate, params_df = measure_similarity(self.fingerprints, pandas_table)
                if is_duplicate:
                    raise ValueError("Image failed fingerprint check!")
Example #8
0
 def setUp(self):
     self.v = PubkeyField()