-
-
Notifications
You must be signed in to change notification settings - Fork 57
fix(evm): read operation inputs when they are submitted #382
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
Changes from all commits
495e89a
b5e1645
c4f7219
feb4864
2b48e64
52e17ce
c550851
844d1b5
28b7655
78db978
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ import { resolveWasmOpt, root, toolchain as pinned } from './toolchain.js' | |
| const crate = path.join(root, 'wasm/evm2') | ||
| const out = 'src/wasm/internal/evm2.wasm.ts' | ||
| const notice = 'wasm/evm2/NOTICE.md' | ||
| const thirdParty = 'wasm/evm2/THIRD-PARTY-LICENSES.md' | ||
| const target = 'wasm32-unknown-unknown' | ||
|
|
||
| /** Where the container drops the artifact, kept out of cargo's own target dir. */ | ||
|
|
@@ -137,6 +138,7 @@ function compile(revision: string): Compiled { | |
| artifact: path.join(crate, `target/${target}/release/ox_evm2.wasm`), | ||
| licenses: hostLicenses(revision), | ||
| metadata: cargo(metadata), | ||
| texts: hostTexts(home), | ||
| rustc: child_process | ||
| .execFileSync('rustc', ['--version'], { cwd: crate, encoding: 'utf8' }) | ||
| .trim(), | ||
|
|
@@ -173,6 +175,20 @@ function compile(revision: string): Compiled { | |
| `cargo ${metadata.join(' ')} > /out/metadata.json`, | ||
| 'rustc --version > /out/rustc.txt', | ||
| `cp ${licenseDir}/LICENSE-MIT ${licenseDir}/LICENSE-APACHE /out/`, | ||
| // Every crate's own license text, keyed by directory name. The artifact | ||
| // is redistributed as compiled code, so an SPDX identifier alone does not | ||
| // satisfy the BSD, Zlib, and MIT terms it links. Copied wholesale here | ||
| // and filtered to what actually links when the notice is generated. | ||
| 'mkdir -p /out/licenses', | ||
| 'for dir in "$CARGO_HOME"/registry/src/*/*/ "$CARGO_HOME"/git/checkouts/*/*/ "$CARGO_HOME"/git/checkouts/*/*/*/; do' + | ||
| ' [ -d "$dir" ] || continue;' + | ||
| ' case "$dir" in *"/git/checkouts/"*) name="git-$(basename "$dir")";;' + | ||
| ' *) name=$(basename "$dir");; esac;' + | ||
| ' for file in "$dir"LICENSE* "$dir"COPYING* "$dir"UNLICENSE* "$dir"NOTICE*; do' + | ||
| ' [ -f "$file" ] || continue;' + | ||
| ' mkdir -p "/out/licenses/$name";' + | ||
| ' cp "$file" "/out/licenses/$name/";' + | ||
| ' done; done', | ||
| // The mounted directory is root-owned inside the container; hand it back | ||
| // so the host can read and replace it. | ||
| 'chmod -R a+rwX /out', | ||
|
|
@@ -191,6 +207,7 @@ function compile(revision: string): Compiled { | |
| }, | ||
| metadata: read('metadata.json'), | ||
| rustc: read('rustc.txt').trim(), | ||
| texts: licenseTexts(path.join(buildDir, 'licenses')), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -209,12 +226,11 @@ function assertLockfile(revision: string) { | |
| } | ||
|
|
||
| /** | ||
| * Builds the attribution notice from the resolved dependency graph. | ||
| * Returns the crates the artifact links, sorted and excluding the root. | ||
| * | ||
| * Generating it is what keeps it honest: a dependency added, removed, or | ||
| * relicensed shows up as a diff instead of going unrecorded. | ||
| * Shared by the notice and the license file so the two describe one set. | ||
| */ | ||
| function attribution(revision: string, raw: string) { | ||
| function linked(raw: string) { | ||
| type Package = { | ||
| id: string | ||
| license?: string | null | ||
|
|
@@ -267,6 +283,121 @@ function attribution(revision: string, raw: string) { | |
|
|
||
| // A crate resolved from git rather than crates.io is a fork, which is an | ||
| // attribution fact and not only a build detail. | ||
| return packages | ||
| } | ||
|
|
||
| /** Standard licenses whose text ships beside the notice. */ | ||
| const standardLicenses = new Map([ | ||
| ['Apache-2.0', 'LICENSE-APACHE'], | ||
| ['MIT', 'LICENSE-MIT'], | ||
| ]) | ||
|
|
||
| /** Collects license texts from the host Cargo cache, for the native build. */ | ||
| function hostTexts(home: string): Map<string, string> { | ||
| const texts = new Map<string, string>() | ||
| const roots = [ | ||
| { git: false, path: path.join(home, 'registry', 'src') }, | ||
| { git: true, path: path.join(home, 'git', 'checkouts') }, | ||
| ] | ||
| for (const root of roots) { | ||
| if (!fs.existsSync(root.path)) continue | ||
| for (const outer of fs.readdirSync(root.path)) { | ||
| const directory = path.join(root.path, outer) | ||
| if (!fs.statSync(directory).isDirectory()) continue | ||
| for (const crate of fs.readdirSync(directory)) { | ||
| const source = path.join(directory, crate) | ||
| if (!fs.statSync(source).isDirectory()) continue | ||
| const files = fs | ||
| .readdirSync(source) | ||
| .filter((file) => /^(LICENSE|COPYING|UNLICENSE|NOTICE)/.test(file)) | ||
| .sort() | ||
| .map( | ||
| (file) => | ||
| `<!-- ${file} -->\n\n${fs.readFileSync(path.join(source, file), 'utf8').trim()}`, | ||
| ) | ||
| if (files.length) | ||
| texts.set(root.git ? `git-${crate}` : crate, files.join('\n\n')) | ||
| } | ||
| } | ||
| } | ||
| return texts | ||
| } | ||
|
|
||
| /** Reads every collected license text, keyed by `name-version` directory. */ | ||
| function licenseTexts(directory: string): Map<string, string> { | ||
| const texts = new Map<string, string>() | ||
| if (!fs.existsSync(directory)) return texts | ||
| for (const crate of fs.readdirSync(directory)) { | ||
| const files = fs | ||
| .readdirSync(path.join(directory, crate)) | ||
| .sort() | ||
| .map((file) => { | ||
| const body = fs.readFileSync(path.join(directory, crate, file), 'utf8') | ||
| return `<!-- ${file} -->\n\n${body.trim()}` | ||
| }) | ||
| if (files.length) texts.set(crate, files.join('\n\n')) | ||
| } | ||
| return texts | ||
| } | ||
|
|
||
| /** | ||
| * Builds the third-party license file from the crates the artifact links. | ||
| * | ||
| * The artifact ships as compiled code, so an SPDX identifier is not enough: the | ||
| * BSD, Zlib, and MIT terms it links require their copyright and license text to | ||
| * travel with the binary. | ||
| */ | ||
| function thirdPartyLicenses( | ||
| metadata: string, | ||
| texts: Map<string, string>, | ||
| ): string { | ||
| const packages = linked(metadata) | ||
| const sections = packages.map((entry) => { | ||
| // A git dependency is stored under its revision rather than its version, so | ||
| // it is looked up by the revision its source records. | ||
| const revision = entry.source?.match(/[?#]rev=([0-9a-f]+)/)?.[1] | ||
| const text = | ||
| texts.get(`${entry.name}-${entry.version}`) ?? | ||
| (revision | ||
| ? (texts.get(`git-${revision}`) ?? | ||
| texts.get(`git-${revision.slice(0, 7)}`)) | ||
| : undefined) | ||
| const heading = `## \`${entry.name}\` ${entry.version}\n\nDeclared license: ${entry.license ?? 'see upstream'}.` | ||
| if (text) return `${heading}\n\n${text}` | ||
|
|
||
| // A crate publishing no file of its own still declares a license, and every | ||
| // one that does here is a standard text. Naming it and pointing at the copy | ||
| // beside this file distributes the terms without inventing a copyright line | ||
| // the crate never stated. | ||
| const standard = (entry.license ?? '') | ||
| .split(/\s+OR\s+/) | ||
| .map((name) => name.trim()) | ||
| .filter((name) => standardLicenses.has(name)) | ||
| return standard.length | ||
| ? `${heading}\n\nThis crate publishes no license file. Its declared terms are the standard ${standard.join(' or ')} text, reproduced in ${standard.map((name) => `\`${standardLicenses.get(name)}\``).join(' and ')} beside this file. Copyright remains the crate authors'.` | ||
| : `${heading}\n\nNo license file ships with this crate; see its repository.` | ||
| }) | ||
|
|
||
| return `<!-- Generated by \`pnpm wasm:build --target=evm2\`. Do not edit. --> | ||
|
|
||
| # Third-party licenses for \`src/wasm/internal/evm2.wasm.ts\` | ||
|
|
||
| The artifact is compiled code linking the crates below. Each one's own license | ||
| text is reproduced here, which distributing a binary requires. | ||
|
|
||
| ${sections.join('\n\n')} | ||
| ` | ||
| } | ||
|
|
||
| /** | ||
| * Builds the attribution notice from the resolved dependency graph. | ||
| * | ||
| * Generating it is what keeps it honest: a dependency added, removed, or | ||
| * relicensed shows up as a diff instead of going unrecorded. | ||
| */ | ||
| function attribution(revision: string, raw: string) { | ||
| const packages = linked(raw) | ||
|
|
||
| const rows = packages.map((entry) => { | ||
| const source = entry.source?.startsWith('git+') | ||
| ? entry.source.replace(/^git\+/, '').replace(/\?rev=[0-9a-f]+#/, ' @ ') | ||
|
|
@@ -284,7 +415,9 @@ The artifact is compiled from \`wasm/evm2\` against | |
| committed beside this file as \`LICENSE-MIT\` and \`LICENSE-APACHE\`. | ||
|
|
||
| Everything compiled into the artifact is listed below, with the license each | ||
| crate declares. | ||
| crate declares. Each crate's own license text is reproduced in | ||
| \`THIRD-PARTY-LICENSES.md\` beside this file, which distributing compiled code | ||
| requires. | ||
|
|
||
| | Crate | Version | License | Source | | ||
| | --- | --- | --- | --- | | ||
|
|
@@ -445,6 +578,7 @@ export async function buildEvm2(): Promise<Record<string, string>> { | |
| ]), | ||
| ), | ||
| [notice]: attribution(revision, compiled.metadata), | ||
| [thirdParty]: thirdPartyLicenses(compiled.metadata, compiled.texts), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When AGENTS.md reference: AGENTS.md:L138-L138 Useful? React with 👍 / 👎. |
||
| [out]: template(bytes, { | ||
| features, | ||
| gzip, | ||
|
|
||
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.
With an asynchronous database, this new guarantee is still false for
Evm.commitSource:src/evm/Evm.ts:1253-1255callsPendingState.changes(state)only when the queued callback runs, whilePendingState.insertAccountretains the caller's mutablecurrentandoriginalobjects. Mutating either object aftercommitSourcereturns but before the queue drains silently changes the state that gets applied; capture a deep snapshot at submission as the other state-bearing operations now do.AGENTS.md reference: AGENTS.md:L49-L49
Useful? React with 👍 / 👎.