Marketplace MVP


#1

Repo: https://github.com/mesg-foundation/marketplace

Developers

  • SID should be chosen by developer or should be generated by the marketplace smart contract?
    • SID could be purely “random”
    • SID could be a string chosen by the developer
    • SID could be dev’s address + string chosen by developer (eg: “0x0343434/bitcoin”)
  • can post a service
  • can update their services
    • same SID mean same service
    • versioning based on hash

Users

  • can list existing services
  • can request an access to a service
    1. either free, just make a transaction
    2. fix fees, one time payment for lifetime. From user to the developer using MESG Token
    3. fix fees for a specific duration (eg, 1 month, 3 month, 6 month, 12 month). From user to the developer using MESG Token

Service

  • Transfer of ownership

Core

  • Need to check if user has access to service on deploy and start

#3

Sid

I like the final solution where there is a prefix but if the ownership changes than the sid is not working anymore so i would suggest responsibility of the developer to chose the sid. Only the developer of the initial service can update a version of this sid.

User

Thing to consider, having an access for free for a period of time and then need to pay this provide a free to test option that is really good for adoption.

All the rest is good, I also started to work on a service to connect this PoC on the same repo


#4

Schema from the meeting:

IMG_0908


#5

Here is the class diagram of the marketplace
https://www.lucidchart.com/invitations/accept/1fe6145a-599b-481a-9ba0-07a7b00a9374


#6

Question from @krhubert on Discord:

So about marketplace and security my first recomendation will be usage of maps… I know I’m saying this again, but by looking at current contract you just invers the order

  • store everything in array and use mapping to keep indexes

Instead of

  • store everything in maps and use array to keep item list

Why am I saying this?
Again more readable code is better testable and could be more secure (because it easy to understand and read)

Here https://github.com/mesg-foundation/marketplace/tree/refactor/mapping/contracts
you can compare two version of marketplace

And about security I don’t see if we have one because it’s too much playing with all indexes etc…

Your version v2 is really easier to read :thumbup:. The access to data is way more straightforward than having to use the map + array!

This check exist to prevent the “opcode” error or in english an “index out of bounds” error.
By using map instead of array, those function don’t need to exist anymore.

Yes it’s nicer again with map.
But I don’t like this variable as a state variable:

mapping(bytes32 => mapping(address => uint)) private expires;

I would prefer to put it in the Service struct.

  • why to add complexity and expose purchases getters like count?

Count getter are required in order to return the number of element of an array. Array.length don’t have an automatic getter. (can you confirm?)
Data getter are required in order to access data in map/array inside a struct. Automatic getter are generated only for state public variable.

we have

  struct Purchase {
    address purchaser;
    uint expirationDate;
  }

    Purchase[] purchases;

 mapping(address => mapping(bytes32 => uint)) private purchaserToSidToPurchase;

And it could be easily replaced with only one mapping:

    mapping(bytes32 => mapping(address => uint)) expires;

And that’s it. Array, struct and mapping vs one mapping.

I would prefer to have it inside Service struct, so the variable itself is just a map (and not a double map), but other I agree about using map instead of array.

there is no validation of metadata (it could be empty) - we could call http get but it’s too dificult for now, so at least we can check if it’s not empty (or is it url by using regxp https://github.com/gnidan/solregex)

Good catch. I think we can just do a keccak of it and test it again a keccak of an empty string.
I don’t want to set a regex to limit for example only http based URL…

I think offers should be per version (not global) - each version of software will have more functions and it should be paid diffrently and by creating one offer to all versions either old version users will pay diffrent or you can’t create new service with higher price

We discuss with Anthony and the having offers per version is a good way to provide more flexibility but also creates a lot more complexity when a dev want to add a new version.
What happen when a new version is created? Should the developer recreate every offer? Should the previous offers be “copied” to the new version?
I feel like it’s also preventing users to upgrade the version they use if they have to pay again.
And if the service update so much that it needs new offers, there is 2 ways:

  • disable previous offers and creates new ones
  • create a new service (with new offers). set a new sid (like a new major release)
    We could do it in a later version of this marketplace.

there is no option to destroy service (or version) or just make it deprecated

yes there is not. Should we add one?


#7

What I mean here is why to create a getter for a count at first place. Why this variable is public, why not private?

But what is the reason to put it inside service struct?

Yep at least empty (metadata.length == 0), regex could be used for any url

Complexity is none because a developer has to create the first offer for each service, so it will only require to do the same step then previous. And by keeping offers like now, the limitation of offers seems huge.

Offer should be created for every version (not copied automaticly or maybe add flag to copy from previous?) it could be easy solution.

If the user want to upgrade the version they have to pay anyway to store version and metadata

why you want to disable previous version it will be up to user if he want to disable it or not

I don’t like this idea, because someone will have to create sid which will be called for example, redis-1.0.0, redis-1.0.1 etc…

I think we should have some mechanism to give an authors and users possibility to stop using service (fro example some service has vulnerability and you want to prevent using it if possible. It could be by delete or deprecate (could be both) but one of it is fine.


#8
Service[] public services;

services.length is not accessible from outside the smart contract. The auto-generated getter is for accessible data in the services variable (function service(int index) Service)

Because this data is linked to the Service.
Same as Offer and Version.

When I used User, I was speaking about User of the service and marketplace. Not the developer.

Yes exactly. I was just showing the different possibilities the developer could choose:


The current system of offer is pretty flexible already but doesn’t allow offer for specific version.

Offer can be created and deactivated.
If a developer want to change its offer, he can create new ones and deactive previous ones.


#9

:+1:

Ok I see. So it’s another option to consider with offers creation.


#10

I don’t remember seeing any post about the format to access services on the marketplace.

We had a discussion with @Nicolas about having an URI to represent that but not really clear about the format. I saw @ilgooz started to implement it with

mesg:marketplace:service:ethwallet:0x6cf549f3f8daa71512d4526aefad05a484f52e161370c38ba68d5a0a61980a6e

I feel it doesn’t look really nice, I would love to have something that looks more like an URL

mesg://marketplace.service/ethwallet@0x6cf549f3f8daa71512d4526aefad05a484f52e161370c38ba68d5a0a61980a6e

With

[protocol]://[marketplace_type]/[ID]@[version]

Where marketplace_type could be marketplace.service but also marketplace.workflow or even 0x13b13uh13iuh31413oh1ffe for a specific marketplace smart contract. The @ is quite common to identify versions of a resource.

Let me know what you think and if there is existing topic where this has been discussed.


#11

I had discussion with @Nicolas and we choose mesg:marketplace:<sid>:<hash> (so there is no service part)

we also thought about:

  • mesg:marketplace:<sid>:<hash>
  • mesg:marketplace?sid=&hash=
  • mesg://marketplace?sid or any other format of arguments

So the etherum support the 2nd format.

So the thing with 3rd fomrat is mesg://0x0...1/ is not valid url, you could write mesg://0x.0...1.marketplace/. So first we decided to be more close to what ethereum has (but go with 1st format and we can pass different parameter later). Also we wanted to add support for mesg:// format later but maybe we will need this protocol syntax for diffrent things in the future.


#12

@Anthony thanks for reminder to specifying service (so in the future it can also be workflow) :wink:
I remember we include service in the URI but I don’t why we forgot to implement it…

After discussion with @Anthony, we are going to use an URI compatible with URL specification because it will be easier to parse, to interact with existing system, and also easier for users to understand it’s a link to a resource (https://en.wikipedia.org/wiki/Uniform_Resource_Identifier).

URI = mesg:[//authority]path[?query][#fragment]
authority = [userinfo@]host[:port]
host = marketplace (for now)
path = /<service | workflow>/<sid | hash>
query = nothing for now
fragment = nothing for now

Example:

  • With hash:
    mesg://marketplace/service/0x37cc575722a83a40fbeb126d2284c038a5f95260566a416bd412d21c00cb9f76

  • With sid:
    mesg://marketplace/service/ethwallet

  • With a workflow:
    mesg://marketplace/workflow/0x37cc575722a83a40fbeb126d2284c038a5f95260566a416bd412d21c00cb9f76

  • With another host:
    mesg://private-marketplace.company.com/service/ethwallet

  • With user info:
    mesg://nicolas:password@private-marketplace.company.com/service/ethwallet


Only the service with hash version will be implemented for now :wink:
mesg://marketplace/service/0x37cc575722a83a40fbeb126d2284c038a5f95260566a416bd412d21c00cb9f76