Open tahpot opened 2 years ago
I need to scope this during the sprint.
First draft of scope complete.
Handover call with @BlockchainCrazy95 complete.
registerService(... pricePerDayPerAccount: decimal, ...)
Need to check the usage of did(:string) value.
How can I realize if he is a Verida Infrastructure Operator or Verida Account?
- How can I determine the decimal of pricePerDayPerAccount?
pricePerDayPerAccount
will be in VDA tokens. The decimal points should match our ERC-20 token.
- Need to check the usage of did(:string) value
A did
is a decentralized identifier
. It's a unique reference for an account on the Verida network. You can think of it like a blockchain address, except it's an address on the Verida network.
- How can I realize if he is a Verida Infrastructure Operator or Verida Account?
I don't think you need to identify them.
I've given them separate names so the user stories make more sense.
@tahpot, when can config variables be changed?
Here's some questions for Service Registry contract.
Verida Infrastructure Operator
or Verida Account
deposit VDA tokens, Verida get fee from them? Or just staked total amount?Verida Infrastructure Operator
register service, proof is made from client side?Verida Infrastructure Operator
can also be Verida account
? Verida Account
add credit, there need to be vdaTokenAmount as a function parameter.Finally, I changed the estimation time from Monday to Wednesday. Is it okay?
- Can ServiceType be deleted later?
No.
- In registerService() function, I think, we need to keep both msg.sender as service register and did. Because another account(not the creator) can update service if he knows the did. So we need to keep and check the owner of service.
No, we don't want to link the msg.sender
and the did
. The purpose of the proof
attribute is to provide a signature that can be verified it was signed by the did
. Don't worry about implementing that signature check yet, we can do that at the end. I have another project that has code to make this easy.
- When the
Verida Infrastructure Operator
orVerida Account
deposit VDA tokens, Verida get fee from them? Or just staked total amount?
No fee.
- When the
Verida Infrastructure Operator
register service, proof is made from client side?
Yes.
Verida Infrastructure Operator
can also beVerida account
?
Verida Infrastructure Operator
is a type of Verida account
being used by an infrastructure provider.
- When
Verida Account
add credit, there need to be vdaTokenAmount as a function parameter.
Ok.
- What is the purpose of distribute() function?
This function is designed to pay the infrastructure operators and take a fee from the Verida account
holder who is using the infrastructure.
It's an automated way to pay for accessing server infrastructure.
If this function is needed, the function must be called externally.
Ok.
Finally, I changed the estimation time from Monday to Wednesday. Is it okay?
Okay.
I have some stuffs to discuss relating to the service update. Above image shows that the payment methods when we update the pricePerAccount. There's no problem in first update (day 3). The problem occurs in the second update (day 10) - after 7 days from first update. As first update delay duration (day 3 ~ day 33) hasn't finished yet, we should decide the second update duration and price. In this case, we can use above two methods (method1 or method2) for accounts' payment. Each method has their own pros and cons. Method1: It's more precise than method 2, but managing state variables might be complicated. Furthermore, if we change the priceChangeDelayDays here, managing state variables might be more and more complicated. Method2: Managing state variables is easier than method 1.
I need to confirm these problems to finish Service Registry contract completely.
We can't iterate all the registered did
s on the connected services. It can cause some problems, including gas fee.
For each
Verida Account Connection
credit VDA tokens to theVerida Infrastructure Operator
and debit tokens from theVerida Account
base on the servicepricePerDayPerAccount
.
We can implement this feature as defining claim function. Verida Infrastructure Operator
will get VDA tokens using claim function when we need.
If the
Verida Account
has insufficient credit the services they have registered are automatically de-registered. There is no guarantee to the order that services are de-registered.
We can make view function which check the account availability on service. We can call this view function externally when we need (every day or whenever we need)
function getAvailability(address identity, bytes32 serviceId, bytes signature) public view { ... };
There's no problem in first update (day 3). The problem occurs in the second update (day 10) - after 7 days from first update.
I suggest we only permit a Verida Infrastructure Operator
changing their price once per 30 days. This avoids them changing the price until a previous price change has completed.
We can implement this feature as defining claim function. Verida Infrastructure Operator will get VDA tokens using claim function when we need.
Ideally we can distribute at the end of the day. removeCredit()
is effectively a claim
function to pull out any unused tokens owned by a DID.
We can make view function which check the account availability on service. We can call this view function externally when we need (every day or whenever we need)
I agree. Let's allow a service availability check. Perhaps we call it checkServiceAvailability()
?
However, I think we are best to record the state of a service in the smart contract. ie: service.status = "active|disabled|pendingPayment"
.
If we change priceChangeDelayDays, how can we apply the payment method?
priceChangeDelayDays
can be changed by the contract owner.
When a price change occurs, the date the price change takes effect is calculated based on the current value of priceChangeDelayDays
.
No further price changes can occur until that date has elapsed.
priceChangeDelayDays
may change, however the new value won't take effect until the previous price change has been completed.
proof: string
This will be a common requirement across all our smart contracts.
Don't worry about implementing this yet. We can implement at the end.
In distribute() function, we have to iterate all the DIDs and serviceIds to check their status. It will consume much gas fees.
But as I suggested yesterday, if we use claim function, we can reduce the gas fees. Actually, Verida Infrastructure Operator
DID will be sent to claim function as a parameter. So claim function will iterate only services that specific DID is connected to.
And we can ask Verida Infrastructure Operator
s to claim every day.
I suggest that accounts have to bond VDA tokens to the contract when they connect to the service, not at every interval.
Verida Account
s should bond when they connect to the service(for 30 days), but they only lose their tokens when the infrastructure operator claims tokens.
Verida Infrastructure Operator
and Verida Account
have to bond tokens before they're going to register or connect to the service.
Verida Infrastructure Operator
can claim tokens once per week (at a fixed day/time ie: Thursday 9am UTC).
Verida Infrastructure Operator
must claim tokens within 14 days or they lose the right to those tokens.
The smart contract will reject the request if, after removing the tokens, the
did
will have insufficient credit to meet 30 days (minimumDaysCreditPerService
) for all connected services.
I mean, we don't need to check this constraint in removeCredit()
function. Because all the tokens will be bonded to the contract when accounts are going to connect service.
Basically completed the Service Registry smart contract and unit test.
Remaining:
Verida Infrastructure Operator
decrease the maxAccounts.
When Verida Infrastructure Operator
change the pricePerDayPerAccount.I think, mainly tested all about its features, but please have a look and give me your feedback.
@tahpot, can you give me some examples of Service Registry updating process? I want examples with detailed numbers in case of above comments.
@BlockchainCrazy95
I have added a new method to implement (discoverServices()
).
@tahpot, can you give me some examples of Service Registry updating process?
Let's discuss this on our call. I'm not sure exactly what you mean.
A diamond is a contract with external functions that are supplied by contracts called facets. Facets are separate, independent contracts that can share internal functions, libraries and state variables.
Diamond standard contracts will include facets. Facets can be defined by external functions or internal functions and state variables.
Diamond standard contracts will include as follows: DiamondCut
, DiamondLoupe
, DiamondStorage
, Facet
contracts...
In this service registry contract, facets can be ServiceMngFacet which Verida Infrastructure Operator
can operate to and ConnectionFacet which Verida Account
will do something with the registered services.
cc @ITStar10
First, once user has not enough credits, is he disconnected from all connected services automatically? So, he needs to connet services again later after he add credit?
Otherwise, if user isn't disconnected but not able to use service till he add credit, does he deposit his credit to the service as done in connecting service? When a user connect a service, he should pay the amount of "price * minimumDays". Do we need 'chargeCredit' function that deposit user's credit into service? If so, how much need to be deposited? Same amount as 'connectService'? In this case, is user responsible to pay credits for non-used days?
Second, we need to separate updateService() function for maxAccount & pricePerDayPerAccount. Because, operators can update maxAccount anytime. But there is delay for pricePerDayPerAccount.
Third, we need to update checkServiceAvailability() function. This function is not implemented correctly. And it will depends on the answer of first question above.
Let's say an operator updated the price. And here priceChangeDelayDays is 30. So updated price will be applied after 30 days. After 10days, owner of our contract updated "priceChangeDelayDays" into 20.
When will be the updated price applied? After 10days from when owner updated "priceChangeDelayDays", because that days is after 20 days(t3) from when operator updated the price? Or will the updated price be applied after 30 days (t4) from when operator updated?
I think second one shoud be better. Because service operators updates their price considering the priceChangeDelayDays on their side.
Now, the service operator can only update price once per each duration of "priceChangeDelayDays".
Owner (we - VeridaCompany) can update the price of vdaPerAccount. Let's say original vdaPerAccount of a service was 30 at registering time. And operator registered a service with value of 100 for 'maxAccount'. So operator deposited 30*100 credits at register. After operator registered a service, owner changed the vdaPerAccount for this service type into 60.
Here, if the operator is trying to decrease 'maxAccounts' into 50, how much do we send back credits to operator? *Amount by original price : 30 (100 - 50)?** or *Amount by new price: 60 (100 - 50) ?** Of course, the first one, right?
Let's say operator is going to increase 'maxAccount' into 200. How much does opeartor deposit credit more? *By original price: 30 (200 - 100) ?** or *By new price: 60 (200 - 100)?**
Time | Register | vdaPerAccount Update - 1 | maxAccount Update - 1 | vdaPerAccount Update - 2 | maxAccount Update - 2 | vdaPerAccount Update - 3 | maxAccount Update - 3 |
---|---|---|---|---|---|---|---|
Done by | Operator | Owner | Operator | Owner | Operator | Owner | Operator |
vdaPerAccount | 10 | 30 | 20 | 15 | |||
maxAccount | 40 | 70 | 50 | 100 | |||
Necessary credits | 400 | ? | ? | ? | ? | ? | ? |
Do we need to check out all the services on each update of 'vdaPerToken'? When the owner updates the vdaPerAccount from 10 to 30, we can't request all the existing services to update credits. Let's say an operator registered his service with value 10 for 'vdaPerToken'. Once vdaPerToken become 30, do we need to request to the operator deposit more credits?
Here, I suggest 2 solutions:
Which solution will be better? (Now implementing in 2nd method)?
@ITStar10
First, once user has not enough credits, is he disconnected from all connected services automatically? So, he needs to connet services again later after he add credit?
I don't think we want to disconnect the services as the user will have to manually go and reconnect each service. Rather, perhaps we have a status
on Verida Account Connection
that is unpaid
?
Otherwise, if user isn't disconnected but not able to use service till he add credit, does he deposit his credit to the service as done in connecting service?
He just calls addCredit()
. That method will need to then update any Verida Account Connection
that is unpaid
to be active
.
When a user connect a service, he should pay the amount of "price * minimumDays".
He doesn't pay
at the time of connection, rather the tokens are locked. The number of locked tokens is servciePrice * minimumDaysCreditPerService *
.
Do we need 'chargeCredit' function that deposit user's credit into service? If so, how much need to be deposited? Same amount as 'connectService'? In this case, is user responsible to pay credits for non-used days?
That was the original purpose of the distribute()
method in the original scope (see top). The amount to charge is pricePerDayPerAccount
specified when registering the service.
Second, we need to separate updateService() function for maxAccount & pricePerDayPerAccount. Because, operators can update maxAccount anytime. But there is delay for pricePerDayPerAccount.
Can maxAccounts
or pricePerAccount
be null parameters in updateService()
and then ignored?
Third, we need to update checkServiceAvailability() function. This function is not implemented correctly. And it will depends on the answer of first question above.
Ok.
Service priceChangeDelayDays
problem
When will be the updated price applied?
- After 10days from when owner updated "priceChangeDelayDays", because that days is after 20 days(t3) from when operator updated the price?
- Or will the updated price be applied after 30 days (t4) from when operator updated?
I agree with option (2).
Service price update problem
Now, the service operator can only update price once per each duration of priceChangeDelayDays
.
If the service operator would like to cancel current update & set new price update operation, can we do this?
No. Not required.
vdaPerAccount problem
- Problem on decreasing 'maxAccount' Here, if the operator is trying to decrease 'maxAccounts' into 50, how much do we send back credits to operator? Amount by original price : 30 * (100 - 50)?
Yes, Amount by original price : 30 * (100 - 50)
- Problem on increasing 'maxAccount' Let's say operator is going to increase 'maxAccount' into 200. How much does opeartor deposit credit more?
In this scenario the operator needs to deposit:
vdaPerAccount
increase ((60 - 30) * (100)), where
100is the original
maxAccount` value.maxAccount
increase (60 * (200 - 100)
)So the formula is:
(oldPrice - newPrice) * (oldMaxAccount)
+ newPrice * (oldMaxAccount - newMaxAccount)
- Problems will be occurred when we use updated price for calculating credit amount
When the owner updates the vdaPerAccount from 10 to 30, we can't request all the existing services to update credits. Let's say an operator registered his service with value 10 for 'vdaPerToken'. Once vdaPerToken become 30, do we need to request to the operator deposit more credits?
Here, I suggest 2 solutions:
- Service operators use current vdaPerAccount on updates of 'maxAccount'.
Agree, I think (2) is best. However, can we ensure that they can't call registerService()
, updateService()
or deregisterService()
until they have added enough credit to allow for the updated vdaPerAccount
value?
Can maxAccounts or pricePerAccount be null parameters in updateService() and then ignored?
There is no null value for uint in Solidity. So these input values can be set 0 instead of 'null' on function calls.
So, I suggest to separate updateService() function for 'maxAccount' & 'pricePerAccount'.
Agree, I think (2) is best. However, can we ensure that they can't call registerService(), updateService() or deregisterService() until they have added enough credit to allow for the updated vdaPerAccount value?
Let's see following case: Operator register a service with 10 for vdaPerAccount & 20 for maxAccounts. After a while, vdaPerAccount is updated to 15. Service operator needs to deposit 5*20 credits more to this service.
Current addCredit() function only deposits credits to our contract, but not on service. When operator register service, 200 credits are removed from operator's credit amount and are locked in service. So we need some functions to deposit more credits on individual service. which function do we need?
I think we need both functions. A service operator can run several services of same infuraType
.
Once vdaPerAccount updated, they should deposit credits on every services of same infuraType
.
In case, they will prefer to resolve all services by one function call.
In the above example, service had been available till before vdaPerAccount updated.
After vdaPerAccount
updated, service operator didn't deposit enough credits (5*20).
Is this service available for users?
Service operators use our contract's function named 'checkServiceAvailability(serviceId, userDID, ...)' to allow their services for only connected users. They should call this functions absolutely, if not any unconnected users can use their services. So if service operator didn't pay enough credits on this service, we can return false inside 'checkServiceAvailability' function for any users including connected users.
Of course, this might be a good choice for us (Verida-Company), but please consider whether 'holding-up service' can be acceptable for service operators. Is this feature ncecessary on our contract?
@ITStar10 Noted.
I'd like flexibility to easily add new variables in the future. As such, can we do this?
updateService(did: string, serviceId: string, key: string, value: any)
Thoughts @pranavburnwal ?
Service operator runs 2 services of service#1 & service#2. After vdaPerAccount updated by contract owner(us - verida), credits deposited before is not enough for service#2.
What will be restricted to this service operator?
Locked Amount = vdaPerAccount * maxAccountsOfService
It can be occured at following cases:
Operator'd be restricted if he didn't add credit:
Though operator is restricted, service will run continuously. Because, users had already deposited their credits to use this service. If we restrict running service, it will make code complicated to calculate user fees. Becasue there are several conditions to consier while calculating service fees.
Operator will not be restricted on his other services:
New users can connect to all other services
If operator didn't make claim in 14 days, he will lose the right for those days.
Ex: Operator make first claim on 1st of the month. He make another claim on 21st of the month.Service fees of this 20 days will send to Verida.
Locked Amount = servicePrice * minimumDaysCreditPerService
Once connected, user can use this service for 'minimumDaysCreditPerService' days. This will not be affcted once 'servicePrice' or 'minimumDaysCreditPerService' updated.
If user disconnect this service before 'minimumDaysCreditPerService' passed, credits for remaining days will send back to the user.
At first 'minimumDaysCreditPerService' days, the price will be calculated by 'servicePrice' of registered time. After that, user should pay fees by updating 'servicePrice'.
Fees are calculated when the service operator requests to claim. Users should pay the fees by 'servicePrice' of claiming time. Fees will be automatically send to the service on claiming request by service operator. To pay the fees, user should has credits in contract.
If user doesn't have enough credits to pay the fees, user can not use service until before he adds enough credits. User should pay for unpaid days though he couldn't use the service.
Ex: User connects to a service with 5 days for 'minimumDaysCreditPerService' and 3 for 'servicePrice'. He has 22 credits at connecting time. After connected, user has 7 credits(= 22 - 3 5) in the contract. User can use the service for first 5 days. As he has 7 credits more, he can use 2 days more after first 5 days. After 7 days, his remaining credit is only 1( = 7 - 3 2). And he can't use the service. User doesn't add credits and pass 4 days(11 days from start). After 11 days from the start, user adds 30 credits. Here, our contract calculate for fees that user unpaid. Among added 30 credits, 12(= 3 * 4) credits are for unpaid days. And remaining credits will be 18( = 30 - 12). So he can use service 6(= 18 / 3) days more after he added 30 credits.
If an operator didn't claim in 14 days, this claim amount will be owned to contract. So we need a function to withdraw these credits.
When service operator calls removeService(), there might be users who didn't pay fees. Should ignore these?
@ITStar10 Noted.
I'd like flexibility to easily add new variables in the future. As such, can we do this?
updateService(did: string, serviceId: string, key: string, value: any)
Thoughts @pranavburnwal ?
I don't think we should do those now. We can leverage upgradable contracts for this.
Maybe even consider the diamond storage
Here, service price is 5 and no updates till t3.
minimumDaysCreditsPerService
is 10 and no updates till t3.
User has 50 credits at t1.
User connects to a service at t1 and use this service for 10( = 50 / 5) days. After 10 days (at t2), user doesn't have any credits. He doesn't add credits till t3. At t3 point, if user wanted to use this service, he should pay for unused days - 20 days from t2 to t3. And he should pay more to further use. So, if he wanted to use 10 more days from t3, he should add 150 credits (100 for past 20 days, 50 for current use) totally.
If the user is allowed to disconnect service at t3, he can disconnect this service. And connect service again. Then he should add only 50 credits for minimumDaysCredits and can use 10 days.
Can user disconnect this service at t3?
User connected 2 services of service#1 & service#2. He passed 'minimumDaysCreditPerService' for both services, so he should pay by credits to use services. He has 10 credits. And the price of services are 6 and 8 respectively.
User can't use 2 services together at this point, he needs 14(6+8) credits to be allowed for both services. We(-Verida) should allow only one service.
Among thse 2 services, which one can we allow to this user?
Question
Can user disconnect this service at t3?
I think we something like 3 grace days (like in fintech) for users to cancel. So like if a user is out of credits at T2, He can cancel for free (pay no extra credit till T2 + 3 days.
So following the story the user finishes credits in T2, if they would have canceled by T2+3days they would not be charged. Instead, they have canceled by T3, so they will have to pay by 17 days extra (they use it or not) if the service is active they should, 3 days grace.
PS: Just my thoughts, needs discussion.
Question
Among thse 2 services, which one can we allow to this user?
My thought will be to disable both. Less than needed balance so just block both. We don't have 'priority' and/or 'weightage' between services so we cannot choose. Ideally, the other option will be to just allow the first service that gets accessed first.
@tahpot Thoughts?
This task is for the smart contract implementation of Service Registry - Phase 1.
Smart contract requirements
The following smart contract requirements have been scoped based on the user stories in Phase 1.
Architecture overview
Methods:
registerService(did: string, serviceType: ServiceType, endpointUri: string, country: string, maxAccounts: integer, pricePerDayPerAccount: decimal, proof: string, consentBlock: integer)
Enable a
Verida Infrastructure Operator
to register a service.proof
is a signed message in the form:Constraints:
Verida Infrastructure Operator
must bond VDA tokens equal to (maxUsers
*vdaPer<InfrastructureType>Account
), whereInfrastructureType
can be determined by looking upserviceType
fromServiceType
lookup. These bonded tokens are removed from the account's credit and placed in a separatebonded tokens
pool of VDA tokens within the smart contract. These bonded tokens will be paid to theVerida Infrastructure Operator
every day via thedistribute()
function.serviceId
that represents the service equal tohash(did+serviceType)
proof
must be signed bydid
and match the parameters supplied when calling the method. How to prevent replay attacks? Use block number range?updateService(did: string, serviceId: string, maxAccounts: integer, pricePerAccount: decimal)`
Update details about the service. Note: Changing the country or endpoint URI is not permitted. Instead a new service type should be created and the current one deregistered.
Updating
maxAccounts
will take effect immediately.Updating
pricePerAccount
will take effect after 30 days (priceChangeDelayDays
) for existing accounts (to give sufficient notice of any changes) if the price increases. The price change will take effect immediately for all new accounts.Constraints:
maxAccounts
is being set to a value lower than the current number of connected accountsderegisterService(did: string, serviceId: string)`
Enable a
Verida Infrastructure Operator
to deregister a service. There will be a 30 day delay (seederegisterDelayDays
) before the service is removed entirely. The service will immediately stop accepting new connections. The service will be flagged as pending removal.addCredit(did: string)
Allow a
Verida Account
to deposit VDA tokens as credit to their account. Credit is stored against a given DID. This can be used by bothVerida Account
users andVerida Infrastructure Operator
users.removeCredit(did: string)
Allow a
Verida Account
to withdraw VDA tokens that are currently sitting as credit in their account. Tokens will be withdrawn to the account that called this method.The smart contract will reject the request if, after removing the tokens, the
did
will have insufficient credit to meet 30 days (minimumDaysCreditPerService
) for all connected services.connectService(did: string, serviceId: string)
Enable a
Verida Account
to connect and make use of a service provided by aVerida Infrastructure Operator
. TheVerida Account
must deposit sufficient tokens for 30 days (minimumDaysCreditPerService
) of utilising the service, calculated based on thepricePerDayPerAccount
.The smart contract will reject the connection if:
disconnectService(did: string, serviceId: string)
Enable a
Verida Account
to disconnect a service provided by aVerida Infrastructure Operator
. TheVerida Account
will free up tokens previously deposited for utilising the service that can then be removed viaremoveCredit()
.discoverServices(infraType: string (required), serviceType: string, country: string, maxPricePerDay)
Enable easy searching of the available services so users can discover the services they want to select.
distribute()
Distribute credit between all the registered
did
's based on the connected services.For each
Verida Account Connection
credit VDA tokens to theVerida Infrastructure Operator
and debit tokens from theVerida Account
base on the servicepricePerDayPerAccount
.If the
Verida Account
has insufficient credit the services they have registered are automatically de-registered. There is no guarantee to the order that services are de-registered.Credit can be added or removed (effectively
claiming
) via theaddCredit()
andremoveCredit()
methods.Does this method need to be called externally or could it be automatically called every day?
Constraints:
DID v blockchain address
Most smart contracts allocate tokens to a blockchain address. In our instance, we need to allocate tokens to a specific DID and only allow those tokens to be withdrawn by that DID.
Data Structures:
InfrastructureType (enum):
ServiceType (lookup):
ServiceTypeConfig:
The configuration will depend on the
ServiceType
.Config Variables:
These variables can be changed by the contract owner.
vdaPerDatabaseAccount
: How many VDA tokens must be bonded by theVerida Infrastructure Operator
to register a database nodevdaPerMessagingAccount
: How many VDA tokens must be bonded by theVerida Infrastructure Operator
to register a messaging nodevdaPerStorageAccount
: How many VDA tokens must be bonded by theVerida Infrastructure Operator
to register a storage nodevdaPerNotificationAccount
: How many VDA tokens must be bonded by theVerida Infrastructure Operator
to register a notification nodederegisterDelayDays
: How many days to delay a service from requesting de-registration to actually being de-registered?minimumDaysCreditPerService
: How many days credit must aVerida Account
maintain for eachVerida Account Connection
?priceChangeDelayDays
: How many days to delay price changes for servicesDeliverables
Future considerations