-
Notifications
You must be signed in to change notification settings - Fork 214
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
chore(fusdc): settle ForwardFailed
, minted while Advancing
, and minted early txs
#10729
base: master
Are you sure you want to change the base?
Conversation
Deploying agoric-sdk with Cloudflare Pages
|
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.
a couple notes before our chat
} catch (e) { | ||
// settlement already received; tx will Forward. |
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.
assuming all exceptions have this meaning seems inconsistent with https://github.com/Agoric/agoric-sdk/wiki/Errors-and-Control-Flow
0c54b6f
to
443b5cc
Compare
log('⚠️ tap: minted before observed', nfa, amount); | ||
// XXX consider capturing in vstorage | ||
// we would need a new key, as this does not have a txHash | ||
this.state.mintedEarly.add(makeMintedEarlyKey(nfa, 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.
I realized while writing this PR desc:
The mintedEarly mapStore could grow large if an attacker spams the settlementAccount with uusdc
Worth adding a check here to see if the advance exceeds min fees? That should like help with 1uusdc spam.
@@ -206,19 +215,19 @@ export const prepareSettler = ( | |||
) { | |||
const { mintedEarly } = this.state; | |||
const { value: fullValue } = fullAmount; | |||
// XXX i think this only contains Advancing txs - should this be a separate store? |
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.
comment for reviewers, not something to check in
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.
what's the alternative? pros/cons?
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'd take some time to think of an attack vector - currently coming up short.
In general, they're pretty distinct behaviors / states so commingling didn't seem right.
Since we don't currently have sufficient motivation to separate, I'll leave as-is for now and remove the comment.
t.deepEqual(bridgeTraffic.local, [], 'no IBC transfers'); | ||
|
||
// activate oracles and submit evidence; expect Settler to forward (slow path) | ||
// 'C12 - Contract MUST only pay back the Pool (fees) only if they started the advance before USDC is minted', |
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 maybe not entirely relevant. We could adjust the test to fail (AdvanceFailed
), and we would not pay fees to the pool here. This is covered in settler.test.ts
, though.
const forwardInfo = JSON.parse(outgoingForwardMessage.memo).forward; | ||
t.is(forwardInfo.receiver, EUD, 'receiver is osmo10tt0'); | ||
|
||
// in lieu of transmitTransferAck so we can set a nonce that matches our initial Advance |
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.
considered parameterizing like: transmitTransferAck(-1)
but came up short. I think it's only the sequence that difference, not the index of localBridgeMessage
.
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.
here are some comments based on one read / skim
log('⚠️ transfer rejected!', reason, ctx); | ||
// const { txHash, nfa, amount } = ctx; | ||
onRejected(reason, txHash) { | ||
log('⚠️ transfer rejected!', reason, txHash); | ||
// TODO(#10510): statusManager.forwardFailed(txHash, nfa, 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.
#10510 is closed. should this TODO still be here?
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 this was removed in a later commit but will check again
// eslint-disable-next-line no-unused-vars | ||
log = makeTracer('Advancer', true), |
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 tracer shouldn't call itself 'Advancer'
.
out of scope of this PR, but since you're in the neighborhood...
@@ -69,6 +69,7 @@ export const prepareStatusManager = ( | |||
txnsNode, | |||
{ | |||
marshaller, | |||
// eslint-disable-next-line no-unused-vars |
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 should use the log/tracer, shouldn't we?
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.
There's nothing currently logging anything in statusManager
. vstorage seems to be the main way of communicating
* @param {EvmHash | undefined} txHash - undefined in case mint before observed | ||
* @param {NobleAddress} nfa | ||
* @param {bigint} amount | ||
* @param {EvmHash} txHash - undefined in case mint before observed |
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 "undefined in case..." docstring looks outdated
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 suppose the preconditions for forwarded(...)
are in a state transition diagram somewhere, but it would be nice to have them here locally.
In particular, this would throw if you call it twice on the same txHash, right?
const key = makeMintedEarlyKey(forwardingAddress, fullValue); | ||
if (mintedEarly.has(key)) { | ||
mintedEarly.delete(key); | ||
if (success) { | ||
// TODO: does not write `ADVANCED` to vstorage |
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.
TODO when? in 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.
#10750 but maybe will collapse into 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.
Closing 10750 and collapsing into this PR
status: M.or( | ||
PendingTxStatus.Advanced, | ||
PendingTxStatus.AdvanceFailed, | ||
PendingTxStatus.Observed, | ||
), | ||
status: M.or(...Object.values(PendingTxStatus)), |
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.
nice.
@@ -277,7 +273,10 @@ export const prepareStatusManager = ( | |||
}, | |||
|
|||
/** | |||
* Remove and return an `ADVANCED` or `OBSERVED` tx waiting to be `SETTLED`. | |||
* Remove and return the _first_ `PendingSettleTx` that matches the observed | |||
* settlement. This is not necessarily an exact match with the original |
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.
what does "not necessarily an exact match" mean? isn't "same forwarding account and amount" the relevant definition of "matching"?
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 ordering could be different if Alice requests two advances for the same 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.
It is an exact match based on available data, which treats (EUD,Amount) interchangeably.
I think think what you're getting at is that a mint deposit may be for any of the matching transactions and we can't distinguish between them, so we go with the first.
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.
How's this:
/**
* Remove and return the oldest pending settlement transaction that matches the given
* forwarding account and amount. Since multiple pending transactions may exist with
* identical (account, amount) pairs, we process them in FIFO order.
*/
]); | ||
}); | ||
|
||
test.todo('create facet methods'); |
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.
creator facet?
txHash, | ||
} = evidence; | ||
|
||
const { borrowerFacet, notifyFacet, poolAccount } = this.state; |
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.
sometime before this PR, but I'm surprised to see Facet
in the name of the object. the consumer needn't be concerned with the structure in which the object resides.
I've added this to revisit in #10432
if ( | ||
notifyFacet.forwardIfMinted( | ||
destination, | ||
forwardingAddress, | ||
fullAmount, | ||
txHash, | ||
) | ||
) { |
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.
surprising to see the condition be the result of an action. please make more clear.
if ( | |
notifyFacet.forwardIfMinted( | |
destination, | |
forwardingAddress, | |
fullAmount, | |
txHash, | |
) | |
) { | |
const forwarded = notifyFacet.forwardIfMinted( | |
destination, | |
forwardingAddress, | |
fullAmount, | |
txHash, | |
); | |
if (forwarded)) { |
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 name was weird to me and as I get through more of the review, I understand better why. The notify
facet shouldn't be telling the settler what to do.
What is it notifying it of? It's telling the settler about a transaction and it needs to know what decision the settler has to say about it. So I think this should be,
if ( | |
notifyFacet.forwardIfMinted( | |
destination, | |
forwardingAddress, | |
fullAmount, | |
txHash, | |
) | |
) { | |
const forwarded = notifyFacet.checkForwarded( | |
destination, | |
forwardingAddress, | |
fullAmount, | |
txHash, | |
); | |
if (forwarded) { | |
return; | |
} |
I took out the logging too because the settler already does that. I'm not as confident in that change.
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 was tricky to name since "checkForwarded" does more than checking - it actually starts a Forward via MsgTransfer if conditions are met. That's why i wanted to include a verb.
I've heeded your suggestion, and renamed to checkIfMinted
. (the forward hasn't occurred yet, and the early mint is the thing we're checking)
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 actually starts a Forward via MsgTransfer if conditions are met. That's why i wanted to include a verb.
It happens to but needn't be a commitment to the caller. The caller is providing information and asking for a state.
@@ -277,7 +273,10 @@ export const prepareStatusManager = ( | |||
}, | |||
|
|||
/** | |||
* Remove and return an `ADVANCED` or `OBSERVED` tx waiting to be `SETTLED`. | |||
* Remove and return the _first_ `PendingSettleTx` that matches the observed | |||
* settlement. This is not necessarily an exact match with the original |
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 is an exact match based on available data, which treats (EUD,Amount) interchangeably.
I think think what you're getting at is that a mint deposit may be for any of the matching transactions and we can't distinguish between them, so we go with the first.
txHash, | ||
) | ||
) { | ||
// settlement already received; tx will Forward. |
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's not "will Forward" because that has started at this point. I don't think this comment adds enough over the log message and the if (forwarded)
branch
// settlement already received; tx will Forward. |
@@ -206,19 +215,19 @@ export const prepareSettler = ( | |||
) { | |||
const { mintedEarly } = this.state; | |||
const { value: fullValue } = fullAmount; | |||
// XXX i think this only contains Advancing txs - should this be a separate store? |
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.
what's the alternative? pros/cons?
} else { | ||
// TODO: does not write `ADVANCE_FAILED` to vstorage |
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.
// TODO: does not write `ADVANCE_FAILED` to vstorage | |
// FIXME: does not write `ADVANCE_FAILED` to vstorage |
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.
TODO will appear in commit history, but not the final HEAD of this PR
08bf844
to
3a0afe7
Compare
* @param {EvmHash} txHash | ||
* @param {boolean} success whether the Transfer succeeded | ||
*/ | ||
advanceOutcomeForSettled(txHash, success) { |
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 crazy about advanceOutcomeForSettled
, but implemented the suggestion.
I liked notify
since all we're doing here is a vstorage write. Technically, it's not settled yet (we could say minted).
Where does that leave us with notifyObserved
?
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 liked notify since all we're doing here is a vstorage write.
That shouldn't be a commitment to the caller. The caller tells the status manager about stuff and the status manager abstracts what it does with that information.
Technically, it's not settled yet (we could say minted).
Yeah that would be better.
Where does that leave us with notifyObserved?
To be renamed for the same reasons
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 landed on advanceOutcomeForMintedEarly
and advanceOutcomeForUnknownMint
. If we want something more concise, i think we can tackle that in #10432
3a0afe7
to
3e111dc
Compare
- verify poolAccount is sender for Advances - verify Advance is Disbursed in happy path - verify pool and settlement account addresses (better visibility in active dev)
- Advancing txs are dequeueable so `case PendingTxStatus.Advancing:` is reachable - update Settler tests to use `notifyAdvancing` result to simulate an advance - add Settler test for minting while advancing (Advanced + AdvanceFailed paths)
- if Forward (slow transfer) fails, capture the state to distinguish from successful forwards
- Advancer calls `forwardIfMinted` to check for an early matching mint - if found, no Advance occurs and the minted funds are forwarded
3e111dc
to
9367164
Compare
- ensure "minted before observed" `Observe` statuses are recorded in vstorage - ensure "minted while Advancing" `Advanced` and `AdvanceFailed` statuses are recorded in vstorage
9367164
to
9c01dc4
Compare
ForwardFailed
and Advancing
txsForwardFailed
, minted while Advancing
, and minted early txs
closes: #10625
Description
Advancing
" results inDisburse
orForward
Settler.forwardIfMinted()
called byAdvancer
ForwardFailed
(terminal) state ifForward
failsSecurity Considerations
None new introduced, these behaviors are consistent with the product spec.
Scaling Considerations
The
mintedEarly
mapStore could grow large if an attacker spams thesettlementAccount
with uusdcDocumentation Considerations
None
Testing Considerations
Includes tests for all new behaviors.
Upgrade Considerations
Targeting FUSDC's first release