Skip to content

Commit

Permalink
feat(compiler): add generateAci, generateAciBySourceCode
Browse files Browse the repository at this point in the history
  • Loading branch information
davidyuk committed Jun 23, 2023
1 parent 2215b22 commit c4effb3
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 4 deletions.
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ services:
- ./docker/accounts_test.json:/home/aeternity/node/data/aecore/.genesis/accounts_test.json

compiler:
image: aeternity/aesophia_http:v7.2.0
build: https://github.com/aeternity/aesophia_http.git#add-no-code-to-aci
hostname: compiler
ports: ["3080:3080"]
26 changes: 26 additions & 0 deletions src/contract/compiler/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,32 @@ export default abstract class CompilerBase {
aci: Aci;
}>;

/**
* Generate contract's ACI by contract's path
* Available only in Node.js
* @param path - Path to contract source code
* @returns ACI
*/
abstract generateAci(path: string): Promise<Aci>;

/**
* Generate contract's ACI by contract's source code
* @param sourceCode - Contract source code as string
* @param fileSystem - A map of contract filename to the corresponding contract source code to
* include into the main contract
* @example
* ```js
* {
* 'library.aes': 'namespace TestLib =\n function sum(x: int, y: int) : int = x + y'
* }
* ```
* @returns ACI
*/
abstract generateAciBySourceCode(
sourceCode: string,
fileSystem?: Record<string, string>,
): Promise<Aci>;

/**
* Verify that a contract bytecode is the result of compiling the given source code
* Available only in Node.js
Expand Down
24 changes: 23 additions & 1 deletion src/contract/compiler/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default class CompilerCli extends CompilerBase {
]);
return {
bytecode: bytecode.trimEnd() as Encoded.ContractBytearray,
aci: aci as Aci,
aci,
};
} catch (error) {
ensureError(error);
Expand All @@ -99,6 +99,28 @@ export default class CompilerCli extends CompilerBase {
}
}

async generateAci(path: string): Promise<Aci> {
await this.#ensureCompatibleVersion;
try {
return JSON.parse(await this.#run('--no_code', '--create_json_aci', path));
} catch (error) {
ensureError(error);
throw new CompilerError(error.message);

Check warning on line 108 in src/contract/compiler/Cli.ts

View check run for this annotation

Codecov / codecov/patch

src/contract/compiler/Cli.ts#L107-L108

Added lines #L107 - L108 were not covered by tests
}
}

async generateAciBySourceCode(
sourceCode: string,
fileSystem?: Record<string, string>,
): Promise<Aci> {
const tmp = await CompilerCli.#saveContractToTmpDir(sourceCode, fileSystem);
try {
return await this.generateAci(tmp);
} finally {
await rm(dirname(tmp), { recursive: true });
}
}

async validate(bytecode: Encoded.ContractBytearray, path: string): Promise<boolean> {
await this.#ensureCompatibleVersion;
try {
Expand Down
19 changes: 19 additions & 0 deletions src/contract/compiler/Http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ export default class CompilerHttp extends CompilerBase {
throw new NotImplementedError('File system access, use CompilerHttpNode instead');
}

async generateAciBySourceCode(
sourceCode: string,
fileSystem?: Record<string, string>,
): Promise<Aci> {
try {
return await this.api.generateACI({ code: sourceCode, options: { fileSystem } });
} catch (error) {
if (error instanceof RestError && error.statusCode === 400) {
throw new CompilerError(error.message);

Check warning on line 92 in src/contract/compiler/Http.ts

View check run for this annotation

Codecov / codecov/patch

src/contract/compiler/Http.ts#L92

Added line #L92 was not covered by tests
}
throw error;

Check warning on line 94 in src/contract/compiler/Http.ts

View check run for this annotation

Codecov / codecov/patch

src/contract/compiler/Http.ts#L94

Added line #L94 was not covered by tests
}
}

// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
async generateAci(path: string): Promise<Aci> {
throw new NotImplementedError('File system access, use CompilerHttpNode instead');

Check warning on line 100 in src/contract/compiler/Http.ts

View check run for this annotation

Codecov / codecov/patch

src/contract/compiler/Http.ts#L100

Added line #L100 was not covered by tests
}

async validateBySourceCode(
bytecode: Encoded.ContractBytearray,
sourceCode: string,
Expand Down
6 changes: 6 additions & 0 deletions src/contract/compiler/HttpNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export default class CompilerHttpNode extends HttpBrowser {
return this.compileBySourceCode(sourceCode, fileSystem);
}

override async generateAci(path: string): Promise<Aci> {
const fileSystem = await getFileSystem(path);
const sourceCode = await readFile(path, 'utf8');
return this.generateAciBySourceCode(sourceCode, fileSystem);
}

override async validate(bytecode: Encoded.ContractBytearray, path: string): Promise<boolean> {
const fileSystem = await getFileSystem(path);
const sourceCode = await readFile(path, 'utf8');
Expand Down
2 changes: 1 addition & 1 deletion test/integration/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('MiddlewareSubscriber', () => {
},
mdwGensPerMinute: res.mdwGensPerMinute,
mdwHeight: res.mdwHeight,
mdwLastMigration: 20230519120000,
mdwLastMigration: res.mdwLastMigration,
mdwRevision: res.mdwRevision,
mdwSynced: true,
mdwSyncing: true,
Expand Down
51 changes: 51 additions & 0 deletions test/integration/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,50 @@ function testCompiler(compiler: CompilerBase): void {
const inclBytecode = 'cb_+QEGRgOg7BH1sCv+p2IrS0Pn3/i6AfE8lOGUuC71lLPn6mbUm9PAuNm4cv4AWolkAjcCBwcHFBQAAgD+RNZEHwA3ADcAGg6CPwEDP/5Nt4A5AjcCBwcHDAECDAEABAMRAFqJZP6SiyA2ADcBBwcMAwgMAQAEAxFNt4A5/pSgnxIANwF3BwwBAAQDEarAwob+qsDChgI3AXcHPgQAALhgLwYRAFqJZD0uU3VibGlicmFyeS5zdW0RRNZEHxFpbml0EU23gDkxLkxpYnJhcnkuc3VtEZKLIDYRdGVzdBGUoJ8SJWdldExlbmd0aBGqwMKGOS5TdHJpbmcubGVuZ3Rogi8AhTcuMS4wAGHgFTw=';
const testBytecode = 'cb_+GhGA6BgYgXqYB9ctBcQ8mJ0+we5OXhb9PpsSQWP2DhPx9obn8C4O57+RNZEHwA3ADcAGg6CPwEDP/6AeCCSADcBd3cBAQCYLwIRRNZEHxFpbml0EYB4IJIZZ2V0QXJngi8AhTcuMC4xAMXqWXc=';

const interfaceSourceCodePath = './test/integration/contracts/Interface.aes';
let interfaceSourceCode: string;
let interfaceFileSystem: Record<string, string>;
const interfaceAci = [
{ namespace: { name: 'ListInternal', typedefs: [] } },
{ namespace: { name: 'List', typedefs: [] } },
{ namespace: { name: 'String', typedefs: [] } },
{
contract: {
functions: [{
arguments: [{ name: '_1', type: 'int' }],
name: 'decrement',
payable: false,
returns: 'int',
stateful: false,
}],
kind: 'contract_child',
name: 'Decrement',
payable: false,
typedefs: [],
},
},
{
contract: {
functions: [{
arguments: [{ name: '_1', type: 'int' }],
name: 'increment',
payable: false,
returns: 'int',
stateful: false,
}],
kind: 'contract_main',
name: 'Increment',
payable: false,
typedefs: [],
},
},
];

before(async () => {
inclSourceCode = await readFile(inclSourceCodePath, 'utf8');
inclFileSystem = await getFileSystem(inclSourceCodePath);
interfaceSourceCode = await readFile(interfaceSourceCodePath, 'utf8');
interfaceFileSystem = await getFileSystem(interfaceSourceCodePath);
});

it('returns version', async () => {
Expand Down Expand Up @@ -54,6 +95,16 @@ function testCompiler(compiler: CompilerBase): void {
);
});

it('generates aci by path', async () => {
const aci = await compiler.generateAci(interfaceSourceCodePath);
expect(aci).to.eql(interfaceAci);
});

it('generates aci by source code', async () => {
const aci = await compiler.generateAciBySourceCode(interfaceSourceCode, interfaceFileSystem);
expect(aci).to.eql(interfaceAci);
});

it('validates bytecode by path', async () => {
expect(await compiler.validate(inclBytecode, inclSourceCodePath)).to.be.equal(true);
expect(await compiler.validate(testBytecode, inclSourceCodePath)).to.be.equal(false);
Expand Down
5 changes: 5 additions & 0 deletions test/integration/contracts/Interface.aes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include "String.aes"
include "./lib/DecrementInterface.aes"

main contract Increment =
entrypoint increment: (int) => int
2 changes: 2 additions & 0 deletions test/integration/contracts/lib/DecrementInterface.aes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
contract Decrement =
entrypoint decrement: (int) => int
2 changes: 1 addition & 1 deletion tooling/autorest/compiler-prepare.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'fs';

const swaggerUrl = 'https://raw.githubusercontent.com/aeternity/aesophia_http/v7.1.1/config/swagger.yaml';
const swaggerUrl = 'https://raw.githubusercontent.com/aeternity/aesophia_http/add-no-code-to-aci/config/swagger.yaml';

const response = await fetch(swaggerUrl);
console.assert(response.status === 200, 'Invalid response code', response.status);
Expand Down

0 comments on commit c4effb3

Please sign in to comment.