-
Notifications
You must be signed in to change notification settings - Fork 66
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
Conversation
Funds flow in/out the contractTokenCapacity--> addEthCapacity() increases capacity Bond--> obtainTicket() adds bond value to contract 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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this 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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1337
There was a problem hiding this 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.
tokenUsableCapacity[ticketData[utxoPos].token] = tokenUsableCapacity[ticketData[utxoPos].token].add(tokenAmount); | ||
bondReserve = bondReserve.add(ticketData[utxoPos].bondValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this not apply here? https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
require(MoreVpFinalization.isStandardFinalized( | ||
plasmaFramework, | ||
rlpTxToQuasarOwner, | ||
utxoQuasarOwnerDecoded.toStrictTxPos(), | ||
txToQuasarOwnerInclusionProof | ||
), "Provided Tx doesn't exist"); |
There was a problem hiding this comment.
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:
- initate an exit on plasma framework contracts
- get ticket from quasar for the exit
- 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
- 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.
There was a problem hiding this comment.
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 -
- get ticket from quasar with an output
- 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
- 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 👍
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes absolutely
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
any particular obstacles if we merge |
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 |
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 |
There was a problem hiding this 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.
|
||
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"); |
There was a problem hiding this comment.
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
* @param utxoPos pos of the output, which is the ticket identifier | ||
*/ | ||
function verifyTicketValidityForClaim(uint256 utxoPos) private { | ||
require(!ticketData[utxoPos].isClaimed, "Already Claimed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require(!ticketData[utxoPos].isClaimed, "Already Claimed"); | |
require(!ticketData[utxoPos].isClaimed, "Already claimed"); |
= PaymentTransactionModel.getOutput(decodedTx, 0); | ||
|
||
// verify output to Quasar Owner | ||
verifyOwnership(outputData, quasarOwner); |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require(decodedTx.inputs[0] == bytes32(utxoPos), "Wrong Tx provided"); | |
require(decodedTx.inputs[0] == bytes32(utxoPos), "The claim transaction does not spend the correct output"); |
Co-authored-by: Kevin Sullivan <[email protected]>
Overview
This includes-
Yet to implement-