Skip to content

Commit

Permalink
transaction vstorage (#10633)
Browse files Browse the repository at this point in the history
refs: #10598

## Description
The dashboard will requires data for each transaction. In the course of designing that we explored how to design the storage node paths without creating too much cost in IAVL. (The data in HEAD carry a burden of magnitude O(k+v) for all network nodes (including non-validators) into perpetuity.)

Examining [the trade-offs](https://alpha.decidedly.co/d/b27cbb6a-df71-4136-aa27-3e947015e84b/view) we arrived at:
- store one node per transaction
  - push all changes through it
  - rely on indexer to demux
- delete on demand
  - when we have [an API for block-safe deletion](#7405) it can delete in the operation
  - until then keep track of what to delete
  - a core-eval can be run to cull data whenever necessary


### Security Considerations
none

### Scaling Considerations
One storage path per transaction, which persists until the deletion job is called.

One entry in a set to keep track of transactions to delete.

### Documentation Considerations
Not developer facing

### Testing Considerations
CI

### Upgrade Considerations
not yet deployed
  • Loading branch information
mergify[bot] authored Dec 17, 2024
2 parents 9835fb0 + ccb9e28 commit a2813f6
Show file tree
Hide file tree
Showing 29 changed files with 2,624 additions and 272 deletions.
2 changes: 2 additions & 0 deletions multichain-testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Install the relevant dependencies:
yarn install
```

(Note that the '@agoric/*' deps will come from the parent directory due to `yarn link --relative .. --all`)

Ensure you have Kubernetes available. See https://docs.cosmology.zone/starship/get-started/step-2.

The following will install `kubectl`, `kind`, `helm`, and `yq` as needed:
Expand Down
50 changes: 49 additions & 1 deletion multichain-testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"packageManager": "[email protected]",
"devDependencies": {
"@agoric/cosmic-proto": "dev",
"@agoric/fast-usdc": "dev",
"@cosmjs/crypto": "^0.32.4",
"@cosmjs/proto-signing": "^0.32.4",
"@cosmjs/stargate": "^0.32.4",
Expand All @@ -41,7 +42,54 @@
"typescript": "~5.7.2"
},
"resolutions": {
"axios": "1.6.7"
"axios": "1.6.7",
"@agoric/cosmos": "portal:../golang/cosmos",
"@agoric/ertp": "portal:../packages/ERTP",
"@agoric/swingset-vat": "portal:../packages/SwingSet",
"@agoric/access-token": "portal:../packages/access-token",
"agoric": "portal:../packages/agoric-cli",
"@agoric/async-flow": "portal:../packages/async-flow",
"@agoric/base-zone": "portal:../packages/base-zone",
"@agoric/builders": "portal:../packages/builders",
"@agoric/cache": "portal:../packages/cache",
"@agoric/casting": "portal:../packages/casting",
"@agoric/client-utils": "portal:../packages/client-utils",
"@agoric/cosmic-proto": "portal:../packages/cosmic-proto",
"@agoric/cosmic-swingset": "portal:../packages/cosmic-swingset",
"@agoric/create-dapp": "portal:../packages/create-dapp",
"@agoric/deploy-script-support": "portal:../packages/deploy-script-support",
"@agoric/eslint-config": "portal:../packages/eslint-config",
"@agoric/fast-usdc": "portal:../packages/fast-usdc",
"@agoric/governance": "portal:../packages/governance",
"@agoric/import-manager": "portal:../packages/import-manager",
"@agoric/inter-protocol": "portal:../packages/inter-protocol",
"@agoric/internal": "portal:../packages/internal",
"@agoric/kmarshal": "portal:../packages/kmarshal",
"@agoric/network": "portal:../packages/network",
"@agoric/notifier": "portal:../packages/notifier",
"@agoric/orchestration": "portal:../packages/orchestration",
"@agoric/pegasus": "portal:../packages/pegasus",
"@agoric/smart-wallet": "portal:../packages/smart-wallet",
"@agoric/solo": "portal:../packages/solo",
"@agoric/sparse-ints": "portal:../packages/sparse-ints",
"@agoric/spawner": "portal:../packages/spawner",
"@agoric/stat-logger": "portal:../packages/stat-logger",
"@agoric/store": "portal:../packages/store",
"@agoric/swing-store": "portal:../packages/swing-store",
"@agoric/swingset-liveslots": "portal:../packages/swingset-liveslots",
"@agoric/swingset-xsnap-supervisor": "portal:../packages/swingset-xsnap-supervisor",
"@agoric/telemetry": "portal:../packages/telemetry",
"@agoric/time": "portal:../packages/time",
"@agoric/vat-data": "portal:../packages/vat-data",
"@agoric/vats": "portal:../packages/vats",
"@agoric/vm-config": "portal:../packages/vm-config",
"@agoric/vow": "portal:../packages/vow",
"@agoric/wallet": "portal:../packages/wallet",
"@agoric/wallet-backend": "portal:../packages/wallet/api",
"@agoric/xsnap": "portal:../packages/xsnap",
"@agoric/xsnap-lockdown": "portal:../packages/xsnap-lockdown",
"@agoric/zoe": "portal:../packages/zoe",
"@agoric/zone": "portal:../packages/zone"
},
"eslintConfig": {
"root": true,
Expand Down
40 changes: 32 additions & 8 deletions multichain-testing/test/fast-usdc/fast-usdc.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import anyTest from '@endo/ses-ava/prepare-endo.js';

import type { TestFn } from 'ava';
import { encodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
import { AmountMath } from '@agoric/ertp';
Expand All @@ -13,6 +14,13 @@ import { commonSetup, type SetupContextWithWallets } from '../support.js';
import { makeFeedPolicy, oracleMnemonics } from './config.js';
import { makeRandomDigits } from '../../tools/random.js';
import { balancesFromPurses } from '../../tools/purse.js';
import { makeTracer } from '@agoric/internal';
import type {
CctpTxEvidence,
EvmAddress,
} from '@agoric/fast-usdc/src/types.js';

const log = makeTracer('MCFU');

const { keys, values, fromEntries } = Object;
const { isGTE, isEmpty, make } = AmountMath;
Expand Down Expand Up @@ -153,6 +161,7 @@ test.serial('oracles accept', async t => {
return offerToUsedInvitation[0][0] === `${name}-accept`;
},
`${name} invitation used`,
{ log },
),
);
}
Expand Down Expand Up @@ -191,6 +200,7 @@ test.serial('lp deposits', async t => {
({ shareWorth }) =>
!isGTE(currShareWorth.numerator, shareWorth.numerator),
'share worth numerator increases from deposit',
{ log },
),
);

Expand All @@ -203,6 +213,7 @@ test.serial('lp deposits', async t => {
return currentPoolShares && isGTE(currentPoolShares, poolSharesWanted);
},
'lp has pool shares',
{ log },
),
);
});
Expand All @@ -216,6 +227,7 @@ const advanceAndSettleScenario = test.macro({
nobleAgoricChannelId,
oracleWds,
retryUntilCondition,
smartWalletKit,
useChain,
usdcOnOsmosis,
vstorageClient,
Expand Down Expand Up @@ -250,15 +262,15 @@ const advanceAndSettleScenario = test.macro({
);
t.log('got forwardingAddress', userForwardingAddr);

// TODO export CctpTxEvidence type
const evidence = harden({
const evidence: CctpTxEvidence = harden({
blockHash:
'0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037663n,
txHash: `0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff3875527617${makeRandomDigits(makeRandomNumber(), 2n)}`,
tx: {
amount: mintAmt,
forwardingAddress: userForwardingAddr,
sender: '0x9a9eE9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9' as EvmAddress,
},
aux: {
forwardingChannel: nobleAgoricChannelId,
Expand All @@ -267,7 +279,7 @@ const advanceAndSettleScenario = test.macro({
chainId: 42161,
});

console.log('User initiates evm mint:', evidence.txHash);
log('User initiates evm mint:', evidence.txHash);

// submit evidences
await Promise.all(
Expand Down Expand Up @@ -299,25 +311,35 @@ const advanceAndSettleScenario = test.macro({
),
);

const queryTxStatus = async () =>
vstorageClient.queryData(
`published.${contractName}.status.${evidence.txHash}`,
const queryTxStatus = async () => {
const record = await smartWalletKit.readPublished(
`fastUsdc.txns.${evidence.txHash}`,
);
if (!record) {
throw new Error(`no record for ${evidence.txHash}`);
}
// @ts-expect-error unknown may not have 'status'
if (!record.status) {
throw new Error(`no status for ${evidence.txHash}`);
}
// @ts-expect-error still unknown?
return record.status;
};

const assertTxStatus = async (status: string) =>
t.notThrowsAsync(() =>
retryUntilCondition(
() => queryTxStatus(),
txStatus => {
console.log('tx status', txStatus);
log('tx status', txStatus);
return txStatus === status;
},
`${evidence.txHash} is ${status}`,
),
);

await assertTxStatus('ADVANCED');
console.log('Advance completed, waiting for mint...');
log('Advance completed, waiting for mint...');

nobleTools.mockCctpMint(mintAmt, userForwardingAddr);
await t.notThrowsAsync(() =>
Expand Down Expand Up @@ -393,6 +415,7 @@ test.serial('lp withdraws', async t => {
return !currentPoolShares || isEmpty(currentPoolShares);
},
'lp no longer has pool shares',
{ log },
),
);

Expand All @@ -404,6 +427,7 @@ test.serial('lp withdraws', async t => {
BigInt(balance.amount) - BigInt(currentUSDCBalance!.amount!) >
LP_DEPOSIT_AMOUNT,
"lp's USDC balance increases",
{ log },
),
);
});
Expand Down
33 changes: 27 additions & 6 deletions multichain-testing/tools/e2e-tools.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check
/** global harden */
import { makeSmartWalletKit, LOCAL_CONFIG } from '@agoric/client-utils';
import { assert } from '@endo/errors';
import { E, Far } from '@endo/far';
import { Nat } from '@endo/nat';
Expand All @@ -9,12 +10,16 @@ import { makeHttpClient, makeAPI } from './makeHttpClient.js';
import { dedup, makeQueryKit, poll } from './queryKit.js';
import { makeVStorage } from './batchQuery.js';
import { makeRetryUntilCondition } from './sleep.js';
import { makeTracer } from '@agoric/internal';

/**
* @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js';
* @import { EnglishMnemonic } from '@cosmjs/crypto';
* @import { RetryUntilCondition } from './sleep.js';
*/

const trace = makeTracer('E2ET');

const BLD = '000000ubld';

export const txAbbr = tx => {
Expand Down Expand Up @@ -223,7 +228,7 @@ export const provisionSmartWallet = async (
const txInfo = await sendAction({ method: 'executeOffer', offer });
console.debug('spendAction', txInfo);
for await (const update of updates) {
// console.log('update', address, update);
trace('update', address, update);
if (update.updated !== 'offerStatus' || update.status.id !== offer.id) {
continue;
}
Expand Down Expand Up @@ -355,7 +360,7 @@ const voteLatestProposalAndWait = async ({
await blockTool.waitForBlock(1, { step: `voting`, on: lastProposalId })
) {
info = await agd.query(['gov', 'proposal', lastProposalId]);
console.log(
trace(
`Waiting for proposal ${lastProposalId} to pass (status=${info.status})`,
);
}
Expand Down Expand Up @@ -398,7 +403,7 @@ const runCoreEval = async (

const evalPaths = evals.map(e => [e.permit, e.code]).flat();
log(evalPaths);
console.log('await tx', evalPaths);
trace('await tx', evalPaths);
const result = await agd.tx(
[
'gov',
Expand All @@ -413,7 +418,7 @@ const runCoreEval = async (
// FIXME TypeError#1: unrecognized details 0
// assert(result.code, 0);

console.log('await voteLatestProposalAndWait', evalPaths);
trace('await voteLatestProposalAndWait', evalPaths);
const detail = await voteLatestProposalAndWait({ agd, blockTool });
log(detail.proposal_id, detail.voting_end_time, detail.status);

Expand All @@ -428,6 +433,8 @@ const runCoreEval = async (
};

/**
* @deprecated use `@agoric/client-utils` instead
*
* @param {typeof console.log} log
* @param {import('@agoric/swingset-vat/tools/bundleTool.js').BundleCache} bundleCache
* @param {object} io
Expand Down Expand Up @@ -462,7 +469,7 @@ export const makeE2ETools = async (
if (typeof info === 'object' && Object.keys(info).length > 0) {
// XXX normally we have the caller pass in the log function
// later, but the way blockTool is factored, we have to supply it early.
console.log({ ...info, delay: ms / 1000 }, '...');
trace({ ...info, delay: ms / 1000 }, '...');
}
return delay(ms);
};
Expand Down Expand Up @@ -531,10 +538,23 @@ export const makeE2ETools = async (

const copyFiles = makeCopyFiles({ execFileSync, log });

/**
* @deprecated use `@agoric/client-utils` instead
*/
const vstorageClient = makeQueryKit(vstorage).query;

/** @type {import('@agoric/client-utils').SmartWalletKit} */
const smartWalletKit = await makeSmartWalletKit(
{
fetch,
delay,
},
LOCAL_CONFIG,
);

return {
vstorageClient,
smartWalletKit,
installBundles,
runCoreEval: buildAndRunCoreEval,
/**
Expand Down Expand Up @@ -605,8 +625,9 @@ export const seatLike = updates => {
});
};

/** @param {Awaited<ReturnType<provisionSmartWallet>>} wallet */
/** @param {Awaited<ReturnType<typeof provisionSmartWallet>>} wallet */
export const makeDoOffer = wallet => {
/** @type {(offer: OfferSpec) => Promise<void>} */
const doOffer = async offer => {
const updates = wallet.offers.executeOffer(offer);
// const seat = seatLike(updates);
Expand Down
3 changes: 2 additions & 1 deletion multichain-testing/tools/noble-tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { IBCChannelID } from '@agoric/vats';
import type { ExecSync } from './agd-lib.js';
import type { ChainAddress } from '@agoric/orchestration';
import type { NobleAddress } from '@agoric/fast-usdc/src/types.js';

const kubectlBinary = 'kubectl';
const noblePod = 'noblelocal-genesis-0';
Expand Down Expand Up @@ -82,7 +83,7 @@ export const makeNobleTools = (
const queryForwardingAddress = (
channelId: IBCChannelID,
address: ChainAddress['value'],
): { address: string; exists: boolean } => {
): { address: NobleAddress; exists: boolean } => {
checkEnv();
log('querying forwarding address', address, channelId);
return JSON.parse(
Expand Down
11 changes: 4 additions & 7 deletions multichain-testing/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
{
"extends": "../tsconfig.json",
"include": [
"src",
"tools",
"test"
],
"compilerOptions": {
"checkJs": false
}
"src",
"tools",
"test"
]
}
Loading

0 comments on commit a2813f6

Please sign in to comment.