-
Notifications
You must be signed in to change notification settings - Fork 208
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
fix(inter-protocol): ensure PSM availability after staging failure #6102
Conversation
stage.clear(); | ||
anchorPool.clear(); | ||
feePool.clear(); | ||
// NOTE someday, reallocate should guarantee that this case cannot happen |
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.
ticket? we probably will want to reference it often and it can serve to teach why and when to clear.
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.
Dunno. That comment pre-dates this PR.
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.
Ok. Since it's written in this contract twice and relevant to other contracts, I'm requesting a ticket to document the footgun and that it be referenced from the places like this that say "someday…"
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.
note that only one of the comments is valid (the one before the mintGains). The assert got copied to provide a better diagnostic, but the comment needs to be deleted.
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.
added #6116 and cited it. I don't understand that issue very well, so I'd appreciate it if someone else would clarify the title / description. @michaelfig helped me a little.
stage.clear(); | ||
anchorPool.clear(); | ||
feePool.clear(); |
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.
could we codify this pattern now with a contract helper? something like,
atomicReallocate(zcf, [
[seat, anchorPool, { In: given }, { Anchor: given }],
[stage, seat, { Stable: afterFee }, { Out: afterFee }],
[stage, feePool, { Stable: fee }],
]);
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 think it needs an onFail:
clause to invoke burnLosses
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.
postponed to #6116
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.
Progress on #6116 at #6577 , where stageMgr.commit()
replaces zcf.reallocate(...)
and does guarantee that all staging are cleared, whether the commit succeeded or failed.
@dtribble 's suggested stageMgr.transfer
operation is much like @turadg 's rows of atomicReallocate
above. The contrast between the list and the reified stageMgr is approx the contrast between #6577 and #4327
/** | ||
* @param {Amount<'nat'>} giveAnchor | ||
* @param {Amount<'nat'>} [wantStable] | ||
* @param {*} [out] |
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.
yay for typing. this param would benefit from more docs:
```suggestion
* @param {{seat: UserSeat, offerResult: *}} [out] will be filled
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.
But I find the need for this suspicious. This function is returning payouts to abstract away seat and offerResult… but here we are needing to punch through. Will contract users need to also? If so we should just change the return type to hide less: {seat, offerResult, payouts}
.
Or would it be sufficient to have {numWantsSatisfied, payouts}
?
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, I was too lazy to change the other call sites. Returning just the seat might be most straightforward.
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 refactored the driver to have additional methods that expose the seat.
let ix = 0; | ||
for (const [kind, give, want, result] of trades) { | ||
switch (kind) { |
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.
this data driven test calls for Ava macros. See #5900
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.
Note that the items have to be used within one test. The whole point is to not make a new PSM between them. Do macros work that way?
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.
usually things that need be shared between tests are configured with before
and beforeEach
. but I won't block on this.
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.
it needs to be shared between these trades but no other tests
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.
then another file would work well. this test
function is doing a lot of work to replicate some of the before
and macro
semantics.
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.
Let's punt on this for today.
@@ -335,7 +337,7 @@ export const createSeatManager = ( | |||
); | |||
return amountKeywordRecord; | |||
}, | |||
clear: ignoreContext(clear), | |||
clear: ({ self }) => clear(self), |
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.
just parroting the linter here: gotta remove the ignoreContext
import
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.
though, why delegate here at all? we can delete the const clear
above.
clear: ({ self }) => clear(self), | |
clear: ({ self }) => { | |
if (zcfSeatToStagedAllocations.has(self)) { | |
zcfSeatToStagedAllocations.delete(self); | |
} | |
}; |
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 think MarkM hinted at the right choice here. Please inline the method, so it'll be more apparent that the parameter handling is parallel to the sibling methods.
@@ -114,7 +114,9 @@ export const createSeatManager = ( | |||
}; | |||
|
|||
const clear = zcfSeat => { | |||
zcfSeatToStagedAllocations.delete(zcfSeat); | |||
if (zcfSeatToStagedAllocations.has(zcfSeat)) { |
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.
this is a change to the throw behavior of clear
. was the earlier behavior documented or tested?
I'll go google it since our API docs aren't on the APIs ;-) https://docs.agoric.com/zoe/api/zoe-contract-facet.html#zcfseat-clear Nope.
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.
Not only are there no tests for clear
, there appear to be no tests for zcfSeat.
[not suggesting this PR add anything.]
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 think there's more discussion going on, so I'm going to submit my current comments and wait to see how it settles before reviewing again.
['give', 200, 190, false], | ||
['want', 101, 100, 1], | ||
['give', 50, 50, false], | ||
['give', 51, 50, 1], |
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.
Even though I've been working with JS for a couple years now, seeing false
and 1
as two possible values of a flag strikes me as wrong.
@@ -160,11 +160,24 @@ export const start = async (zcf, privateArgs, baggage) => { | |||
AmountMath.isGTE(maxAnchor, wanted), |
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 the TODO on line 158 interact with the current problem at all? Can it be updated?
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.
we certainly have relevant tests; I'm getting rid of that TODO in favor of #6116
stage.clear(); | ||
anchorPool.clear(); | ||
feePool.clear(); |
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 think it needs an onFail:
clause to invoke burnLosses
@@ -335,7 +337,7 @@ export const createSeatManager = ( | |||
); | |||
return amountKeywordRecord; | |||
}, | |||
clear: ignoreContext(clear), | |||
clear: ({ self }) => clear(self), |
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 think MarkM hinted at the right choice here. Please inline the method, so it'll be more apparent that the parameter handling is parallel to the sibling methods.
It led to strange failures, at least in a test.
48c101b
to
7f0f8fd
Compare
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.
LGTM given #6116 (which I've added to PSM audit release)
E(publicFacet).makeWantStableInvitation(), | ||
harden({ | ||
give: { In: giveAnchor }, | ||
...(wantStable ? { want: { Out: wantStable } } : {}), |
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 like this. It's a definite improvement on my approach of making the proposal, and then adding the want later.
['give', 200, 190, false], | ||
['want', 101, 100, true, 1], | ||
['give', 50, 50, false], | ||
['give', 51, 50, true, 1], |
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 get what this is doing now, but only because of side conversations about ava macros. It would be nice if it said that the first line was intended to fail because the PSM doesn't have any anchor until after a want
completes. Does the third fail because it doesn't include fees?
closes: #6096
Description
When an attempt to trade IST for anchor/reference token fails due to lack of anchor funds in the PSM, seat stagings were left in an inconsistent state, preventing further service. This fix clears the seat stagings on failure.
@erights @Chris-Hibbert this also includes a couple zoe tweaks that @dtribble helped me with.
Security Considerations
availability is good :)
Testing Considerations
added a reasonably thorough unit test