Skip to content

Commit 1cb9d7e

Browse files
committed
ContractController to run contract functions and compiling refactoring
1 parent 0967885 commit 1cb9d7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+401
-233
lines changed

src/api/bytecode/EVMDisassembler.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import 'reflect-metadata'
22
import { Disassembler } from './Disassembler'
3-
import { EVMDisassembler } from './EVMDisassembler'
43
import { DisassembledContract } from './DisassembledContract'
4+
import { createEVMDisassembler } from '../symbolic/evm/exec/TestUtils';
55
let BN = require('bn.js')
66

77
describe('Disassembler test', () => {
88
let disass: Disassembler
99

1010
beforeEach(() => {
11-
disass = new EVMDisassembler()
11+
disass = createEVMDisassembler()
1212
})
1313

1414
it('Test disassembler bytecode', async () => {

src/api/bytecode/EVMDisassembler.ts

Lines changed: 15 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
11
import { Disassembler } from './Disassembler'
22
import { Operation } from './Operation'
33
import { Opcodes } from './Opcodes'
4-
import { injectable } from 'inversify'
4+
import { injectable, inject } from 'inversify'
55
import { Opcode } from './Opcode'
66
import { DisassembledContract } from './DisassembledContract'
77
import { logger } from '../../Logger';
8+
import { TYPES } from '../../inversify/types';
9+
import { ContractService } from '../service/service/ContractService';
810
let BN = require('bn.js')
9-
let solc = require('solc')
10-
let fs = require('fs')
11-
let nodePath = require('path')
11+
1212

1313
@injectable()
1414
export class EVMDisassembler implements Disassembler {
1515
static readonly metadataPrefix = 'a165627a7a72305820'
1616

17-
constructor() {}
17+
constructor(
18+
@inject(TYPES.ContractService) private contractService: ContractService
19+
) {}
1820

1921
disassembleSourceCode(contractName: string, source: string, path: string): DisassembledContract {
2022
if (source.startsWith('0x')) {
2123
return this.disassembleContract(source)
2224
}
23-
const compileJson = this.generateCompileObject(contractName, source, path)
24-
const compiledContract = JSON.parse(solc.compileStandardWrapper(JSON.stringify(compileJson)))
25-
const contractWithExt = `${contractName}.sol`
26-
const contract = compiledContract.contracts[contractWithExt][contractName]
27-
if (!contract) {
28-
throw new Error('Bad source code')
29-
}
25+
26+
const contract = this.contractService.compileContract(contractName, source, path)
27+
28+
3029
const bytecode = contract.evm.bytecode.object
31-
const runtimeBytecode = compiledContract.contracts[contractWithExt][contractName].evm.deployedBytecode.object
32-
const contractAssembly = compiledContract.contracts[contractWithExt][contractName].evm.legacyAssembly
30+
const runtimeBytecode = contract.evm.deployedBytecode.object
31+
const contractAssembly = contract.evm.legacyAssembly
3332
if (!contractAssembly) {
34-
logger.error(JSON.stringify(compiledContract))
33+
logger.error(JSON.stringify(contract))
3534
throw new Error(`No code found in contract ${contractName}`)
3635
}
3736
const asmRuntime = contractAssembly['.data'][0]['.code'].filter(elem => elem.name !== 'tag')
38-
const asmConstructor = compiledContract.contracts[contractWithExt][contractName].evm.legacyAssembly['.code'].filter(
37+
const asmConstructor = contract.evm.legacyAssembly['.code'].filter(
3938
elem => elem.name !== 'tag'
4039
)
4140
const disassembledCode: DisassembledContract = this.disassembleContract(bytecode)
@@ -164,75 +163,7 @@ export class EVMDisassembler implements Disassembler {
164163
} as Operation
165164
}
166165

167-
private findImports(sources: any, content: string, path: string, filesChecked: string[], initialPath: string) {
168-
const regexp = /import "(.*)"|import '(.*)'/g
169-
const match = content.match(regexp)
170-
if (!match) {
171-
return
172-
}
173-
const matches = match.map(imp => {
174-
const splittedImp = imp.split('"')
175-
if (splittedImp.length < 2) {
176-
return imp.split("'")[1]
177-
} else {
178-
return splittedImp[1]
179-
}
180-
})
181-
182-
for (const imp of matches) {
183-
let importFilePath = path
184-
if (!importFilePath.endsWith(nodePath.sep)) {
185-
importFilePath = importFilePath + nodePath.sep
186-
}
187-
importFilePath = nodePath.normalize(importFilePath + imp)
188-
const importPathRelative = nodePath
189-
.relative(initialPath, importFilePath)
190-
.replace('./', '')
191-
.replace('../', '')
192-
.replace(/^\./, '')
193-
194-
const importContent = fs.readFileSync(importFilePath).toString()
195-
let sourceFileName = imp.replace('./', '').replace('../', '')
196-
if (sourceFileName.startsWith('.')) {
197-
sourceFileName = sourceFileName.substr(1, sourceFileName.length)
198-
}
199-
if (filesChecked.includes(importPathRelative)) {
200-
continue
201-
}
202-
filesChecked.push(importPathRelative)
203-
sources[importPathRelative] = {
204-
content: importContent
205-
}
206-
this.findImports(
207-
sources,
208-
importContent,
209-
nodePath.normalize(nodePath.dirname(importFilePath)),
210-
filesChecked,
211-
initialPath
212-
)
213-
}
214-
}
215166

216-
private generateCompileObject(contractName: string, content: string, path: string) {
217-
const sources = {}
218-
sources[`${contractName}.sol`] = {
219-
content: content
220-
}
221-
const filesChecked = []
222-
this.findImports(sources, content, path, filesChecked, path)
223-
const compileJson = {
224-
language: 'Solidity',
225-
sources,
226-
settings: {
227-
outputSelection: {
228-
'*': {
229-
'*': ['evm.bytecode', 'evm.legacyAssembly', 'evm.deployedBytecode']
230-
}
231-
}
232-
}
233-
}
234-
return compileJson
235-
}
236167

237168
private isPush(opcode: Opcode): boolean {
238169
return opcode.name.startsWith('PUSH')

src/api/cfg/EthereumCFGCreator.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Disassembler } from '../bytecode/Disassembler'
55
import { Operation } from '../bytecode/Operation'
66
import { Opcodes } from '../bytecode/Opcodes'
77
import { CFGBlocks } from './CFGBlocks'
8+
import { createEVMDisassembler } from '../symbolic/evm/exec/TestUtils';
89
let BN = require('bn.js')
910

1011
describe('EthereumCFGCreator', () => {
@@ -13,7 +14,7 @@ describe('EthereumCFGCreator', () => {
1314

1415
beforeEach(() => {
1516
cfgCreator = new EthereumCFGCreator()
16-
disassembler = new EVMDisassembler()
17+
disassembler = createEVMDisassembler()
1718
})
1819

1920
it('Test blocks correctly created, no jumps', () => {

src/api/cfg/GraphVizService.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { EthereumCFGCreator } from './EthereumCFGCreator'
22
import { Disassembler } from '../bytecode/Disassembler'
33
import { OpcodeExecutor } from '../symbolic/evm/exec/OpcodeExecutor'
4-
import { EVMDisassembler } from '../bytecode/EVMDisassembler'
54
import { EVMExecutor } from '../symbolic/evm/EVMExecutor'
6-
import { createExecutor } from '../symbolic/evm/exec/TestUtils'
5+
import { createExecutor, createEVMDisassembler } from '../symbolic/evm/exec/TestUtils'
76
import { GraphVizService } from './GraphVizService'
87

98
describe('GraphVizService', () => {
@@ -14,7 +13,7 @@ describe('GraphVizService', () => {
1413

1514
beforeEach(() => {
1615
cfgCreator = new EthereumCFGCreator()
17-
disassembler = new EVMDisassembler()
16+
disassembler = createEVMDisassembler()
1817
graph = new GraphVizService()
1918
})
2019

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Route, Controller, Get, Query, Post, Path, Body } from 'tsoa'
2+
import { provideSingleton, inject } from '../../../inversify/ioc';
3+
import { TYPES } from '../../../inversify/types';
4+
import { ContractService } from '../service/ContractService';
5+
import { logger } from '../../../Logger';
6+
import { RunContractFunctionRequest } from '../request/RunContractFunctionRequest';
7+
import { IWeb3 } from '../../blockchain/IWeb3';
8+
import { Web3Instance } from '../../blockchain/Web3Instance';
9+
import { Web3Configuration } from '../../blockchain/Web3Configuration';
10+
11+
@Route('contract')
12+
@provideSingleton(ContractController)
13+
export class ContractController extends Controller {
14+
15+
constructor(
16+
@inject(TYPES.ContractService) private contractService: ContractService,
17+
) {
18+
super()
19+
}
20+
21+
@Get('abi')
22+
async getAbi(
23+
@Query('source') source: string,
24+
@Query('name') name: string,
25+
@Query('path') path: string
26+
){
27+
try {
28+
const abi = this.contractService.getAbi(name, source, path)
29+
if (!abi) {
30+
throw new Error(`No abi found for contract ${name}`)
31+
}
32+
return abi
33+
} catch (err) {
34+
logger.error(err)
35+
throw new Error(err.message)
36+
}
37+
}
38+
39+
@Post('run/{contractAddress}')
40+
async run(
41+
@Path() contractAddress: string,
42+
@Body() runFunction: RunContractFunctionRequest,
43+
@Query('blockchainHost') blockchainHost?: string,
44+
@Query('blockchainProtocol') blockchainProtocol?: string,
45+
@Query('blockchainBasicAuthUsername') blockchainBasicAuthUsername?: string,
46+
@Query('blockchainBasicAuthPassword') blockchainBasicAuthPassword?: string
47+
)
48+
{
49+
const config = {
50+
blockchainHost,
51+
blockchainProtocol,
52+
blockchainBasicAuthUsername,
53+
blockchainBasicAuthPassword
54+
} as Web3Configuration
55+
const iWeb3: IWeb3 = new Web3Instance(config)
56+
const web3 = iWeb3.getInstance()
57+
const functionCallEncoded = web3.eth.abi.encodeFunctionCall(runFunction.abi, runFunction.params)
58+
const accounts = await web3.eth.getAccounts()
59+
const receipt = await web3.eth.sendTransaction({
60+
to: contractAddress,
61+
from: runFunction.from || accounts[0],
62+
gas: runFunction.gas,
63+
gasPrice: runFunction.gasPrice,
64+
value: runFunction.value,
65+
input: functionCallEncoded
66+
})
67+
return receipt
68+
}
69+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface RunContractFunctionRequest {
2+
abi: any
3+
params: string[],
4+
from?: string,
5+
gas?: number,
6+
gasPrice?: number,
7+
value?: number
8+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { injectable } from "inversify";
2+
let solc = require('solc')
3+
let fs = require('fs')
4+
let nodePath = require('path')
5+
6+
@injectable()
7+
export class ContractService {
8+
9+
getAbi(contractName: string, source: string, path: string) {
10+
const contract = this.compileContract(contractName, source, path)
11+
return contract.abi
12+
}
13+
14+
compileContract(contractName: string, source: string, path: string) {
15+
const compileJson = this.generateCompileObject(contractName, source, path)
16+
const compiledContract = JSON.parse(solc.compileStandardWrapper(JSON.stringify(compileJson)))
17+
const contractWithExt = `${contractName}.sol`
18+
const contract = compiledContract.contracts[contractWithExt][contractName]
19+
if (!contract) {
20+
throw new Error('Bad source code')
21+
}
22+
return contract
23+
}
24+
25+
private generateCompileObject(contractName: string, content: string, path: string) {
26+
const sources = {}
27+
sources[`${contractName}.sol`] = {
28+
content: content
29+
}
30+
const filesChecked = []
31+
this.findImports(sources, content, path, filesChecked, path)
32+
const compileJson = {
33+
language: 'Solidity',
34+
sources,
35+
settings: {
36+
outputSelection: {
37+
'*': {
38+
'*': ['evm.bytecode', 'evm.legacyAssembly', 'evm.deployedBytecode', "abi"]
39+
}
40+
}
41+
}
42+
}
43+
return compileJson
44+
}
45+
private findImports(sources: any, content: string, path: string, filesChecked: string[], initialPath: string) {
46+
const regexp = /import "(.*)"|import '(.*)'/g
47+
const match = content.match(regexp)
48+
if (!match) {
49+
return
50+
}
51+
const matches = match.map(imp => {
52+
const splittedImp = imp.split('"')
53+
if (splittedImp.length < 2) {
54+
return imp.split("'")[1]
55+
} else {
56+
return splittedImp[1]
57+
}
58+
})
59+
60+
for (const imp of matches) {
61+
let importFilePath = path
62+
if (!importFilePath.endsWith(nodePath.sep)) {
63+
importFilePath = importFilePath + nodePath.sep
64+
}
65+
importFilePath = nodePath.normalize(importFilePath + imp)
66+
const importPathRelative = nodePath
67+
.relative(initialPath, importFilePath)
68+
.replace('./', '')
69+
.replace('../', '')
70+
.replace(/^\./, '')
71+
72+
const importContent = fs.readFileSync(importFilePath).toString()
73+
let sourceFileName = imp.replace('./', '').replace('../', '')
74+
if (sourceFileName.startsWith('.')) {
75+
sourceFileName = sourceFileName.substr(1, sourceFileName.length)
76+
}
77+
if (filesChecked.includes(importPathRelative)) {
78+
continue
79+
}
80+
filesChecked.push(importPathRelative)
81+
sources[importPathRelative] = {
82+
content: importContent
83+
}
84+
this.findImports(
85+
sources,
86+
importContent,
87+
nodePath.normalize(nodePath.dirname(importFilePath)),
88+
filesChecked,
89+
initialPath
90+
)
91+
}
92+
}
93+
}

src/api/service/service/StorageRecover.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Web3Configuration } from "../../blockchain/Web3Configuration";
66
import { TransactionReceipt } from "../bean/TransactionReceipt";
77
import { TransactionService } from "./TransactionService";
88
import { Storage } from "../bean/Storage"
9-
import { IWeb3 } from "../../blockchain/IWeb3";
109
import { Transaction } from "../bean/Transaction";
1110

1211
@injectable()

src/api/symbolic/evm/EVMExecutor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Disassembler } from '../../bytecode/Disassembler'
55
import { EVMDisassembler } from '../../bytecode/EVMDisassembler'
66
import { OpcodeExecutor } from './exec/OpcodeExecutor'
77
import { Word } from './Word'
8-
import { createExecutor } from './exec/TestUtils'
8+
import { createExecutor, createEVMDisassembler } from './exec/TestUtils'
99
import { DisassembledContract } from '../../bytecode/DisassembledContract'
1010

1111
describe('EVMExecutor', () => {
@@ -15,7 +15,7 @@ describe('EVMExecutor', () => {
1515

1616
beforeEach(() => {
1717
cfgCreator = new EthereumCFGCreator()
18-
disassembler = new EVMDisassembler()
18+
disassembler = createEVMDisassembler()
1919
})
2020

2121
it('Test simple PUSH execution', () => {

0 commit comments

Comments
 (0)