raiden-network / raiden-contracts

Raiden Network Smart Contracts
MIT License
53 stars 45 forks source link

Proposal for a new type of refund/cancellation #1216

Open hackaugusto opened 5 years ago

hackaugusto commented 5 years ago

This is a rough sketch, it is by no means correct. I'm opening the issue so that we don't lose this proposal:

All values bellow are monotonic increasing.

## Settlement computation on-chain

Node's values set on the smart contract:
D = confirmed deposit
W = withdraw

Partner's balance proof values sent by the node:
L = locked
U = unlocked
E = expired
R = refund

Node's balance proof values sent by the partner:
R' = refund
U' = unlocked

Computed
N = net
P = pending

Constraints:
L,U,E,R,R',U',D,W >= 0
L >= U + E

Formulas:

N = D - W - U + U'
P = L - max(R', E) - U

The idea here is: For a channel A-B, the above definition would allow B to send a balance proof that changes the locked amount of A, with this balance proof A could prove to the smart contract that the tokens have been returned by B to A, so no backwards transfer would be required.

The biggest question that I have with the above is that if a locksroot is also required, or if just a returned amount is sufficient.

pirapira commented 5 years ago

max in max(R', E) looks weird to me. You don't care about the value of R' when you have lots of expired locks?

pirapira commented 5 years ago

The biggest question that I have with the above is that if a locksroot is also required, or if just a returned amount is sufficient.

I think a locksroot is required. Say two transfers with two different secret hashes were refunded. And say only one of the secrets has been revealed. Now it matters a lot which lock was refunded and which lock is alive.

hackaugusto commented 5 years ago

max in max(R', E) looks weird to me. You don't care about the value of R' when you have lots of expired locks?

I don't understand. How does max(R', E) mean that expired locks are ignored? (I'm assuming that is what you meant by don't care).

The core idea here is that both participants will be changing the same value. To avoid synchronization among the parties the value is made monotonic and max is used, otherwise we would require some sort of synchronization among the nodes to change this value. The next thing is to make sure the result of max(R', E) is semantically correct and not exploitable.

pirapira commented 5 years ago

max(0,100) is equal to max(20,100). So when E is 100, you don't care whether R' is 0 or 20.

pirapira commented 5 years ago

That's weird. When the channel is very young, E is still zero, so every change in R' matters. When the channel is very old after many expired locks of some value, E would be big, and you don't care about small R's anymore.

I think E can grow big while R' can stay at zero.

hackaugusto commented 5 years ago

max(0,100) is equal to max(20,100). So when E is 100, you don't care whether R' is 0 or 20.

Yes, that is intentional.

It has to possibilities, R' < E or E < R'. The value of R' is controlled by the partner node, while the value of E is controlled by the local node. For the case R' < E the result would be E, this means the local node does not have to wait for the partner to send a RemovedExpiredLock message. E < R' is the opposite, the partner does not have to wait for the local node to send a RefundTransfer

pirapira commented 5 years ago

@hackaugusto

  1. I send locked transfers but never reveal secrets. So E == 100 or something.

  2. I send a payment through this channel but the routing fails, so I get a refund R' == 20.

  3. The max computation kills my refund.

hackaugusto commented 5 years ago

@pirapira That would be an invalid message, it does not refund anything, the correct value for R' is the previous E in addition to the new refund amount, so it should be 120.

pirapira commented 5 years ago

Refunds include expired locks?

hackaugusto commented 5 years ago

With this definition, yes. Pretty much a refund message would be the partner node saying it's okay for the local node to use the tokens from a given lock, because the partner is "anticipating" the expiration.

Edit: Perhaps refund is a bad name for this approach, but I didn't think much about alternative names and this is a proposal for a new approach to refunds after all.

pirapira commented 5 years ago

I see definitions:

R' = refund
E = expired

but I see no equations between refund and expired.

pirapira commented 5 years ago

Are they somehow related?

pirapira commented 5 years ago

Is it intentional that

U' = unlock

Has unlock instead of unlocked?

hackaugusto commented 5 years ago

Are they somehow related?

Yes, they are "the same value" from the settlement algorithm perspective. This happens because the max is used in formula P = L - max(R', E) - U.

pirapira commented 5 years ago

So, R' and E supposed to be equal in a completely synchronous world?

pirapira commented 5 years ago

Still max is weird.

Let's say we have an event 1 that inceases R'. And event 2 that increases E. Both events increase the value by 10. In total the increase should be 20.

Event 1 happened on the peer's side so I see R' == 10. Event 2 happened on my side so I see E == 10. These two ten's come from different causes so the perfectly synchronous world I should see 20 already, but the max calculation only gives me 10.

pirapira commented 5 years ago

Yes, they are "the same value" from the settlement algorithm perspective. This happens because the max is used in formula P = L - max(R', E) - U.

Now you've put me in a loop. I asked why max and you answered it's because the same value. It's the same value because the max is used?

pirapira commented 5 years ago

I need to see what kind of event increases which variable.

hackaugusto commented 5 years ago

Here are two examples:

A -> B; LockedTransfer; locked_amount = 10 B -> A; Refund; refund_amount = 10

With the above, there is nothing pending, so P = L - max(R', E) - U would be P = 0 - max(10, 0) - 0, and there is not transfer of value. Which means the partner did a refund without locking additional funds.

Let's add some interaction:

A -> B; LockedTransfer; locked_amount = 10 A -> B; LockedTransfer; locked_amount = 20 B -> A; Refund; refund_amount = 10 A -> B; Unlock; unlocked_amount = 10

In this case, there is nothing pending P = L - max(R', E) - U would be P = 20 - max(10, 0) - 10, and the partner get the 10 tokens, because it had a net positive of 10 token from the formula N = D - W - U' + U, which would be N = D - 0 - 0 + 10 (Note: I had to fix the net formula)

hackaugusto commented 5 years ago

This is a hard case for me:

  1. A->B; LockedTransfer
  2. A->B; LockedTransfer
  3. Time passes
  4. B->A; RefundTransfer for 1
  5. A->B; RemoveExpiredLock for 2

I don't really know how to fix that race.

pirapira commented 5 years ago

A -> B; LockedTransfer; locked_amount = 10 A -> B; LockedTransfer; locked_amount = 20 B -> A; Refund; refund_amount = 10 A -> B; Unlock; unlocked_amount = 10

So the two parties disagree on the first lock, whether it's refunded or unlocked?

pirapira commented 5 years ago

When you send RemoveExpiredLock, do you write a bigger E?

When you receive Refund, do you expect a bigger R'?

pirapira commented 5 years ago
1. A->B; LockedTransfer
2. A->B; LockedTransfer
3. Time passes
4. B->A; RefundTransfer for 1
5. A->B; RemoveExpiredLock for 2

What's hard about this? I see no conflicting information.

hackaugusto commented 5 years ago

So the two parties disagree on the first lock, whether it's refunded or unlocked?

No, the values are monotonic, so a locked_amount of 20 means a second lock of 10. I didn't specify which lock was unlocked an which was refunded in the example for simplicity's sake.

hackaugusto commented 5 years ago

When you send RemoveExpiredLock, do you write a bigger E? When you receive Refund, do you expect a bigger R'?

Yes

pirapira commented 5 years ago

Then why do E and R' grow together?

hackaugusto commented 5 years ago

Then why do E and R' grow together?

They are changed by different computers separated by a network in between.

Edit: By "together", do you mean atomically?

pirapira commented 5 years ago

I heard E and R' are "the same value" (in the synchronous world).

hackaugusto commented 5 years ago

What's hard about this? I see no conflicting information.

One of the messages will be invalid, and then we have to reprocess the queue of messages. The whole point of using monotonically increasing values is to avoid synchronization, we need a policy on how to move forward even if that happens without having to synchronize.

pirapira commented 5 years ago

Is there a rule like, "When you receive Refund, you have to send RemoveExpiredLock"?

pirapira commented 5 years ago

There has to be some connection between E and R'?

pirapira commented 5 years ago
1. A->B; LockedTransfer
2. A->B; LockedTransfer
3. Time passes
4. B->A; RefundTransfer for 1
5. A->B; RemoveExpiredLock for 2

I can see all messages being valid. What kind of assumption am I missing?

pirapira commented 5 years ago

Maybe the locks must be removed in the same order as they are created?

pirapira commented 5 years ago

Maybe it's easier to start with the lifecycle of a hashtime lock.

So a hashtime lock can have these states. a. hasn't been created b. A added it in A-->B side of the channel state c. B saw it in A-->B side of the channel state d. expired e. unlocked f. refunded

And a lock can move

Right?

hackaugusto commented 5 years ago

Is there a rule like, "When you receive Refund, you have to send RemoveExpiredLock"?

Unless the current rules don't work (as in, send the remove expired lock after the lock expires), I wouldn't change it. But I did not think it in detail to see if there is a corner case that breaks this proposal.

There has to be some connection between E and R'?

Yes:

Effectively the defined values are saying that for the life time of the channel "X tokens were locked in this direction" locked_amount. "X tokens were moved to the partner" unlocked_amount. "X tokens were returned because the transfer failed" (refund_amount and expired_amount).

Maybe the locks must be removed in the same order as they are created?

That would be bad for refunds, either only the first or last lock would be refundable.

pirapira commented 5 years ago

"X tokens were returned because the transfer failed" (refund_amount and expired_amount).

Isn't expired_amount about the current block number moving ahead and the expiration block has passed?

pirapira commented 5 years ago

Maybe refund and expiration are two ways that the transaction fails?

Then I'm imagining now: E is about the expired locks from me to the peer. R' is about the refunded locks from me to the peer.

hackaugusto commented 5 years ago

Isn't expired_amount about the current block number moving ahead and the expiration block has passed?

The block being larger than the lock expiration is a requirement from the sender perspective. But the value itself only increases when the sender sends a balance proof with it. (I have the feeling I didn't get what you meant with this one)

pirapira commented 5 years ago

But the expired locks are counted towards the sender's balance, so E should be about the locks from the peer to me, that has expired.

hackaugusto commented 5 years ago

Maybe refund and expiration are two ways that the transaction fails? Then I'm imagining now: E is about the expired locks from me to the peer. R' is about the refunded locks from me to the peer.

yes (edit: almost, it should be R' is about the refunded locks from the peer to me.)

pirapira commented 5 years ago

If E and R' are about locks in different directions, why are they financially the same value?

pirapira commented 5 years ago

I miss the table with :arrow_up: and :arrow_down: filled for all messages and all variables.

hackaugusto commented 5 years ago

If E and R' are about locks in different directions, why are they financially the same value?

expired -> used by the sender refund -> used by the receiver

They both have the end effect of returning locked tokens to the sender.

I miss the table with arrow_up and arrow_down filled for all messages and all variables.

All values are monotonically increasing, what changes up or down is the balance (which is computed from the monotonic values).

Edit: With this proposal, the effect of a refund and a expired lock from the local view will return tokens to the sender, but from the partner's view it may not make a difference if the complementary message has been sent ... so I guess we also need a = entry in such a table.

pirapira commented 5 years ago

I still don't get it.

Why do these values need to be equal?

pirapira commented 5 years ago

Why do I take the max of the amount of lock A and the amount of lock B?

hackaugusto commented 5 years ago

Why do these values need to be equal?

In you example they are not, the values are mixed. This would make sense:

I send a lock A to the peer. This lock expires. That's counted in E. I send a lock B to the peer. The peer refunds this lock. That's counted in R'.

pirapira commented 5 years ago

I'm confused. You wrote

yes (edit: almost, it should be R' is about the refunded locks from the peer to me.)

So I thought R' is about a lock that the peer sends.

pirapira commented 5 years ago

I send a lock A to the peer. This lock expires. That's counted in E.

Now the lock is expired, and removed from the state. So a refund cannot happen, right?

hackaugusto commented 5 years ago

So I thought R' is about a lock that the peer sends.

Sorry, I misinterpreted your sentence. I read R' is about the refunded locks from me to the peer. as R' is about the refund_amount for all locks sent from my peer to me. and not as R' is the total amount of tokens returned from my peer as refunds

pirapira commented 5 years ago

I see, the message direction vs lock direction.