Skip to content

Commit

Permalink
feat(Fortuna): Node 3.0.0 compatibility (#397)
Browse files Browse the repository at this point in the history
* feat(Fortuna): Make combatible

* change node version to 3.0.0

* point node to master

* feat(Node): Change supported node version range from 2.5.0 to 4.0.0

* feat(TX): Refactor contract create tx schema. Add vm/abi serialization. Clean up high lv API. Add Fo

* fix(Fix semver implem,entation):

* fix(TX Builder): Fix linter

* fix(Contract): Fix contract tests

* fix(Node): Revert probe methods  default block value

* refactor(Node): Change supported node version range

from 2.3.0 to 4.0.0
  • Loading branch information
nduchak authored May 16, 2019
1 parent b5b5c61 commit 17b78d5
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 60 deletions.
1 change: 0 additions & 1 deletion es/ae/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ export const Contract = Ae.compose(ContractBase, ContractACI, {
Ae: {
defaults: {
deposit: 0,
vmVersion: 1,
gasPrice: 1000000000, // min gasPrice 1e9
amount: 0,
gas: 1600000 - 21000,
Expand Down
3 changes: 1 addition & 2 deletions es/ae/oracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export async function pollForQueryResponse (oracleId, queryId, { attempts = 20,
* @return {Promise<Object>} Oracle object
*/
async function registerOracle (queryFormat, responseFormat, options = {}) {
const opt = R.merge(R.merge(this.Ae.defaults, { vmVersion: this.Ae.defaults.oracleVmVersion }), options) // Preset VmVersion for oracle
const opt = R.merge(this.Ae.defaults, options) // Preset VmVersion for oracle
const accountId = await this.address()

const oracleRegisterTx = await this.oracleRegisterTx(R.merge(opt, {
Expand Down Expand Up @@ -248,7 +248,6 @@ const Oracle = Ae.compose({
getQueryObject
},
deepProps: { Ae: { defaults: {
oracleVmVersion: 0,
queryFee: 30000,
oracleTtl: { type: 'delta', value: 500 },
queryTtl: { type: 'delta', value: 10 },
Expand Down
2 changes: 1 addition & 1 deletion es/chain/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ async function poll (th, { blocks = 10, interval = 5000 } = {}) {
}

async function getTxInfo (hash) {
return this.api.getTransactionInfoByHash(hash)
return this.api.getTransactionInfoByHash(hash).then(res => res.callInfo ? res.callInfo : res)
}

async function mempool () {
Expand Down
4 changes: 2 additions & 2 deletions es/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const Node = stampit({
}
})

const NODE_GE_VERSION = '1.4.0'
const NODE_LT_VERSION = '3.0.0'
const NODE_GE_VERSION = '2.3.0'
const NODE_LT_VERSION = '4.0.0'

export default Node
8 changes: 8 additions & 0 deletions es/tx/builder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const ORACLE_TTL_TYPES = {
function deserializeField (value, type, prefix) {
if (!value) return ''
switch (type) {
case FIELD_TYPES.ctVersion:
// eslint-disable-next-line no-unused-vars
const [vm, _, abi] = value
return { vmVersion: readInt(Buffer.from([vm])), abiVersion: readInt(Buffer.from([abi])) }
case FIELD_TYPES.int:
return readInt(value)
case FIELD_TYPES.id:
Expand Down Expand Up @@ -97,6 +101,8 @@ function serializeField (value, type, prefix) {
return buildPointers(value)
case FIELD_TYPES.mptree:
return value.map(mpt.serialize)
case FIELD_TYPES.ctVersion:
return Buffer.from([...toBytes(value.vmVersion), 0, ...toBytes(value.abiVersion)])
case FIELD_TYPES.callReturnType:
switch (value) {
case 'ok': return writeInt(0)
Expand Down Expand Up @@ -125,6 +131,8 @@ function validateField (value, key, type, prefix) {
return assert(value.split('_')[0] === prefix, { prefix, value })
case FIELD_TYPES.string:
return assert(true)
case FIELD_TYPES.ctVersion:
return assert(typeof value === 'object' && value.hasOwnProperty('abiVersion') && value.hasOwnProperty('vmVersion'))
case FIELD_TYPES.pointers:
return assert(Array.isArray(value) && !value.find(e => e !== Object(e)), { value })
default:
Expand Down
53 changes: 34 additions & 19 deletions es/tx/builder/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,6 @@ const TX_SCHEMA_FIELD = (schema, objectId) => [schema, objectId]

export const MIN_GAS_PRICE = 1000000000 // min gasPrice 1e9

// # see https://github.com/aeternity/protocol/blob/minerva/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain
const VM_VERSIONS = {
NO_VM: 0,
SOPHIA: 1,
SOLIDITY: 2,
SOPHIA_IMPROVEMENTS: 3
}
// # see https://github.com/aeternity/protocol/blob/minerva/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain
const ABI_VERSIONS = {
NO_ABI: 0,
SOPHIA: 1,
SOLIDITY: 2
}

const revertObject = (obj) => Object.entries(obj).reduce((acc, [key, v]) => (acc[v] = key) && acc, {})

/**
Expand Down Expand Up @@ -154,6 +140,33 @@ export const TX_TYPE = {
accountsTree: 'accountsTree'
}

// # see https://github.com/aeternity/protocol/blob/minerva/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain
export const VM_VERSIONS = {
NO_VM: 0,
SOPHIA: 1,
SOLIDITY: 2,
SOPHIA_IMPROVEMENTS_MINERVA: 3,
SOPHIA_IMPROVEMENTS_FORTUNA: 4
}
// # see https://github.com/aeternity/protocol/blob/minerva/contracts/contract_vms.md#virtual-machines-on-the-%C3%A6ternity-blockchain
export const ABI_VERSIONS = {
NO_ABI: 0,
SOPHIA: 1,
SOLIDITY: 2
}

export const VM_ABI_MAP_MINERVA = {
[TX_TYPE.contractCreate]: { vmVersion: [VM_VERSIONS.SOPHIA_IMPROVEMENTS_MINERVA], abiVersion: [ABI_VERSIONS.SOPHIA] },
[TX_TYPE.contractCall]: { vmVersion: [VM_VERSIONS.SOPHIA, VM_VERSIONS.SOPHIA_IMPROVEMENTS_MINERVA], abiVersion: [ABI_VERSIONS.SOPHIA] },
[TX_TYPE.oracleRegister]: { vmVersion: [VM_VERSIONS.SOPHIA_IMPROVEMENTS_MINERVA], abiVersion: [ABI_VERSIONS.NO_ABI, ABI_VERSIONS.SOPHIA] }
}

export const VM_ABI_MAP_FORTUNA = {
[TX_TYPE.contractCreate]: { vmVersion: [VM_VERSIONS.SOPHIA_IMPROVEMENTS_MINERVA, VM_VERSIONS.SOPHIA_IMPROVEMENTS_FORTUNA], abiVersion: [ABI_VERSIONS.SOPHIA] }, // vmVersion 0x4 do not work with fortuna
[TX_TYPE.contractCall]: { vmVersion: [VM_VERSIONS.SOPHIA, VM_VERSIONS.SOPHIA_IMPROVEMENTS_FORTUNA, VM_VERSIONS.SOPHIA_IMPROVEMENTS_MINERVA], abiVersion: [ABI_VERSIONS.SOPHIA] },
[TX_TYPE.oracleRegister]: { vmVersion: [], abiVersion: [ABI_VERSIONS.NO_ABI, ABI_VERSIONS.SOPHIA] }
}

export const OBJECT_ID_TX_TYPE = {
[OBJECT_TAG_ACCOUNT]: TX_TYPE.account,
[OBJECT_TAG_SIGNED_TRANSACTION]: TX_TYPE.signed,
Expand Down Expand Up @@ -219,7 +232,8 @@ export const FIELD_TYPES = {
callStack: 'callStack',
proofOfInclusion: 'proofOfInclusion',
mptree: 'mptree',
callReturnType: 'callReturnType'
callReturnType: 'callReturnType',
ctVersion: 'ctVersion'
}

// FEE CALCULATION
Expand Down Expand Up @@ -281,7 +295,8 @@ export const VALIDATION_MESSAGE = {
[FIELD_TYPES.id]: ({ value, prefix }) => VALIDATION_ERROR(`'${value}' prefix doesn't match expected prefix '${prefix}' or ID_TAG for prefix not found`),
[FIELD_TYPES.binary]: ({ prefix, value }) => VALIDATION_ERROR(`'${value}' prefix doesn't match expected prefix '${prefix}'`),
[FIELD_TYPES.string]: ({ value }) => VALIDATION_ERROR(`Not a string`),
[FIELD_TYPES.pointers]: ({ value }) => VALIDATION_ERROR(`Value must be of type Array and contains only object's like '{key: "account_pubkey", id: "ak_lkamsflkalsdalksdlasdlasdlamd"}'`)
[FIELD_TYPES.pointers]: ({ value }) => VALIDATION_ERROR(`Value must be of type Array and contains only object's like '{key: "account_pubkey", id: "ak_lkamsflkalsdalksdlasdlasdlamd"}'`),
[FIELD_TYPES.ctVersion]: ({ value }) => VALIDATION_ERROR(`Value must be an object with "vmVersion" and "abiVersion" fields`)
}

const BASE_TX = [
Expand Down Expand Up @@ -378,7 +393,7 @@ const CONTRACT_CREATE_TX = [
TX_FIELD('ownerId', FIELD_TYPES.id, 'ak'),
TX_FIELD('nonce', FIELD_TYPES.int),
TX_FIELD('code', FIELD_TYPES.binary, 'cb'),
TX_FIELD('vmVersion', FIELD_TYPES.int),
TX_FIELD('ctVersion', FIELD_TYPES.ctVersion),
TX_FIELD('fee', FIELD_TYPES.int),
TX_FIELD('ttl', FIELD_TYPES.int),
TX_FIELD('deposit', FIELD_TYPES.int),
Expand All @@ -393,7 +408,7 @@ const CONTRACT_CALL_TX = [
TX_FIELD('callerId', FIELD_TYPES.id, 'ak'),
TX_FIELD('nonce', FIELD_TYPES.int),
TX_FIELD('contractId', FIELD_TYPES.id, 'ct'),
TX_FIELD('vmVersion', FIELD_TYPES.int),
TX_FIELD('abiVersion', FIELD_TYPES.int),
TX_FIELD('fee', FIELD_TYPES.int),
TX_FIELD('ttl', FIELD_TYPES.int),
TX_FIELD('amount', FIELD_TYPES.int),
Expand Down Expand Up @@ -427,7 +442,7 @@ const ORACLE_REGISTER_TX = [
TX_FIELD('oracleTtlValue', FIELD_TYPES.int),
TX_FIELD('fee', FIELD_TYPES.int),
TX_FIELD('ttl', FIELD_TYPES.int),
TX_FIELD('vmVersion', FIELD_TYPES.int)
TX_FIELD('abiVersion', FIELD_TYPES.int)
]

const ORACLE_EXTEND_TX = [
Expand Down
72 changes: 40 additions & 32 deletions es/tx/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,9 @@ import Tx from './'
import Node from '../node'

import { buildTx, calculateFee } from './builder'
import { MIN_GAS_PRICE, TX_TYPE } from './builder/schema'
import { MIN_GAS_PRICE, TX_TYPE, VM_ABI_MAP_FORTUNA, VM_ABI_MAP_MINERVA } from './builder/schema'
import { buildContractId, oracleQueryId } from './builder/helpers'

const ORACLE_VM_VERSION = 0
const CONTRACT_VM_VERSION = 1
// TODO This values using as default for minerva node
const CONTRACT_MINERVA_VM_ABI = 196609
const CONTRACT_MINERVA_VM = 3
const CONTRACT_MINERVA_ABI = 1

async function spendTx ({ senderId, recipientId, amount, payload = '' }) {
// Calculate fee, get absolute ttl (ttl + height), get account nonce
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.spend, { senderId, ...R.head(arguments), payload })
Expand Down Expand Up @@ -125,58 +118,50 @@ async function nameRevokeTx ({ accountId, nameId }) {
return tx
}

// TODO move this to tx-builder
// Get VM_ABI version for minerva
function getContractVmVersion () {
return semverSatisfies(this.version.split('-')[0], '2.0.0', '3.0.0') // Minerva
? { splitedVmAbi: CONTRACT_MINERVA_VM_ABI, contractVmVersion: CONTRACT_MINERVA_VM }
: { splitedVmAbi: CONTRACT_VM_VERSION, contractVmVersion: CONTRACT_VM_VERSION }
}
async function contractCreateTx ({ ownerId, code, vmVersion, abiVersion, deposit, amount, gas, gasPrice = MIN_GAS_PRICE, callData }) {
// TODO move this to tx-builder
// Get VM_ABI version for minerva
const { splitedVmAbi, contractVmVersion } = getContractVmVersion.bind(this)()
// Get VM_ABI version
const ctVersion = this.getVmVersion(TX_TYPE.contractCreate, R.head(arguments))
// Calculate fee, get absolute ttl (ttl + height), get account nonce

const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.contractCreate, { senderId: ownerId, ...R.head(arguments), vmVersion: splitedVmAbi, gasPrice })

const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.contractCreate, { senderId: ownerId, ...R.head(arguments), ctVersion, gasPrice })
// Build transaction using sdk (if nativeMode) or build on `AETERNITY NODE` side
return this.nativeMode
? {
...buildTx(R.merge(R.head(arguments), { nonce, ttl, fee, vmVersion: splitedVmAbi, gasPrice }), TX_TYPE.contractCreate),
...buildTx(R.merge(R.head(arguments), { nonce, ttl, fee, ctVersion, gasPrice }), TX_TYPE.contractCreate),
contractId: buildContractId(ownerId, nonce)
}
: this.api.postContractCreate(R.merge(R.head(arguments), { nonce, ttl, fee: parseInt(fee), gas: parseInt(gas), gasPrice, vmVersion: contractVmVersion, abiVersion: CONTRACT_MINERVA_ABI }))
: this.api.postContractCreate(R.merge(R.head(arguments), { nonce, ttl, fee: parseInt(fee), gas: parseInt(gas), gasPrice, vmVersion: ctVersion.vmVersion, abiVersion: ctVersion.abiVersion }))
}

async function contractCallTx ({ callerId, contractId, vmVersion = CONTRACT_VM_VERSION, amount, gas, gasPrice = MIN_GAS_PRICE, callData }) {
async function contractCallTx ({ callerId, contractId, abiVersion, amount, gas, gasPrice = MIN_GAS_PRICE, callData }) {
const ctVersion = this.getVmVersion(TX_TYPE.contractCall, R.head(arguments))
// Calculate fee, get absolute ttl (ttl + height), get account nonce
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.contractCall, { senderId: callerId, ...R.head(arguments), gasPrice, vmVersion })
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.contractCall, { senderId: callerId, ...R.head(arguments), gasPrice, abiVersion: ctVersion.abiVersion })

// Build transaction using sdk (if nativeMode) or build on `AETERNITY NODE` side
const { tx } = this.nativeMode
? buildTx(R.merge(R.head(arguments), { nonce, ttl, fee, vmVersion, gasPrice }), TX_TYPE.contractCall)
? buildTx(R.merge(R.head(arguments), { nonce, ttl, fee, abiVersion: ctVersion.abiVersion, gasPrice }), TX_TYPE.contractCall)
: await this.api.postContractCall(R.merge(R.head(arguments), {
nonce,
ttl,
fee: parseInt(fee),
gas: parseInt(gas),
gasPrice,
vmVersion
abiVersion: ctVersion.vmVersion
}))

return tx
}

async function oracleRegisterTx ({ accountId, queryFormat, responseFormat, queryFee, oracleTtl, vmVersion = ORACLE_VM_VERSION }) {
async function oracleRegisterTx ({ accountId, queryFormat, responseFormat, queryFee, oracleTtl, abiVersion }) {
const { abiVersion: abi } = this.getVmVersion(TX_TYPE.oracleRegister, R.head(arguments))
// Calculate fee, get absolute ttl (ttl + height), get account nonce
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.oracleRegister, { senderId: accountId, ...R.head(arguments), vmVersion })
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.oracleRegister, { senderId: accountId, ...R.head(arguments), abiVersion: abi })
// Build transaction using sdk (if nativeMode) or build on `AETERNITY NODE` side
const { tx } = this.nativeMode
? buildTx({
accountId,
queryFee,
vmVersion,
abiVersion: abi,
fee,
oracleTtl,
nonce,
Expand All @@ -187,7 +172,7 @@ async function oracleRegisterTx ({ accountId, queryFormat, responseFormat, query
: await this.api.postOracleRegister({
accountId,
queryFee,
vmVersion,
abiVersion: abi,
fee: parseInt(fee),
oracleTtl,
nonce,
Expand Down Expand Up @@ -354,6 +339,28 @@ async function channelSnapshotSoloTx ({ channelId, fromId, payload }) {
return tx
}

/**
* Validated vm/abi version or get default based on transaction type and NODE version
*
* @param {string} txType Type of transaction
* @param {object} vmAbi Object with vm and abi version fields
* @return {object} Object with vm/abi version ({ vmVersion: number, abiVersion: number })
*/
function getVmVersion (txType, { vmVersion, abiVersion } = {}) {
const isMinerva = semverSatisfies(this.version.split('-')[0], '2.5.0', '3.0.0')
const supported = isMinerva ? VM_ABI_MAP_MINERVA[txType] : VM_ABI_MAP_FORTUNA[txType]
if (!supported) throw new Error('Not supported tx type')

const ctVersion = {
abiVersion: abiVersion !== undefined ? abiVersion : supported.abiVersion[0],
vmVersion: vmVersion !== undefined ? vmVersion : supported.vmVersion[0]
}
if (supported.vmVersion.length && !R.contains(ctVersion.vmVersion, supported.vmVersion)) throw new Error(`VM VERSION ${ctVersion.vmVersion} do not support by this node. Supported: [${supported.vmVersion}]`)
if (!R.contains(ctVersion.abiVersion, supported.abiVersion)) throw new Error(`ABI VERSION ${ctVersion.abiVersion} do not support by this node. Supported: [${supported.abiVersion}]`)

return ctVersion
}

/**
* Compute the absolute ttl by adding the ttl to the current height of the chain
*
Expand Down Expand Up @@ -447,7 +454,8 @@ const Transaction = Node.compose(Tx, {
channelSlashTx,
channelSettleTx,
channelSnapshotSoloTx,
getAccountNonce
getAccountNonce,
getVmVersion
}
})

Expand Down
6 changes: 4 additions & 2 deletions es/utils/semver-satisfies.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default function (version, geVersion, ltVersion) {
const toNumber = components => components.reverse()
.reduce((acc, n, idx) => acc + n * Math.pow(base, idx), 0)

return toNumber(versionComponents) >= toNumber(geComponents) &&
toNumber(versionComponents) < toNumber(ltComponents)
const vNumber = toNumber(versionComponents)
const geNumber = toNumber(geComponents)
const ltNumber = toNumber(ltComponents)
return vNumber >= geNumber && vNumber < ltNumber
}
2 changes: 1 addition & 1 deletion test/integration/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ contract StateContract =

const encodedNumberSix = 'cb_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaKNdnK'

plan('10000000000000000')
plan('1000000000000000000000')

describe('Contract', function () {
configure(this)
Expand Down

0 comments on commit 17b78d5

Please sign in to comment.