-
Notifications
You must be signed in to change notification settings - Fork 206
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
refactor: Switch default archive format to endoZipBase64 #3273
Conversation
145ac71
to
c688fd8
Compare
ee4fd38
to
534641f
Compare
a11d96e
to
eb0def8
Compare
The import and archive functions now accepta `dev` flag that indicates that the `devDependencies` of the entry module's package should be included in the compartment graph. This must be passed explicitly and is off by default since that will be the norm for applications. This is the bug of the day blocking Agoric/agoric-sdk#3273 In furtherance of Agoric/agoric-sdk#2684
c74a8cd
to
a049acb
Compare
2d36ee8
to
7bfa155
Compare
7bfa155
to
4bf1772
Compare
4bf1772
to
be0d555
Compare
@@ -286,6 +286,9 @@ function makeWorker(port) { | |||
assert, | |||
// bootstrap provides HandledPromise | |||
HandledPromise: globalThis.HandledPromise, | |||
TextEncoder, | |||
TextDecoder, | |||
Base64: globalThis.Base64, // Present only in XSnap |
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.
Absent from this list is URL, which is never present inside XSnap.
@@ -15,7 +15,7 @@ import { SourceMapConsumer } from 'source-map'; | |||
|
|||
import './types.js'; | |||
|
|||
const DEFAULT_MODULE_FORMAT = 'nestedEvaluate'; | |||
const DEFAULT_MODULE_FORMAT = 'endoZipBase64'; |
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 the biggest/riskiest line in the diff. cc @warner
/* | ||
* Getting the properties of a SES-shim module exports namespace object throws | ||
* an error if inspected before the module initializes. | ||
* Allowing this case is necessary for the compartment mapper to be hosted by | ||
* the compartment mapper under metering. | ||
*/ | ||
const maybeGetOwnPropertyDescriptors = object => { | ||
try { | ||
return getOwnPropertyDescriptors(object); | ||
} catch (_error) { | ||
return {}; | ||
} | ||
}; | ||
|
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 change is necessary for making Zip files work on Node.js with metering. We might not be exercising this anymore, but I include the change for completeness. cc @michaelfig
* Forked from npm polycrc@1.1.0 for module system compatibility, then trimmed | ||
* down to a version that assumes TypedArrays are universal. |
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.
Embedding CRC6 is a reversible expedient. The existing polycrc
doesn’t work with Endo because of a bad interaction with named exports. I may be able to fix that with more work on the Endo side. Otherwise, bringing this in-house and getting it past lint and tsc increased my confidence in the code, so there’s an upside. The downside is that I don’t think it’s worthwhile to also bring in the tests from polycrc
. So, if this isn’t palatable, the alternatives are:
- Do more work on Endo until the CommonJS/ESM boundary works for this particular case. That is, an ESM importing a CJS with the previously working incantation
import { models as crcmodels } from 'polycrc'
, which requires detecting thatmodels
is an export detected bymodule.exports = { models }
(approximately) in the lexical analysis. - Patch
polycrc
so it’s"type": "module"
and uses the appropriate ESM gestures.
@@ -73,7 +73,7 @@ const makeZoeKit = ( | |||
}, | |||
meteringConfig = { | |||
incrementBy: 25_000_000n, | |||
initial: 50_000_000n, // executeContract for treasury is 13.5M | |||
initial: 75_000_000n, // executeContract for treasury is 13.5M |
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.
@katelynsills This was enough of a boost to clear CI. More boost might be necessary to preserve more overhead. The comment is probably stale now. Do we have a tool for measuring computron cost? cc @warner
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.
@kriskowal does it seem plausible that zip archives are 50% more compute-intensive? What is it doing that take so much more computrons?
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 nestedEvaluate
format is a string
, whereas the endoZipArchive
is a string
in base64, which we then convert to a TypedArray with Base64.decode
(which is probably free regardless of size since it’s native and I assume not metered), but then we slice the TypedArray into small files and UTF-8 decode them (which might be metered, but should be close to free on XS).
Some of this overhead should turn in our favor when we switch CapTP to a binary protocol, which will reduce the size of setBundle messages by about 25% and eliminate the base64 decode. The UTF-8 decode might not change.
The nestedEvaluate
format is a code string that contains code strings. The link phase is expressed as code, whereas with endoZipBase64
, it’s expressed as a JSON blob that creates a Compartment graph and links them. That’s overhead. The overhead could probably be reduced by translating the JSON compartment map into code.
There would remain some overhead though because nestedEvaluate
is a single-compartment runtime and endoZipBase64
puts us on footing for LavaMoat style dependency sandboxing.
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.
ah. right. Compartment-per-package for least-authority linkage is why we would pay this cost.
I wonder whether Base64.decode
should be metered... or if all uses of it are going to cost enough to build the input so that it doesn't matter. I probably should open an issue.
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.
Opening an issue sounds good to me. Where does Moddable want us to open issues?
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 should be an agoric-sdk issue.
We probably don't need help from Moddable. We should be able to just combine the native base64 implementation that does something O(n) on the length of the input... for example, since regexps are already metered: s.match(/.*/)
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.
Excellent. Filed #3874
@@ -415,7 +418,7 @@ test(`zoe.getConfiguration`, async t => { | |||
}, | |||
meteringConfig: { | |||
incrementBy: 25000000n, | |||
initial: 50000000n, | |||
initial: 75000000n, |
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 test detects any change to the default metering config, which isn’t terribly valuable. Could this instead import the defaults and inject them back 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.
Yup, that's a good idea. I'll make an issue.
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.
Issue here: #3884
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 looks good to me as per @dtribble's decision that we will add validation to Zoe later! Some small requests to leave in code that I think got wrongly deleted.
assert(installation === installations.atomicSwap, X`wrong installation`); | ||
assert( | ||
sameStructure( | ||
harden({ Asset: invitationIssuer, Price: bucksIssuer }), | ||
issuerKeywordRecord, | ||
), | ||
X`issuerKeywordRecord were not as expected`, | ||
); |
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 these lines are wrongly removed. They're unrelated to the changes in this PR.
// const instance = await E(zoe).getInstance(invitation); | ||
// const installation = await E(zoe).getInstallation(invitation); |
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.
// const instance = await E(zoe).getInstance(invitation); | |
// const installation = await E(zoe).getInstallation(invitation); | |
// const installation = await E(zoe).getInstallation(invitation); |
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'm not sure why instance
is gotten here if it's the source/hash that we care about
const instance = await E(zoe).getInstance(invitation); | ||
const installation = await E(zoe).getInstallation(invitation); | ||
const issuerKeywordRecord = await E(zoe).getIssuers(instance); |
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.
These lines should remain in.
const [moolaIssuer, simoleanIssuer, bucksIssuer] = issuers; | ||
const [moolaIssuer, simoleanIssuer] = issuers; |
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.
Should remain the same
@@ -415,7 +418,7 @@ test(`zoe.getConfiguration`, async t => { | |||
}, | |||
meteringConfig: { | |||
incrementBy: 25000000n, | |||
initial: 50000000n, | |||
initial: 75000000n, |
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.
Yup, that's a good idea. I'll make an issue.
@@ -415,7 +418,7 @@ test(`zoe.getConfiguration`, async t => { | |||
}, | |||
meteringConfig: { | |||
incrementBy: 25000000n, | |||
initial: 50000000n, | |||
initial: 75000000n, |
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.
Issue here: #3884
I should add, I did not look closely at the |
*BREAKING CHANGE*: This change switches the default bundle format from `nestedEvaluate` to `endoZipBase64`. The new bundle format has a fundamentally different shape and content, so any existing code that depends on being able to inspect the `source` string of the bundle will break. This functionality will be replaced with a new feature for consistent-hash integrity checking. #3859
Fixes: #2684