Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quasar Fast Exit: Implement withdrawal the happy path #723

Merged
merged 10 commits into from
Jan 14, 2021

Conversation

souradeep-das
Copy link
Contributor

@souradeep-das souradeep-das commented Dec 2, 2020

Overview

This includes-

  1. Happy Withdrawal path with challenges
  2. Updating Quasar Parameters

Yet to implement-

  1. Byzantine condition and IFE-Claims
  2. ERC-20 support
  3. Anti front-running and service provider fee

@souradeep-das
Copy link
Contributor Author

Funds flow in/out the contract

TokenCapacity

--> addEthCapacity() increases capacity
----> obtainTicket() decreases capacity
------> flushExpiredTicket() can increase the capacity back
------> challengeClaim() can increase the capacity back
else, withdrawal successful, capacity decreased

Bond

--> obtainTicket() adds bond value to contract
----> flushExpiredTicket() adds bond to bond reserve (stays in the contract)
----> challengeClaim() returns bond to challenger
----> processClaim() returns bond to output owner

bondReserve is collective amount of bonds that is not blocked by any claim. can be withdrawn

* @notice Only an unclaimed ticket can be flushed, bond amount is added to bondReserve
* @param utxoPos pos of the output, which is the ticket identifier
*/
function flushExpiredTicket(uint256 utxoPos) public {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can push tickets to queue, to flush expired tickets together according to timestamp, but guessing there might not be many expired unclaimed tickets because of the bond

* @dev Process the Claim to get liquid funds
* @param utxoPos pos of the output, which is the ticket identifier
*/
function processClaim(uint256 utxoPos) public {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can maintain a queue for processing multiple claims together

Copy link
Contributor Author

@souradeep-das souradeep-das left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is a scope to remove utxoPos from the args required for claim() and challengeClaim(). I have kept it for now for simplicity and readability but this might increase gas.
Any suggestions about these design decisions?

Copy link
Contributor

@InoMurko InoMurko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1337

Copy link
Contributor

@InoMurko InoMurko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed I didn't look at the correct PR.

Comment on lines 107 to 108
tokenUsableCapacity[ticketData[utxoPos].token] = tokenUsableCapacity[ticketData[utxoPos].token].add(tokenAmount);
bondReserve = bondReserve.add(ticketData[utxoPos].bondValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tokenUsableCapacity[ticketData[utxoPos].token] = tokenUsableCapacity[ticketData[utxoPos].token].add(tokenAmount);
bondReserve = bondReserve.add(ticketData[utxoPos].bondValue);
tokenUsableCapacity[ticketData[utxoPos].token] = tokenUsableCapacity[ticketData[utxoPos].token].add(tokenAmount);
bondReserve = bondReserve.add(ticketData[utxoPos].bondValue);

this is basically returning the reserved amount back to the Quasar and claim the bond?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! It's just adding to the amount of bonds from expired tickets, that the quasar owner can withdraw.(failed bonds)
I'll redirect you to this for the flow #723 (comment)

tokenUsableCapacity[token] = tokenUsableCapacity[token].sub(residualAlmount);
emit QuasarTotalEthCapacityUpdated(tokenUsableCapacity[address(0x0)]);
}
msg.sender.transfer(amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I should change this. Thanks for catching!

* @param rlpOutputCreationTx RLP-encoded transaction that created the output
* @param outputCreationTxInclusionProof Transaction inclusion proof
*/
function obtainTicket(uint256 utxoPos, bytes memory rlpOutputCreationTx, bytes memory outputCreationTxInclusionProof) public payable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed we sometimes use underscores to differentiate global params and function arguments, is this a thing we should be doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think what we followed in the past is to move away from underscore prefixes unless there is a var name collision, probably because of possible confusion of it being an unused var 😄

require(msg.value == bondValue, "Bond Value incorrect");

tokenUsableCapacity[outputData.token] = tokenUsableCapacity[outputData.token].sub(outputData.amount);
ticketData[utxoPos] = Ticket(msg.sender, block.timestamp.add(14400), outputData.amount, outputData.token, msg.value, rlpOutputCreationTx, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would an emmited event be useful here? I'm not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we definitely need more events.

Comment on lines +199 to +204
require(MoreVpFinalization.isStandardFinalized(
plasmaFramework,
rlpTxToQuasarOwner,
utxoQuasarOwnerDecoded.toStrictTxPos(),
txToQuasarOwnerInclusionProof
), "Provided Tx doesn't exist");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand this correctly, the output needs to be transfered to the quasar owner on the actuall plasma chain and it has to be included in the block, correct?

This means, from the UX perspective:

  1. initate an exit on plasma framework contracts
  2. get ticket from quasar for the exit
  3. after the ticket from the quasar, use the same data to make a transfer via Watcher to ChildChain and transfer that output to the quasar owner and wait for a block to be submitted
  4. now claim

is that correct?

is there a way to merge any of these steps?

perhaps:
quasar emits an event that marks the output from alice that wants to exit as spent
and quasar emits an event that "makes a fake deposit" that basically transfers the ownership of the output to the quasar owner?

maybe that's actually one event emmited. this events would be tracked by the childchain and watcher.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes correct, for the standard way of doing the claim, you have to spend the output to the quasar owner and it has to be included in a block, (there is IFEClaim for tx not included, which is more of a way to recover funds)
The initiate exit on plasma is not required, instead just spending the output
so the UX will be -

  1. get ticket from quasar with an output
  2. after the ticket from the quasar, make a transfer via Watcher to ChildChain and spend that same output to the quasar owner and wait for a block to be submitted
  3. Now Claim (there is no processClaim if there is no waiting period on quasar, so they can be merged)

Merging some steps on the UX would be interesting to look at. I think looking at it from the core, the 1st step is contract, 2nd is Plasma and 3rd is contract again. I was thinking if 1 and 2 could be one step, doing the second on a success receipt of 1, but still would require two signing?

yeah, these are good thoughts to explore 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it safe to merge step 1 and 2 by emitting an event? so that the childchain and watcher spend the output and there's no need to manually send a transaction from alice to quasar owner?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm this is tricky, currently the watcher would expect the tx to be correclty signed by alice after the block is submitted and also if there is a need to start exit on this tx later, it won't happen if it isn't signed by alice, so I think merging the steps from a ux perspective should be good as long as we get alice to sign both times? I might be wrong though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for 3, I feel it should be fine for anyone to call the claim func with the parameters. so maybe maybe there is a chance here that we could have a bot automate 3 after 2?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes absolutely

Copy link
Contributor

@InoMurko InoMurko Dec 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the topic of 1 and 2.
what if we make the transaction between alice and quasar - like a deposit event?
and alice, since this is coming from her, could still combine an output spending into the deposit?

okay... I might be missing something. Brainstorming here:

  • ticket claiming emits an event that combines an output spenditure + a deposit event to the quasar owner?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting! one thing i think will stand in the way is - the need of alice's output to be an input in the tx to the quasar owner in order to verify the claim. in case we mark the output as spent and send a deposit event, we won't have inputs on the deposit tx, and still thinking if there is a way to verify alice spent her output. but worth exploring.
Also to mark alice's output as spent we need a tx that spends that output? since this tx will be used to challenge alice exiting that output. So we would still need a signed tx from alice anyway?

@InoMurko
Copy link
Contributor

any particular obstacles if we merge flushExpiredTicket (by iterating through the list of tickets and checking block.timestamp > expiryTimestamp && expiryTimestamp != 0)?

@souradeep-das
Copy link
Contributor Author

Oh do you mean to collectively flush all expired tickets? we can totally do that

@InoMurko
Copy link
Contributor

Oh do you mean to collectively flush all expired tickets? we can totally do that

Yeah, but also, maybe we can do that while someone calls obtainTicket/3? Or would that be too expensive?

@souradeep-das
Copy link
Contributor Author

ohh so attempting to flush expired tickets from the list everytime a ticket obtained? that's a possibility. Yeah that might be expensive, and we might expect very less tickets to go expired beacuse of the bond ( we should call it unclaimed instead of expired ticket) only the tickets that weren't claimed will need to be flushed

Copy link
Contributor

@kevsul kevsul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general looks good.
I've updated many of the error messages for readability, but feel free to modify my suggestions.

plasma_framework/contracts/poc/fast_exits/Quasar.sol Outdated Show resolved Hide resolved
plasma_framework/contracts/poc/fast_exits/Quasar.sol Outdated Show resolved Hide resolved
plasma_framework/contracts/poc/fast_exits/Quasar.sol Outdated Show resolved Hide resolved

require(outputData.amount <= tokenUsableCapacity[outputData.token], "Requested amount exceeds the Usable Liqudity");
require(outputData.amount != 0, "The reserved amount cannot be zero");
require(msg.value == bondValue, "Bond Value incorrect");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rearrange these checks to fail sooner when possible?
This one could be the first check, for example

plasma_framework/contracts/poc/fast_exits/Quasar.sol Outdated Show resolved Hide resolved
* @param utxoPos pos of the output, which is the ticket identifier
*/
function verifyTicketValidityForClaim(uint256 utxoPos) private {
require(!ticketData[utxoPos].isClaimed, "Already Claimed");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
require(!ticketData[utxoPos].isClaimed, "Already Claimed");
require(!ticketData[utxoPos].isClaimed, "Already claimed");

= PaymentTransactionModel.getOutput(decodedTx, 0);

// verify output to Quasar Owner
verifyOwnership(outputData, quasarOwner);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message if this fails doesn't make sense - see comment on verifyOwnership() above

// verify output to Quasar Owner
verifyOwnership(outputData, quasarOwner);
// considering fee as a separate input
require(ticketData[utxoPos].reservedAmount == outputData.amount, "Wrong Amount Sent to Quasar Owner");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
require(ticketData[utxoPos].reservedAmount == outputData.amount, "Wrong Amount Sent to Quasar Owner");
require(ticketData[utxoPos].reservedAmount == outputData.amount, "Wrong amount sent to quasar owner");

verifyOwnership(outputData, quasarOwner);
// considering fee as a separate input
require(ticketData[utxoPos].reservedAmount == outputData.amount, "Wrong Amount Sent to Quasar Owner");
require(ticketData[utxoPos].token == outputData.token, "Wrong Token Sent to Quasar Owner");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
require(ticketData[utxoPos].token == outputData.token, "Wrong Token Sent to Quasar Owner");
require(ticketData[utxoPos].token == outputData.token, "Wrong token sent to quasar owner");

= PaymentTransactionModel.decode(claimTx);

// first input should be the Utxo with which ticket was obtained
require(decodedTx.inputs[0] == bytes32(utxoPos), "Wrong Tx provided");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
require(decodedTx.inputs[0] == bytes32(utxoPos), "Wrong Tx provided");
require(decodedTx.inputs[0] == bytes32(utxoPos), "The claim transaction does not spend the correct output");

souradeep-das and others added 2 commits January 5, 2021 17:04
Co-authored-by: Kevin Sullivan <4653170+kevsul@users.noreply.github.com>
@souradeep-das souradeep-das requested review from kevsul and InoMurko and removed request for madxor January 11, 2021 07:08
@souradeep-das souradeep-das merged commit bf6c44c into v2.0.0 Jan 14, 2021
@souradeep-das souradeep-das deleted the souradeep/quasar branch January 14, 2021 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants