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

8445 add promiseWatcher to smartWallet #8488

Merged
merged 1 commit into from
Jan 5, 2024
Merged

8445 add promiseWatcher to smartWallet #8488

merged 1 commit into from
Jan 5, 2024

Conversation

Chris-Hibbert
Copy link
Contributor

closes: #8445

Description

Use watchedPromises to monitor expected payouts. This made it necessary to be able to call several functions from more than one location, so a lot of state and behavior that was ephemeral was made durable. I think the code is simpler now.

along the way payments.tryReclaimingWithdrawnPayments() went away. I'm pretty sure it was redundant with ERTP recoverySets. If there was other functionality that we should keep, please let me know.

Security Considerations

offer security, not operational security. Without this fix, wallets wouldn't be able to pay out if the Zoe vat upgraded.

Scaling Considerations

Repairs all the outstanding seats for each smartWallet on upgrade.

Documentation Considerations

not applicable

Testing Considerations

TBD. Not done yet.

Upgrade Considerations

It's all about upgrade.

/** @type {[PromiseRejectedResult]} */
// @ts-expect-error cast
const result = status.result;
t.is(result[0].status, 'rejected');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The previous code just happened to call updateStatus on a Promise.all() that covered this case. We get the same results without the Promise.all(), so there's nothing to log at that point. The 'rejected' status is redundant with the error detected just above.

Copy link
Member

Choose a reason for hiding this comment

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

This is a breaking change and why test-oracle-integration is failing. When this code handles a rejection:

facets.helper.updateStatus({ error: err.toString() });
it's not making it to this error handler:
} catch (err) {
console.error('OFFER ERROR:', err);

And so it's not getting thrown

to the executeOffer caller.

It's a smell that there are two error handlers which both do updateStatus({ error: err.toString() });. Let's see if we can refactor this to have one error handler that throws and executeOffer() waits for the offer to complete.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this 'rejected' status came from tryReclaimingWithdrawnPayments(), which failed because the error happened before there was a seat. insufficientFunds was detected while attempting to create the payment to send to Zoe, so Zoe was never called.

The new approach doesn't attempt to collect those payments, so the rejected status is never set, which I think is fine. status.error tells all that needs to be understood.

test-oracle-integration now passes because of your fixes that pass the error through executeOffer.

@Chris-Hibbert Chris-Hibbert force-pushed the 8445-addWatcher branch 2 times, most recently from 9c8ec51 to 6730e01 Compare November 29, 2023 17:51
packages/agoric-cli/src/commands/auction.js Outdated Show resolved Hide resolved
packages/boot/test/bootstrapTests/liquidation.ts Outdated Show resolved Hide resolved
'open-vault',
),

// does not throw, because it's in the result, but it will show up in errors
Copy link
Member

Choose a reason for hiding this comment

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

this is a breaking change to the API. I don't think it's necessary. Let's discuss.

LiquidationSetup,
} from './liquidation.ts';

const test = anyTest as TestFn<LiquidationTestContext>;

//#region Product spec
const setup: LiquidationSetup = {
Copy link
Member

Choose a reason for hiding this comment

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

why is LiquidationSetup in a test about walletSurvivesZoeRestart?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in order to test that the wallets survive, it's using a vaultLiquidation scenario to exercise the vaults.

packages/smart-wallet/src/smartWallet.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/smartWallet.js Outdated Show resolved Hide resolved
packages/store/package.json Outdated Show resolved Hide resolved
packages/wallet/api/src/lib-wallet.js Outdated Show resolved Hide resolved
@@ -6,7 +6,7 @@
*/

import { makeHelpers } from '@agoric/deploy-script-support';
import { getManifestForUpgrade } from '@agoric/smart-wallet/src/proposals/upgrade-walletFactory-proposal.js';
import { getManifestForUpgrade } from '@agoric/vats/src/proposals/upgrade-wallet-factory-proposal.js';
Copy link
Member

Choose a reason for hiding this comment

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

In master I see the file packages/smart-wallet/src/proposals/upgrade-walletFactory-proposal.js and when I run agoric run scripts/smart-wallet/build-walletFactory-upgrade.js it works.

Please revert moving the file. The proposal is about wallet-factory so belongs in smart-wallet.

packages/smart-wallet/src/invitations.js Outdated Show resolved Hide resolved
packages/smart-wallet/test/test-addAsset.js Outdated Show resolved Hide resolved
packages/smart-wallet/test/test-addAsset.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/offerWatcher.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/smartWallet.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/smartWallet.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/smartWallet.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/smartWallet.js Show resolved Hide resolved
const buildAndExecuteProposal = async packageSpec => {
const buyer = await walletFactoryDriver.provideSmartWallet('agoric1buyer');

const buildAndExecuteProposal = async (packageSpec: string) => {
Copy link
Member

Choose a reason for hiding this comment

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

Out of scope but we should move this to SwingsetTestKit

Copy link
Member

Choose a reason for hiding this comment

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

take care to pass I/O access explicitly when doing so.

packages/smart-wallet/package.json Outdated Show resolved Hide resolved
@@ -218,6 +249,12 @@ export const prepareSmartWallet = (baggage, shared) => {
invitationDisplayInfo: DisplayInfoShape,
publicMarshaller: M.remotable('Marshaller'),
zoe: M.eref(M.remotable('ZoeService')),

// known only to smartWallets and walletFactory, this allows the
Copy link
Member

Choose a reason for hiding this comment

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

clever

purseForBrand: M.call(BrandShape).returns(M.promise()),
logWalletInfo: M.call(M.any()).returns(),
logWalletError: M.call(M.any()).returns(),
// XXX is there a better guard for a bigMapStore?
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// XXX is there a better guard for a bigMapStore?
// XXX is there a tighter shape for a bigMapStore than M.any()?

fwiw I don't think so

packages/smart-wallet/src/walletFactory.js Outdated Show resolved Hide resolved
packages/smart-wallet/src/walletFactory.js Show resolved Hide resolved
packages/smart-wallet/src/walletFactory.js Show resolved Hide resolved
packages/smart-wallet/test/test-addAsset.js Outdated Show resolved Hide resolved
Copy link
Member

@turadg turadg left a comment

Choose a reason for hiding this comment

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

Please change the conventional commit tag from refactor to feat. It's new functionality. E.g.

feat: smartWallet v2 with watchedPromises

Chris-Hibbert added a commit that referenced this pull request Dec 15, 2023
* test: support for watchedPromises in fakeVirtualSupport

Needed by #8488

* chore: update allocatePromiseID to use nextExportID

* chore: distinct ID range for proises
@Chris-Hibbert
Copy link
Contributor Author

@dckc, are you willing/able to approve this PR for merging. I believe I've addressed all the requested changes.

@Chris-Hibbert Chris-Hibbert force-pushed the 8445-addWatcher branch 3 times, most recently from 3b6db32 to 50b525c Compare January 4, 2024 19:00
Copy link
Member

@turadg turadg left a comment

Choose a reason for hiding this comment

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

I left some non-blocking feedback. Should be squashed into one feat commit

packages/vats/package.json Outdated Show resolved Hide resolved
packages/smart-wallet/src/walletFactory.js Show resolved Hide resolved
purseForBrand: M.call(BrandShape).returns(M.promise()),
logWalletInfo: M.call().rest(M.arrayOf(M.any())).returns(),
logWalletError: M.call().rest(M.arrayOf(M.any())).returns(),
// XXX is there a tighter guard for a bigMapStore than M.any()?
Copy link
Member

Choose a reason for hiding this comment

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

there isn't, but there could be a shape for it. consider,

Suggested change
// XXX is there a tighter guard for a bigMapStore than M.any()?
// TODO define a shape for bigMapStore

even better, make an issue and mention it here

Copy link
Member

Choose a reason for hiding this comment

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

M.remotable() seems like the right shape.
or with a label such as M.remotable('mapStore').

return Far('mapStore', mapStore);

Copy link
Member

Choose a reason for hiding this comment

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

I assumed we could do better by including the actual properties (methods) of a mapStore. M.remotable() is incrementally better than M.any() but then the shape can't change later to one that is a local object with methods.

Copy link
Member

@dckc dckc Jan 4, 2024

Choose a reason for hiding this comment

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

Patterns only admit passables, such as a remotable or a copyRecord with properties (whose values are passable), but local objects with methods aren't passable, IIUC.

And even for a local Far() object, whether a pattern matches or not is pass-invariant, so you can't test its methods with patterns any more than you can for a remote presence.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

M.remotable('mapStore') is indeed better than M.any(). Thanks

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @dckc and sorry for steering your wrong @Chris-Hibbert . I neglected to think through it, and was yet was hasty in my certainty. I plead guilty by reason of fever.

Comment on lines 579 to 582
const { address, invitationPurse } = state;
const { liveOffers, liveOfferSeats } = state;
const { zoe, agoricNames } = shared;
const { invitationBrand, invitationIssuer } = shared;
Copy link
Member

Choose a reason for hiding this comment

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

I can't tell whether it was intentional to separate them. Please make these two lines or explain why it's four.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hate prettier.

It used to turn 2 lines here or 4 lines into 10 lines. Now it's happy with two lines per declaration. Hurrah!
But with longer names or more names, it will jump from 2 lines to 6, and I'll go back to multiple declarations, or not declaring them all, or declaring some later.

@Chris-Hibbert Chris-Hibbert added the automerge:rebase Automatically rebase updates, then merge label Jan 4, 2024
pulled offers.js and payments.js into smartWallet.js as they shared
plenty of state that needs to be durable in order to be callable from
the watchedPromise.

build an upgrade proposal; tested in
Agoric/agoric-3-proposals#34
@mergify mergify bot merged commit 376e47d into master Jan 5, 2024
67 checks passed
@mergify mergify bot deleted the 8445-addWatcher branch January 5, 2024 01:27
Chris-Hibbert added a commit that referenced this pull request Jan 16, 2024
* test: support for watchedPromises in fakeVirtualSupport

Needed by #8488

* chore: update allocatePromiseID to use nextExportID

* chore: distinct ID range for proises
mhofman pushed a commit that referenced this pull request Jan 19, 2024
* test: support for watchedPromises in fakeVirtualSupport

Needed by #8488

* chore: update allocatePromiseID to use nextExportID

* chore: distinct ID range for proises
mhofman pushed a commit that referenced this pull request Jan 19, 2024
* test: support for watchedPromises in fakeVirtualSupport

Needed by #8488

* chore: update allocatePromiseID to use nextExportID

* chore: distinct ID range for proises
anilhelvaci pushed a commit to Jorge-Lopes/agoric-sdk that referenced this pull request Mar 4, 2024
…ic#8772)

* test: support for watchedPromises in fakeVirtualSupport

Needed by Agoric#8488

* chore: update allocatePromiseID to use nextExportID

* chore: distinct ID range for proises
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:rebase Automatically rebase updates, then merge wallet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

vat-walletFactory upgrade would cause wallet to stop tracking existing offers
3 participants