Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/sunny-chairs-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@mimicprotocol/lib-ts": patch
"@mimicprotocol/cli": patch
"@mimicprotocol/test-ts": patch
---

Add runner target on compilation
8 changes: 8 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
},
"peerDependencies": {
"@mimicprotocol/lib-ts": ">=0.0.1-rc.38"
},
"peerDependenciesMeta": {
"@mimicprotocol/lib-ts": {
"optional": false
}
},
"oclif": {
"bin": "mimic",
"commands": "./dist/commands",
Expand Down
64 changes: 63 additions & 1 deletion packages/cli/src/commands/compile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RUNNER_TARGET_VERSION } from '@mimicprotocol/lib-ts/constants'
import { Command, Flags } from '@oclif/core'
import * as fs from 'fs'
import * as path from 'path'
Expand Down Expand Up @@ -46,13 +47,14 @@ export default class Compile extends Command {
log.startAction('Verifying Manifest')
const manifest = ManifestHandler.load(cmd, manifestDir)
log.startAction('Compiling')
const wasmPath = path.join(absBuildDir, 'function.wasm')

const ascArgs = [
absFunctionFile,
'--target',
'release',
'--outFile',
path.join(absBuildDir, 'function.wasm'),
wasmPath,
'--optimize',
'--exportRuntime',
'--transform',
Expand All @@ -67,10 +69,70 @@ export default class Compile extends Command {
})
}

log.startAction('Injecting metadata')
const wasmBuffer = fs.readFileSync(wasmPath)
const metadata = {
runnerTarget: RUNNER_TARGET_VERSION,
}
const wasmWithMetadata = addCustomSection(wasmBuffer, 'mimic-metadata', JSON.stringify(metadata))
fs.writeFileSync(wasmPath, wasmWithMetadata)

log.startAction('Saving files')

fs.writeFileSync(path.join(absBuildDir, 'manifest.json'), JSON.stringify(manifest, null, 2))
log.stopAction()
console.log(`Build complete! Artifacts in ${absBuildDir}/`)
}
}

/**
* Add a custom section to a WASM binary
* @param wasmBuffer - The original WASM binary
* @param sectionName - Name of the custom section
* @param data - String data to store in the section
* @returns Modified WASM binary with the custom section
*/
function addCustomSection(wasmBuffer: Buffer, sectionName: string, data: string): Buffer {
const dataBuffer = Buffer.from(data, 'utf-8')
const nameBuffer = Buffer.from(sectionName, 'utf-8')

// WASM custom section format:
// - Section ID: 0 (custom section)
// - Section size (LEB128) - size of name length + name + data
// - Name length (LEB128)
// - Name bytes
// - Data bytes

const nameLengthBuffer = encodeLEB128(nameBuffer.length)
const sectionContentSize = nameLengthBuffer.length + nameBuffer.length + dataBuffer.length
const sectionSizeBuffer = encodeLEB128(sectionContentSize)

const customSection = Buffer.concat([
Buffer.from([0]), // Custom section ID
sectionSizeBuffer,
nameLengthBuffer,
nameBuffer,
dataBuffer,
])

// Insert after the WASM header (8 bytes: magic + version)
const headerSize = 8
return Buffer.concat([wasmBuffer.slice(0, headerSize), customSection, wasmBuffer.slice(headerSize)])
}

/**
* Encode an unsigned integer as LEB128 (Little Endian Base 128)
*/
function encodeLEB128(value: number): Buffer {
const bytes: number[] = []
while (true) {
let byte = value & 0x7f
value >>>= 7
if (value !== 0) {
byte |= 0x80
}
bytes.push(byte)
if (value === 0) break
}
return Buffer.from(bytes)
}
19 changes: 2 additions & 17 deletions packages/cli/src/lib/ManifestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RUNNER_TARGET_VERSION } from '@mimicprotocol/lib-ts/constants'
import { Command } from '@oclif/core'
import * as fs from 'fs'
import { load } from 'js-yaml'
import * as path from 'path'
import { ZodError } from 'zod'

import { DuplicateEntryError, EmptyManifestError, MoreThanOneEntryError } from '../errors'
Expand All @@ -17,7 +17,7 @@ export default {
...manifest,
inputs: mergeIfUnique(manifest.inputs),
abis: mergeIfUnique(manifest.abis),
metadata: { libVersion: getLibVersion() },
metadata: { runnerTarget: RUNNER_TARGET_VERSION },
}
return ManifestValidator.parse(mergedManifest)
},
Expand Down Expand Up @@ -79,18 +79,3 @@ function handleValidationError(command: Command, err: unknown): never {

command.error(message, { code, suggestions })
}

function getLibVersion(): string {
try {
let currentDir = process.cwd()
while (currentDir !== path.dirname(currentDir)) {
const libPackagePath = path.join(currentDir, 'node_modules', '@mimicprotocol', 'lib-ts', 'package.json')
if (fs.existsSync(libPackagePath)) return JSON.parse(fs.readFileSync(libPackagePath, 'utf-8')).version
currentDir = path.dirname(currentDir)
}

throw new Error('Could not find @mimicprotocol/lib-ts package')
} catch (error) {
throw new Error(`Failed to read @mimicprotocol/lib-ts version: ${error}`)
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ export const ManifestValidator = z.object({
inputs: z.record(String, InputValue),
abis: z.record(String, String),
metadata: z.object({
libVersion: String.regex(SEM_VER_REGEX, 'Must be a valid semver'),
runnerTarget: String.regex(SEM_VER_REGEX, 'Must be a valid semver'),
}),
})
8 changes: 4 additions & 4 deletions packages/cli/tests/ManifestHandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ describe('ManifestHandler', () => {
})
})

context('when dealing with lib version', () => {
context('when the lib version is not present', () => {
it('adds the lib version to the manifest', () => {
context('when dealing with runner target version', () => {
context('when the runner target version is not present', () => {
it('adds the runner target version to the manifest', () => {
const parsedManifest = ManifestHandler.validate(manifest)

expect(parsedManifest.metadata.libVersion).to.match(SEM_VER_REGEX)
expect(parsedManifest.metadata.runnerTarget).to.match(SEM_VER_REGEX)
})
})

Expand Down
1 change: 1 addition & 0 deletions packages/lib-ts/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const RUNNER_TARGET_VERSION = '0.0.1'
5 changes: 5 additions & 0 deletions packages/lib-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
"files": [
"src",
"index.ts",
"constants.ts",
"asconfig.json"
],
"exports": {
".": "./index.ts",
"./constants": "./constants.ts"
},
"devDependencies": {
"@as-pect/cli": "8.1.0",
"assemblyscript": "0.27.36"
Expand Down