Skip to content

Commit

Permalink
feat(Compiler): Compiler 4.0.0 compatibility (#632)
Browse files Browse the repository at this point in the history
* feat(newCompiler): Increase compiler version. Add `backend` option to compiler API

* fix(TX): Add fate vm version

* feat(Contract): Make Contract works with node 5.0.0 rc1 (lima hard-fork) and compiler 4.0.0 rc2

* chore(test): Debug channel test

* fix(channel): fix contract in channel test

* chore(test): Remove logs

* feat(ACI): Add `payable` to contract call

* feat(ACI): Add test for payable

* fix(Linter): Fix linter error

* feat(Compiler): Point to compiler 3.2.0. Improve `payble` to support old compiler

* feat(Compiler): disable payable test

* feat(Lima): Point compiler to 4.0.0. Enable tests for `payable`

* feat(Lima): Make contact for channel test payable

* chore(test): Enable tests
  • Loading branch information
nduchak authored Sep 6, 2019
1 parent 2797396 commit d5f1632
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
TAG=v5.0.0-rc.1
COMPILER_TAG=v3.2.0
COMPILER_TAG=v4.0.0-rc2
6 changes: 5 additions & 1 deletion es/contract/aci/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as R from 'ramda'
import { validateArguments, transform, transformDecodedData } from './transformation'
import { buildContractMethods, getFunctionACI } from './helpers'
import AsyncInit from '../../utils/async-init'
import { BigNumber } from 'bignumber.js'

/**
* Validated contract call arguments using contract ACI
Expand Down Expand Up @@ -146,7 +147,10 @@ const call = ({ client, instance }) => async (fn, params = [], options = {}) =>

if (!fn) throw new Error('Function name is required')
if (!instance.deployInfo.address) throw new Error('You need to deploy contract before calling!')

if (
BigNumber(opt.amount).gt(0) &&
(Object.prototype.hasOwnProperty.call(fnACI, 'payable') && !fnACI.payable)
) throw new Error(`You try to pay "${opt.amount}" to function "${fn}" which is not payable. Only payable function can accept tokens`)
params = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, params) : params
const result = opt.callStatic
? await client.contractCallStatic(source, instance.deployInfo.address, fn, params, {
Expand Down
19 changes: 14 additions & 5 deletions es/contract/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ async function getCompilerVersion (options = {}) {

async function contractEncodeCallDataAPI (source, name, args = [], options = {}) {
this.isInit()
options = { ...this.compilerOptions, ...options }
return this.http
.post('/encode-calldata', { source, function: name, arguments: args }, options)
.post('/encode-calldata', { source, function: name, arguments: args, options }, options)
.then(({ calldata }) => calldata)
}

Expand All @@ -56,8 +57,9 @@ async function contractDecodeCallDataBySourceAPI (source, fn, callData, options

async function contractDecodeCallResultAPI (source, fn, callValue, callResult, options = {}) {
this.isInit()
options = { ...this.compilerOptions, ...options }
return this.http
.post('/decode-call-result', { function: fn, source, 'call-result': callResult, 'call-value': callValue }, options)
.post('/decode-call-result', { function: fn, source, 'call-result': callResult, 'call-value': callValue, options }, options)
}

async function contractDecodeDataAPI (type, data, options = {}) {
Expand All @@ -69,16 +71,18 @@ async function contractDecodeDataAPI (type, data, options = {}) {

async function compileContractAPI (code, options = {}) {
this.isInit()
options = { ...this.compilerOptions, ...options }
return this.http.post('/compile', { code, options }, options)
.then(({ bytecode }) => bytecode)
}

async function contractGetACI (code, options = {}) {
this.isInit()
options = { ...this.compilerOptions, ...options }
return this.http.post('/aci', { code, options }, options)
}

async function setCompilerUrl (url, { forceCompatibility } = {}) {
async function setCompilerUrl (url, { forceCompatibility = false } = {}) {
this.http.changeBaseUrl(url)
this.compilerVersion = null
await this.checkCompatibility({ forceCompatibility })
Expand All @@ -100,6 +104,8 @@ function isInit () {
return true
}

const VM_TYPE = { FATE: 'fate', AEVM: 'aevm' }

/**
* Contract Compiler Stamp
*
Expand Down Expand Up @@ -131,11 +137,14 @@ const ContractCompilerAPI = AsyncInit.compose(ContractBase, {
checkCompatibility
},
props: {
compilerVersion: null
compilerVersion: null,
compilerOptions: {
backend: VM_TYPE.AEVM
}
}
})

const COMPILER_GE_VERSION = '3.1.0'
const COMPILER_LT_VERSION = '4.0.0'
const COMPILER_LT_VERSION = '5.0.0'

export default ContractCompilerAPI
5 changes: 2 additions & 3 deletions test/integration/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ const wsUrl = process.env.TEST_WS_URL || 'ws://localhost:3014/channel'
plan(BigNumber('1000e18').toString())

const identityContract = `
contract Identity =
type state = ()
entrypoint main(x : int) = x
payable contract Identity =
payable entrypoint main(x : int): int = x
`

function waitForChannel (channel) {
Expand Down
42 changes: 28 additions & 14 deletions test/integration/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import * as R from 'ramda'

const identityContract = `
contract Identity =
entrypoint main(x : int) = x
entrypoint main(x : int) = x
`
const stateContract = `
contract StateContract =
Expand All @@ -47,32 +47,32 @@ contract StateContract =
datatype dateUnit = Year | Month | Day
entrypoint init(value: string, key: int, testOption: option(string)) : state = { value = value, key = key, testOption = testOption }
entrypoint retrieve() : (string, int) = (state.value, state.key)
entrypoint retrieve() : string*int = (state.value, state.key)
entrypoint intFn(a: int) : int = a
entrypoint stringFn(a: string) : string = a
payable entrypoint stringFn(a: string) : string = a
entrypoint boolFn(a: bool) : bool = a
entrypoint addressFn(a: address) : address = a
entrypoint contractAddress (ct: address) : address = ct
entrypoint accountAddress (ak: address) : address = ak
entrypoint tupleFn (a: (string, int)) : (string, int) = a
entrypoint tupleInTupleFn (a: ((string, string), int)) : ((string, string), int) = a
entrypoint tupleWithList (a: (list(int), int)) : (list(int), int) = a
entrypoint tupleFn (a: string*int) : string*int = a
entrypoint tupleInTupleFn (a: (string*string)*int) : (string*string)*int = a
entrypoint tupleWithList (a: list(int)*int) : list(int)*int = a
entrypoint listFn(a: list(int)) : list(int) = a
entrypoint listInListFn(a: list(list(int))) : list(list(int)) = a
entrypoint mapFn(a: map(address, (string, int))) : map(address, (string, int)) = a
entrypoint mapOptionFn(a: map(address, (string, option(int)))) : map(address, (string, option(int))) = a
entrypoint mapFn(a: map(address, string*int)) : map(address, string*int) = a
entrypoint mapOptionFn(a: map(address, string*option(int))) : map(address, string*option(int)) = a
entrypoint getRecord() : state = state
stateful entrypoint setRecord(s: state) = put(s)
entrypoint intOption(s: option(int)) : option(int) = s
entrypoint listOption(s: option(list((int, string)))) : option(list((int ,string))) = s
entrypoint listOption(s: option(list(int*string))) : option(list(int*string)) = s
entrypoint testFn(a: list(int), b: bool) : (list(int), bool) = (a, b)
entrypoint testFn(a: list(int), b: bool) : list(int)*bool = (a, b)
entrypoint approve(tx_id: int, voting_contract: Voting) : int = tx_id
entrypoint hashFn(s: hash): hash = s
Expand Down Expand Up @@ -116,7 +116,7 @@ describe('Contract', function () {
})

it('deploys compiled contracts', async () => {
deployed = await bytecode.deploy([])
deployed = await bytecode.deploy([]).catch(async e => console.log(await e.verifyTx()))
return deployed.should.have.property('address')
})

Expand Down Expand Up @@ -206,7 +206,7 @@ describe('Contract', function () {
let contractObject

it('Generate ACI object', async () => {
contractObject = await contract.getContractInstance(testContract, { opt: { amount: 10000, ttl: 10 } })
contractObject = await contract.getContractInstance(testContract, { opt: { ttl: 10 } })
contractObject.should.have.property('interface')
contractObject.should.have.property('aci')
contractObject.should.have.property('source')
Expand All @@ -215,7 +215,7 @@ describe('Contract', function () {
contractObject.should.have.property('compile')
contractObject.should.have.property('call')
contractObject.should.have.property('deploy')
contractObject.options.amount.should.be.equal(10000)
contractObject.options.ttl.should.be.equal(10)
const functionsFromACI = contractObject.aci.functions.map(({ name }) => name)
const methods = Object.keys(contractObject.methods)
R.equals(methods, functionsFromACI).should.be.equal(true)
Expand All @@ -241,10 +241,24 @@ describe('Contract', function () {

it('Deploy contract before compile', async () => {
contractObject.compiled = null
await contractObject.methods.init('123', 1, Promise.resolve('hahahaha'))
await contractObject.methods.init('123', 1, Promise.resolve('hahahaha'), { })
const isCompiled = contractObject.compiled.length && contractObject.compiled.slice(0, 3) === 'cb_'
isCompiled.should.be.equal(true)
})
it('Fail on paying to not payable function', async () => {
const amount = 100
try {
await contractObject.methods.intFn.send(1, { amount })
} catch (e) {
e.message.should.be.equal(`You try to pay "${amount}" to function "intFn" which is not payable. Only payable function can accept tokens`)
}
})
it('Can pay to payable function', async () => {
const contractBalance = await contract.balance(contractObject.deployInfo.address)
await contractObject.methods.stringFn.send('1', { amount: 100 })
const balanceAfter = await contract.balance(contractObject.deployInfo.address)
balanceAfter.should.be.equal(`${+contractBalance + 100}`)
})
it('Call contract on specific account', async () => {
const current = await contract.address()
const onAccount = contract.addresses().find(acc => acc !== current)
Expand Down
3 changes: 1 addition & 2 deletions test/integration/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { commitmentHash } from '../../es/tx/builder/helpers'
const nonce = 1
const nameTtl = 1
const clientTtl = 1
const amount = 1
const amount = 0
const senderId = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'
const recipientId = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'
const name = 'test123test.test'
Expand All @@ -44,7 +44,6 @@ const queryResponse = '{\'tmp\': 101}'
// Contract test data
const contractCode = `
contract Identity =
type state = ()
entrypoint main(x : int) = x
`
let contractId
Expand Down

0 comments on commit d5f1632

Please sign in to comment.