Open cameel opened 6 years ago
@cameel
POST bankster/trigger-payments/ endpoint Output A value indicating for each transaction whether it has been successfully submitted.
Maybe they should be mapped with subtask_id
?
They could but does it add anything? You specify DespositClaim
s as input and they already contain subtask_id
s.
Depends if it matters to client in anyway, I mean, for example, if he needs info which transaction was not successful. It would be more difficult to break something if they will be mapped with subtask_ids
instead of basing it on list order.
I don't see why.
Let's say you do POST bankster/trigger-payments/
with deposit_claim_ids=[1, 2, 3, 4]
. You must have four DepositClaim
objects in your database and both Bankster and Concent Core can access them. First for example with id=1
and subtask_id='aaa'
. Second with id=2
and subtask_id='bbb'
and so on. You get success=[True, False, True, True]
in response. So you know that deposit 2
associated with subtask 'bbb'
is the one that failed.
If you want to process the response you just go over both lists:
for deposit_claim, success in zip(deposit_claims, success_list):
if not success:
print(f"Failed to pay for subtask {desposit_claim.subtask_id}")
How about for logging and debugging purposes ? Lets image a situation a client comes with complains that he sends a request with ids 1,2,3
and retrieves True, True, False
, however payment related to id 1 is not done. Then you start debugging with assumption that False
was returned for id 3. And if there will be a bug in the code, which result in output table being ordered wrong way, you will look in the wrong place for solution. By having it matched you avoid this situation.
OK. Including this redundant information might indeed help when you only have the response or if you cannot rely on the the order being correct. I have updated the description. Now success
is a dict.
Blueprint updated:
finalize payment
now processes only a single claimdiscard claim
as a separate operation and removed this responsibility from finalize payment
.DepositAccount
.DepositClaim.tx_hash
should be unique.DespositClaim.subtask_id
replaced with a foreign key.Update:
settle overdue acceptances
. Step 6 (Transaction
) said to create a transaction and step 7 (Deposit lock
) said to create DepositClaim
but did not mention that transaction ID should be stored in the claim. Without the ID we would be unable to track the transaction and remove the claim when it's satisfied.
Bankster (with deposit claim tracking)
Bankster is an intermediate layer between Concent Core and the Ethereum client (Geth). All communication with the Ethereum client is performed via Golem's Smart Contracts Interface (SCI) which is used as a library.
Bankster's purpose is to encapsulate the logic involved in paying out funds from deposits in the course of a Concent use case.
Operations
The process of making a payment from deposit has three stages:
claim deposit
operationThe purpose of this operation is to check whether the clients participating in a use case have enough funds in their deposits to cover all the costs associated with the use case in the pessimistic scenario and to mark those funds as locked until they get paid out.
Concent core performs this operation at the beginning of any use case that may require payment from deposit within a single subtask. Currently those are
ForcedAcceptance
andAdditionalVerification
.ForcedPayment
use case operates on more than one subtask and for that reason requires a separate operation.The funds are locked in anticipation of a future payment but it does not necessarily mean that exactly that amount will be paid out or even that it will actually be paid. The actual amount paid will depend on the outcome of the use case.
Input
subtask
Subtask
concent_use_case
ConcentUseCase
requestor_public_key
TaskToCompute
.provider_public_key
TaskToCompute
.requestor_ethereum_public_key
provider_ethereum_public_key
. Comes fromTaskToCompute
.provider_ethereum_public_key
requestor_ethereum_public_key
. Comes fromTaskToCompute
.subtask_cost
TaskToCompute.price
multiplied by maximum task duration. Must be greater than zero.Output
claim_against_requestor
DepositClaim
DepositClaim
object that was created to lock a part of requestor's deposit.claim_against_provider
DepositClaim
DepositClaim
object that was created to lock a part of provider's deposit. May be empty if nothing is claimed from the provider.Sequence of operation
ForcedAcceptance
use case:subtask_cost
.AdditionalVerification
use case:subtask_cost
.Client
andDepositAccount
objects (if they don't exist yet) for the requestor and also for the provider if there's a non-zero claim against his account.DepositAccount
objects that will be used as payers in newly createdDepositClaim
s.amount
s of all existingDepositClaim
s where the requestor is the payer.claim_against_requestor
andclaim_against_requestor
being bothNone
.amount
s of all existingDepositClaim
s where the provider is the payer.DepositClaim
just created aginst requestor's deposit.claim_against_requestor
andclaim_against_requestor
being bothNone
.DepositClaim
objectpayee_ethereum_public_key
: payee can be either the provider (if the requestor is the payer) or Concent (if the provider is the payer). The address of Concent's account either comes from settings or is automatically inserted by the contract.amount
: The full amount claimed. Even if there's not enough deposit, we don't reduce the claim until the moment when we actually create a transaction. The situation may change in the meantime - some claims may be canceled or the client may increase (or reduce) the deposit.tx_hash
: We're not creating a transaction yet so this field is left empty.DepositClaim
s are included in the result. At least one claim must have been created.DepositAccount
s are released.Expected Concent Core behavior
DepositClaim
objects created by Bankster (they're in the same database).DepositClaim
objects if it fails at any point after the operation if the operation has succeeded.DepositClaim
objects only after they're successfully paid for.DepositClaim
objects if the outcome of the use case indicates that they don't need to be paid (e.g. if additional verification confirms requestor's decision to reject the result).Client
andDepositAccount
objects are never removed. They stay in the database even if the operation fails and even after all theDepositClaim
s that refer to them are removed.finalize payment
operationThis operation tells Bankster to pay out claimed funds.
Concent Core performs this operation at the end of any use case that may require payment from deposit within a single subtask, for all
DepositClaims
created in that use case. Currently those areForcedAcceptance
andAdditionalVerification
.ForcedPayment
use case operates on more than one subtask and for that reason requires a separate operation.Bankster uses SCI to submit an Ethereum transaction to the Ethereum client which then propagates it to the rest of the network. Hopefully the transaction is included in one of the upcoming blocks on the blockchain. Bankster updates
DepositClaim
with the transaction ID and starts listening for blockchain events. The claim is removed from the database once the transaction actually appears on the blockchain.If there's not enough funds at the moment, the
amount
is decreased. If there's nothing at all, the claim is discarded without payment.Input
deposit_claim
DepositClaim
Output
tx_hash
Sequence of operation
DepositClaim
.DepositAccount
object.amount
s of all existingDepositClaim
s that have the same payer as the one being processed.DepositClaim
object being processed.DepositAccount.amount
, Bankster sets this field to the amount that's actually available.DepositClaim
still exists at this point:DepositClaim.tx_hash
.DepositAccount
is still locked to prevent two simultaneous operations from independently making two separate payments for the same claim.DepositAccount
s are released.DepositClaim.tx_hash
if a transaction has been created.Expected Concent Core behavior
DepositClaim
objects created by Bankster (they're in the same database).discard claim
operationThis operation tells Bankster to discard the claim. Claim is simply removed, freeing the funds.
This operation is only possible if the payment has not been finalized. If it has, the claim stays in the database until Bankster determines whether the corresponding Ethereum transaction has succeeded. If it has succeeded, the claim is discarded automatically. If it has failed, the claim stays. We may want to add a mechanism to submit a new transaction to retry a failed payment later but for now it's going to be a manual process.
Concent Core performs this operation for all
DepositClaims
created in a use case for which payment is not necessary. This is the case for example if it's determined that the provider has not computed the subtask correctly or when the use case times out. Currently those use cases areForcedAcceptance
andAdditionalVerification
.ForcedPayment
use case operates on more than one subtask and for that reason requires a separate operation.Note that the provider has to pay for additional verification, regardless of the result. The fee may be waived only if Concent fails to perform the service and the use case times out.
Input
deposit_claim
DepositClaim
Output
claim_removed
True
if the claim has actually been removed.Sequence of operation
DepositAccount
object.DepositClaim.tx_hash
not empty:DepositClaim
object from the database.claim_removed
isTrue
.claim_removed
isFalse
.DepositAccount
s are released.claim_removed
.settle overdue acceptances
operationThe purpose of this operation is to calculate the total amount that the requestor owes provider for completed computations and transfer that amount from requestor's deposit. The caller is responsible for making sure that the payment is legitimate and should be performed. Bankster simply calculates the amount and executes it.
The provider proves to Concent that the computations were performed and accepted and Concent Core asks Bankster to compare the total value with the amount actually paid by the requestor, either directly or from deposit. If it turns out that the total value of provider's claims was not covered completely, Concent transfers the missing amount from requestor's deposit. If the deposit is not large enough to cover the whole amount, Concent transfers as much as possible. After this operation the provider can no longer claim any other overdue payments that happened before the deposit transfer.
Input
requestor_public_key
TaskToCompute
.provider_public_key
TaskToCompute
.requestor_ethereum_public_key
provider_ethereum_public_key
. Comes fromTaskToCompute
.provider_ethereum_public_key
requestor_ethereum_public_key
. Comes fromTaskToCompute
.acceptances
SubtaskAcceptance
SubtaskAcceptance
subtask
Subtask
payment_ts
payment_ts
timestamp from the acceptance message.amount
Output
claim_against_requestor
DepositClaim
DepositClaim
object that was created to lock a part of requestor's deposit. If this is empty, the requestor either has no deposit or the deposit is completely covered by existing claims.Sequence of operation
Client
andDepositAccount
objects for the requestor if they don't exist yet.DepositAccount
object.amount
s of all existingDepositClaim
s where the requestor is the payer.claim_against_requestor
beingNone
.DepositClaim
objectsubtask
is empty since the claim may refer to multiple subtasks.amount
: The amount actually used in the transaction.tx_hash
: Transaction ID of the newly created transaction.DepositClaim
is included in the result.DepositAccount
s are released.Background operations
Blockchain event subscriptions
SCI actively monitors forced payments on the blockchain and offers a way to be notified about them. Bankster subscribes to these notifications and uses them to discard claims that have been satisfied.
Claims are only ever dropped if a transaction is not successful. A failed transaction means that a claim remains in effect (it has not been paid after all) and may require intervention of a person managing the cluster.
Every payment type (forced payment, forced subtask payment, verification fee) should be covered by a subscription.
Subscription handler is running in every container that hosts SCI. Since every SCI instance monitors blockchain independently, this means that one event can be reported and processed multiple times. The handler should silently ignore events for which the corresponding claim no longer exists.
Sequence of operation
The assumption here is that the event is only reported when a transaction has been successfully included in the blockchain and has enough confirmations to make its reversal very unlikely.
DepositClaim
withtx_hash
reported in the event exists in the database.DepositAccount
object associated with the claim.DepositClaim
object.DepositAccount
s are released.Database
DepositClaim
modelcontrol
subtask
Subtask
NULL
if and only ifconcent_use_case
isForcedPayment
.payer_deposit_account
DepositAccount
payer_deposit_account.ethereum_public_key
cannot be the same aspayee_ethereum_public_key
.payee_ethereum_public_key
payer_deposit_account.ethereum_public_key
.concent_use_case
ConcentUseCase
amount
tx_hash
created_at
updated_at
DepositAccount
modelcontrol
It would be possible to have the account address directly in
DepositClaim
. The main reason for having a separate object for it is to allow us to put a database lock on allDepositClaim
s belonging to a specific payer. Putting locks directly onDepositClaim
would not work when there are no claims yet.We never create
DepositAccount
for the payee because the payee may be Concent itself.client
Client
Client
who owns the deposit on this account.ethereum_public_key
created_at
ConcentUseCase
enumForcedAcceptance
AdditionalVerification
ForcedPayment
Communication examples
Initial state
Deposits:
DepositClaim
s:tx_hash
ForcedAcceptance
None
ForcedAcceptance
None
AdditionalVerification
AdditionalVerification
Subtask
objects for all subtasks referenced by claims already exist.DepositAccount
s.Sequence 1: client D forces client A to accept subtask S10
[t=500] Client D agrees to compute subtask S10 for client A.
TaskToCompute.timestamp
: 500.TaskToCompute.deadline
: 600.[t=1001] Client D submits
ForceSubtaskResults
message tosend/
endpoint.TaskToCompute.price
* task duration): 10 GNTclaim deposit
operation.concent_use_case
:ForcedAcceptance
subtask_cost
: 10 GNT[t=1010] Bankster handles
claim deposit
operation.Client
instance for client A already exists.DepositAccount
instance for ethereum account A1 already exists.get_deposit_value(A1)
from SCI.DepositAccount
A1.DepositClaim
DC10ForcedAcceptance
tx_hash
:None
claim_against_requestor
: DC10claim_against_provider
:None
[t=1030] Response from Bankster, processing in
send/
continues.Subtask
S10.ForceSubtaskResults
to the receive queue of client A.[t=1100] A transaction is published on the blockchain
[t=1160] Client D makes a request to the
receive/
endpoint.ForceSubtaskResultsResponse
and it's already past deadline + SVT + FAT + CMT.Subtask
S10 due to the timeout.finalize payments
operation.deposit_claim
: 10[t=1170] Bankster handles
finalize payments
operation.get_deposit_value(A1)
from SCIDepositAccount
A1DepositClaim.amount
in DC10 lowered to 3 GNTforce_subtask_payment(DC10, D1, 3 GNT, S10)
from SCItx_hash
101010
DepositClaim.tx_hash
to101010
success
True
[t=1210] Response from Bankster, processing in
receive/
continues.SubtaskResultsSettled
to providers receive queue.SubtaskResultsSettled
to requestor's out-of-band receive queue.