Skip to content

Commit

Permalink
Merge pull request #6134 from Agoric/6127-psm-new-wallet
Browse files Browse the repository at this point in the history
Wallet UI working with new Smart Wallet contract
  • Loading branch information
mergify[bot] authored Sep 15, 2022
2 parents 7aa443d + d0d5d00 commit a390655
Show file tree
Hide file tree
Showing 27 changed files with 232 additions and 345 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
cli: [link-cli, local-npm]
# FIXME: Use this to enable NPM deploys...
# cli: [link-cli, local-npm]
cli: [link-cli]
steps:
- uses: actions/checkout@v2

Expand Down
3 changes: 3 additions & 0 deletions packages/casting/src/change-follower.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,8 @@ export const makePollingChangeFollower = async leader => {
return Far('polling change follower', {
getLatestIterable: async () => iterable,
getEachIterable: async () => iterable,
getReverseIterable: async () => {
throw Error('not implemented for polling change follower');
},
});
};
45 changes: 43 additions & 2 deletions packages/casting/src/follower-cosmjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,21 @@ export const makeCosmjsFollower = (
}
}

/**
* @param {StreamCell<T>} streamCell
* @param {number} currentBlockHeight
* @yields {FollowerElement<T>}
*/
function* reverseValuesFromCell(streamCell, currentBlockHeight) {
for (let i = streamCell.values.length - 1; i >= 0; i -= 1) {
yield followerElementFromStreamCellValue(
streamCell.values[i],
streamCell.blockHeight,
currentBlockHeight,
);
}
}

/**
* @param {StreamCell<T>} streamCell
* @param {number} currentBlockHeight
Expand Down Expand Up @@ -444,7 +459,6 @@ export const makeCosmjsFollower = (
// If the block has no corresponding data, wait for the first block to
// contain data.
for (;;) {
cursorBlockHeight = await getBlockHeight();
cursorData = await getDataAtHeight(cursorBlockHeight);
if (cursorData.length !== 0) {
const cursorStreamCell = streamCellForData(
Expand All @@ -457,6 +471,7 @@ export const makeCosmjsFollower = (
// TODO Long-poll for next block
// https://github.com/Agoric/agoric-sdk/issues/6154
await E(leader).jitter(where);
cursorBlockHeight = await getBlockHeight();
}

// For each subsequent iteration, yield every value that has been
Expand Down Expand Up @@ -551,7 +566,27 @@ export const makeCosmjsFollower = (
}
}

// Enable the periodic fetch.
/**
* @param {number} cursorBlockHeight
* @yields {FollowerElement<T>}
*/
async function* getReverseIterableAtHeight(cursorBlockHeight) {
// Track the data for the last emitted cell (the cell at the
// cursorBlockHeight) so we know not to emit duplicates
// of that cell.
let cursorData;
while (cursorBlockHeight > 0) {
cursorData = await getDataAtHeight(cursorBlockHeight);
if (cursorData.length === 0) {
// No data at the cursor height, so signal beginning of stream.
return;
}
const cursorStreamCell = streamCellForData(cursorBlockHeight, cursorData);
yield* reverseValuesFromCell(cursorStreamCell, cursorBlockHeight);
cursorBlockHeight = cursorStreamCell.blockHeight - 1;
}
}

/** @type {Follower<FollowerElement<T>>} */
return Far('chain follower', {
async getLatestIterable() {
Expand All @@ -563,5 +598,11 @@ export const makeCosmjsFollower = (
}
return getEachIterableAtHeight(height);
},
async getReverseIterable({ height = undefined } = {}) {
if (height === undefined) {
height = await getBlockHeight();
}
return getReverseIterableAtHeight(height);
},
});
};
6 changes: 6 additions & 0 deletions packages/casting/src/follower.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ const makeSubscriptionFollower = spec => {
}
return mapAsyncIterable(ai, transform);
},

getReverseIterable: async () => {
throw Error(
'reverse iteration not implemented for subscription follower',
);
},
});
return follower;
};
Expand Down
18 changes: 18 additions & 0 deletions packages/casting/src/iterable.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,21 @@ export const iterateEach = (follower, options) =>
});
},
});

/**
* @template T
* @param {ERef<import('./types.js').Follower<T>>} follower
* @param {import('./types.js').IterateEachOptions} [options]
*/
export const iterateReverse = (follower, options) =>
// For now, just pass through the iterable.
Far('iterateReverse iterable', {
/** @returns {AsyncIterator<T>} */
[Symbol.asyncIterator]: () => {
const eachIterable = E(follower).getReverseIterable(options);
const iterator = E(eachIterable)[Symbol.asyncIterator]();
return Far('iterateEach iterator', {
next: () => E(iterator).next(),
});
},
});
1 change: 1 addition & 0 deletions packages/casting/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export {};
* @typedef {object} Follower
* @property {() => Promise<AsyncIterable<T>>} getLatestIterable
* @property {(options?: IterateEachOptions) => Promise<AsyncIterable<T>>} getEachIterable
* @property {(options?: IterateEachOptions) => Promise<AsyncIterable<T>>} getReverseIterable
*/

/**
Expand Down
7 changes: 6 additions & 1 deletion packages/cosmic-swingset/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ scenario2-setup-nobuild:
$(AGCH) --home=t1/bootstrap keys add bootstrap --keyring-backend=test
$(AGCH) --home=t1/bootstrap keys show -a bootstrap --keyring-backend=test > t1/bootstrap-address
$(AGCH) --home=t1/n0 add-genesis-account `cat t1/bootstrap-address` $(BOOT_COINS)
# Create
# Create the (singleton) chain node.
$(AGCH) --home=t1/n0 --keyring-dir=t1/bootstrap gentx --keyring-backend=test bootstrap 73000000ubld --chain-id=$(CHAIN_ID)
$(AGCH) --home=t1/n0 collect-gentxs
Expand Down Expand Up @@ -211,7 +212,11 @@ t1-provision-one-with-powers: wait-for-cosmos
$(AGCH) --home=t1/bootstrap tx swingset provision-one --keyring-backend=test --from=bootstrap \
--gas=auto --gas-adjustment=$(GAS_ADJUSTMENT) --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
t1/$(BASE_PORT) $$addr $(AGORIC_POWERS) -ojson | tee /dev/stderr | grep -q '"code":0'; }


# Send some USDC to the vbank/provision module account where it can be traded for IST.
fund-provision-pool: wait-for-cosmos
$(MAKE) ACCT_ADDR=agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346 SOLO_COINS=1234000000ibc/usdc1234 fund-acct

fund-acct: wait-for-cosmos
$(AGCH) \
--home=t1/bootstrap --keyring-backend=test --from=bootstrap \
Expand Down
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/smartWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ const finish = ({ state, facets }) => {
{
brand: desc.brand,
issuer: desc.issuer,
petname: desc.proposedName,
petname: desc.issuerName,
},
purse,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { observeIteration, subscribeEach } from '@agoric/notifier';
* If this proves to be a problem we can add an option to this or a related
* utility to reset state from RPC.
*
* @param {ERef<StoredSubscriber<import('./smartWallet').UpdateRecord>>} updates
* @param {ERef<Subscriber<import('./smartWallet').UpdateRecord>>} updates
*/
export const coalesceUpdates = updates => {
/** @type {Map<Brand, import('./smartWallet').BrandDescriptor>} */
Expand Down
3 changes: 1 addition & 2 deletions packages/smart-wallet/test/devices.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import bundleCentralSupply from '@agoric/vats/bundles/bundle-centralSupply.js';
import bundleMintHolder from '@agoric/vats/bundles/bundle-mintHolder.js';
import bundleWalletFactory from '@agoric/vats/bundles/bundle-legacy-walletFactory.js';
import bundleWalletFactory from '@agoric/vats/bundles/bundle-walletFactory.js';
import bundleProvisionPool from '@agoric/vats/bundles/bundle-provisionPool.js';

export const devices = {
Expand All @@ -12,7 +12,6 @@ export const devices = {
return bundleCentralSupply;
case 'mintHolder':
return bundleMintHolder;
// TODO(PS0) replace this bundle with the non-legacy smart-wallet
case 'walletFactory':
return bundleWalletFactory;
case 'provisionPool':
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/decentral-core-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"sourceSpec": "@agoric/wallet/contract/src/singleWallet.js"
},
"walletFactory": {
"sourceSpec": "@agoric/wallet/contract/src/walletFactory.js"
"sourceSpec": "@agoric/smart-wallet/src/walletFactory.js"
},
"zoe": {
"sourceSpec": "@agoric/vats/src/vat-zoe.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/decentral-demo-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"sourceSpec": "@agoric/wallet/contract/src/singleWallet.js"
},
"walletFactory": {
"sourceSpec": "@agoric/wallet/contract/src/walletFactory.js"
"sourceSpec": "@agoric/smart-wallet/src/walletFactory.js"
},
"zoe": {
"sourceSpec": "@agoric/vats/src/vat-zoe.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test:xs": "exit 0",
"lint-fix": "yarn lint:eslint --fix",
"lint": "run-s --continue-on-error lint:*",
"lint:types": "tsc --maxNodeModuleJsDepth 4 -p jsconfig.json",
"lint:types": "tsc --maxNodeModuleJsDepth 5 -p jsconfig.json",
"lint:eslint": "eslint ."
},
"keywords": [],
Expand Down
4 changes: 2 additions & 2 deletions packages/vats/scripts/build-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const sourceToBundle = [
`../bundles/bundle-singleWallet.js`,
],
[
`@agoric/wallet/contract/src/walletFactory.js`,
`../bundles/bundle-legacy-walletFactory.js`,
`@agoric/smart-wallet/src/walletFactory.js`,
`../bundles/bundle-walletFactory.js`,
],
];

Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/core/startWalletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Stable } from '../tokens.js';

/**
* @param {ERef<ZoeService>} zoe
* @param {Installation<import('@agoric/legacy-smart-wallet/src/walletFactory').start>} inst
* @param {Installation<import('@agoric/smart-wallet/src/walletFactory').start>} inst
* @typedef {Awaited<ReturnType<typeof startFactoryInstance>>} WalletFactoryStartResult
*/
// eslint-disable-next-line no-unused-vars
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/core/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
* interchainPool: Promise<Installation<import('@agoric/inter-protocol/src/interchainPool.js').start>>,
* mintHolder: Promise<Installation<import('@agoric/vats/src/mintHolder.js').start>>,
* singleWallet: Promise<Installation<import('@agoric/legacy-smart-wallet/src/singleWallet.js').start>>,
* walletFactory: Promise<Installation<import('@agoric/legacy-smart-wallet/src/walletFactory.js').start>>,
* walletFactory: Promise<Installation<import('@agoric/smart-wallet/src/walletFactory.js').start>>,
* },
* },
* instance:{
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/test/devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import bundlePSMCharter from '@agoric/inter-protocol/bundles/bundle-psmCharter.j
import bundleCentralSupply from '../bundles/bundle-centralSupply.js';
import bundleMintHolder from '../bundles/bundle-mintHolder.js';
import bundleSingleWallet from '../bundles/bundle-singleWallet.js';
import bundleWalletFactory from '../bundles/bundle-legacy-walletFactory.js';
import bundleWalletFactory from '../bundles/bundle-walletFactory.js';
import bundleProvisionPool from '../bundles/bundle-provisionPool.js';

const bundles = {
Expand Down
6 changes: 4 additions & 2 deletions packages/wallet/api/src/marshal-contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ const initSlotVal = (table, slot, val) => {

/**
* Make context for exporting wallet data where brands etc. can be recognized by boardId.
* Export for use outside the smart wallet.
*
* When serializing wallet state for, there's a tension between
*
Expand Down Expand Up @@ -236,7 +235,7 @@ const defaultMakePresence = iface => {
};

/**
* Make context for marshalling wallet or board data. To be imported into the client, which never makes objects.
* Make context for unserializing wallet or board data.
*
* @param {(iface: string) => unknown} [makePresence]
*/
Expand Down Expand Up @@ -295,6 +294,9 @@ export const makeImportContext = (makePresence = defaultMakePresence) => {
* @param {string} iface
*/
fromMyWallet: (slot, iface) => {
if (!slot) {
return makePresence('dummy');
}
const { kind, id } = parseWalletSlot(walletObjects, slot);
return kind
? provideVal(walletObjects[kind], id, iface)
Expand Down
57 changes: 3 additions & 54 deletions packages/wallet/contract/README.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,5 @@
# Smart Wallet contracts
# Legacy Smart Wallet contract

## Single contract
This used to have the multi-tenant wallet but that moved to `packages/smart-wallet`. Now `walletFactory` refers to that one.

The `singleWalet` contract manages a single smart wallet.

# Multi-tenant contract

The `walletFactory` contract provisions and manages smart wallets.

## Common

There can be zero or one wallets per Cosmos address.

lib-wallet has makeWallet but that's really makeWalletKit

1. Generate an address (off-chain)
2. Provision an account using that address, which causes a Bank to get created
??? What happens if you try to provision again using the same address? It's a Cosmos level transaction; maybe that fails.
3. Create a Wallet using the Bank (it includes the implementation of Virtual Purses so when you getAmount it goes down to the Golang layer)
??? What happens if you try to create another wallet using that bank?

1 Address : 0/1 Bank
1 Address : 1 `myAddressNamesAdmin`
1 Bank : 0/1 Wallet

By design there's a 1:1 across all four.

`namesByAddress` and `board` are shared by everybody.

`myAddressNamesAdmin` is from the account you provision.

# Testing
There are no automated tests yet verifying the smart wallet running on chain. Here are procedures you can use instead.

## Notifiers

```
# tab 1 (chain)
cd packages/cosmic-swingset/
make scenario2-setup scenario2-run-chain
# starts bare chain, don’t need AMM
# tab 2 (client server)
cd packages/cosmic-swingset/
make scenario2-run-client
# confirm no errors in logs
# tab 3 (interactive)
agoric open --repl
# confirm in browser that `home.wallet` and `home.smartWallet` exist
agd query vstorage keys 'published.wallet'
# confirm it has a key like `published.wallet.agoric1nqxg4pye30n3trct0hf7dclcwfxz8au84hr3ht`
agoric follow :published.wallet.agoric1nqxg4pye30n3trct0hf7dclcwfxz8au84hr3ht
# confirm it has JSON data
```
This package retains `singleWallet` which is a contract-wrapper around the solo wallet. That too is not long for this life.
Loading

0 comments on commit a390655

Please sign in to comment.