Skip to content

Commit

Permalink
feat(AENS): Lima compatibility (#687)
Browse files Browse the repository at this point in the history
* Aens lima compatibility

* Make aens cross compatible with pre-lima releases

* Remove .only

* Fix native build of claim tx test
  • Loading branch information
mpowaga authored and nduchak committed Oct 2, 2019
1 parent e3a7fdb commit d3d0970
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 48 deletions.
2 changes: 1 addition & 1 deletion docker/aeternity_node_mean16.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ chain:
"1": 0
"2": 2
"3": 4
"4": 5
"4": 6

mining:
autostart: true
Expand Down
20 changes: 15 additions & 5 deletions es/ae/aens.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import * as R from 'ramda'
import { encodeBase58Check, salt } from '../utils/crypto'
import { commitmentHash, isNameValid } from '../tx/builder/helpers'
import { commitmentHash, prelimaCommitmentHash, isNameValid } from '../tx/builder/helpers'
import Ae from './'
import { CLIENT_TTL, NAME_TTL } from '../tx/builder/schema'

Expand Down Expand Up @@ -176,8 +176,11 @@ async function claim (name, salt, options = {}) {
}))

const result = await this.send(claimTx, opt)
const nameInter = this.Chain.defaults.waitMined ? await this.aensQuery(name, opt) : {}
return Object.assign(result, nameInter)
if (opt.vsn === 1) {
const nameInter = this.Chain.defaults.waitMined ? await this.aensQuery(name, opt) : {}
return Object.assign(result, nameInter)
}
return result
}

/**
Expand All @@ -190,11 +193,18 @@ async function claim (name, salt, options = {}) {
* @return {Promise<Object>}
*/
async function preclaim (name, options = {}) {
// TODO remove cross compatibility
const { version } = this.getNodeInfo()
const [majorVersion] = version.split('.')
const vsn = +majorVersion === 5 && version !== '5.0.0-rc.1' ? 2 : 1

isNameValid(name)
const opt = R.merge(this.Ae.defaults, options)
const _salt = salt()
const height = await this.height()
const hash = await commitmentHash(name, _salt)
const hash = vsn === 1
? await prelimaCommitmentHash(name, _salt)
: await commitmentHash(name, _salt)

const preclaimTx = await this.namePreclaimTx(R.merge(opt, {
accountId: await this.address(opt),
Expand All @@ -206,7 +216,7 @@ async function preclaim (name, options = {}) {
return Object.freeze({
...result,
height,
claim: options => this.aensClaim(name, _salt, { ...options, onAccount: opt.onAccount }),
claim: options => this.aensClaim(name, _salt, { ...options, onAccount: opt.onAccount, vsn }),
salt: _salt,
commitmentId: hash
})
Expand Down
18 changes: 17 additions & 1 deletion es/tx/builder/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ export function formatSalt (salt) {
return Buffer.from(salt.toString(16).padStart(64, '0'), 'hex')
}

/**
* Generate the commitment hash by hashing the formatted salt and
* name, base 58 encoding the result and prepending 'cm_'
*
* @alias module:@aeternity/aepp-sdk/es/tx/builder/helpers
* @function prelimaCommitmentHash
* @category async
* @rtype (name: String, salt?: String) => hash: Promise[String]
* @param {String} name - Name to be registered
* @param {Number} salt Random salt
* @return {String} Commitment hash
*/
export async function prelimaCommitmentHash (name, salt = createSalt()) {
return `cm_${encodeBase58Check(hash(Buffer.concat([nameId(name), formatSalt(salt)])))}`
}

/**
* Generate the commitment hash by hashing the formatted salt and
* name, base 58 encoding the result and prepending 'cm_'
Expand All @@ -92,7 +108,7 @@ export function formatSalt (salt) {
* @return {String} Commitment hash
*/
export async function commitmentHash (name, salt = createSalt()) {
return `cm_${encodeBase58Check(hash(Buffer.concat([nameId(name), formatSalt(salt)])))}`
return `cm_${encodeBase58Check(hash(Buffer.concat([Buffer.from(name), formatSalt(salt)])))}`
}

/**
Expand Down
24 changes: 12 additions & 12 deletions es/tx/builder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,15 @@ function getOracleRelativeTtl (params) {
* @return {String|Number}
* @example calculateMinFee('spendTx', { gas, params })
*/
export function calculateMinFee (txType, { gas = 0, params }) {
export function calculateMinFee (txType, { gas = 0, params, vsn }) {
const multiplier = BigNumber(1e9) // 10^9 GAS_PRICE
if (!params) return BigNumber(DEFAULT_FEE).times(multiplier).toString(10)

let actualFee = buildFee(txType, { params: { ...params, fee: 0 }, multiplier, gas })
let actualFee = buildFee(txType, { params: { ...params, fee: 0 }, multiplier, gas, vsn })
let expected = BigNumber(0)

while (!actualFee.eq(expected)) {
actualFee = buildFee(txType, { params: { ...params, fee: actualFee }, multiplier, gas })
actualFee = buildFee(txType, { params: { ...params, fee: actualFee }, multiplier, gas, vsn })
expected = actualFee
}
return expected.toString(10)
Expand All @@ -228,8 +228,8 @@ export function calculateMinFee (txType, { gas = 0, params }) {
* @param multiplier
* @return {BigNumber}
*/
function buildFee (txType, { params, gas = 0, multiplier }) {
const { rlpEncoded: txWithOutFee } = buildTx({ ...params }, txType)
function buildFee (txType, { params, gas = 0, multiplier, vsn }) {
const { rlpEncoded: txWithOutFee } = buildTx({ ...params }, txType, { vsn })
const txSize = txWithOutFee.length
return TX_FEE_BASE_GAS(txType)
.plus(TX_FEE_OTHER_GAS(txType)({ txSize, relativeTtl: getOracleRelativeTtl(params) }))
Expand All @@ -249,10 +249,10 @@ function buildFee (txType, { params, gas = 0, multiplier }) {
* @return {String|Number}
* @example calculateFee(null, 'spendTx', { gas, params })
*/
export function calculateFee (fee = 0, txType, { gas = 0, params, showWarning = true } = {}) {
export function calculateFee (fee = 0, txType, { gas = 0, params, showWarning = true, vsn } = {}) {
if (!params && showWarning) console.warn(`Can't build transaction fee, we will use DEFAULT_FEE(${DEFAULT_FEE})`)

const minFee = calculateMinFee(txType, { params, gas })
const minFee = calculateMinFee(txType, { params, gas, vsn })
if (fee && BigNumber(minFee).gt(BigNumber(fee)) && showWarning) console.warn(`Transaction fee is lower then min fee! Min fee: ${minFee}`)

return fee || minFee
Expand Down Expand Up @@ -333,15 +333,15 @@ export function unpackRawTx (binary, schema) {
* @throws {Error} Validation error
* @return {Object} { tx, rlpEncoded, binary } Object with tx -> Base64Check transaction hash with 'tx_' prefix, rlp encoded transaction and binary transaction
*/
export function buildTx (params, type, { excludeKeys = [], prefix = 'tx' } = {}) {
export function buildTx (params, type, { excludeKeys = [], prefix = 'tx', vsn = VSN } = {}) {
if (!TX_SERIALIZATION_SCHEMA[type]) {
throw new Error('Transaction serialization not implemented for ' + type)
}
if (!TX_SERIALIZATION_SCHEMA[type][VSN]) {
throw new Error('Transaction serialization not implemented for ' + type + ' version ' + VSN)
if (!TX_SERIALIZATION_SCHEMA[type][vsn]) {
throw new Error('Transaction serialization not implemented for ' + type + ' version ' + vsn)
}
const [schema, tag] = TX_SERIALIZATION_SCHEMA[type][VSN]
const binary = buildRawTx({ ...params, VSN, tag }, schema, { excludeKeys }).filter(e => e !== undefined)
const [schema, tag] = TX_SERIALIZATION_SCHEMA[type][vsn]
const binary = buildRawTx({ ...params, VSN: vsn, tag }, schema, { excludeKeys }).filter(e => e !== undefined)

const rlpEncoded = rlp.encode(binary)
const tx = encode(rlpEncoded, prefix)
Expand Down
17 changes: 15 additions & 2 deletions es/tx/builder/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,17 @@ const NAME_CLAIM_TX = [
TX_FIELD('ttl', FIELD_TYPES.int)
]

const NAME_CLAIM_TX_2 = [
...BASE_TX,
TX_FIELD('accountId', FIELD_TYPES.id, 'ak'),
TX_FIELD('nonce', FIELD_TYPES.int),
TX_FIELD('name', FIELD_TYPES.binary, 'nm'),
TX_FIELD('nameSalt', FIELD_TYPES.int),
TX_FIELD('nameFee', FIELD_TYPES.int),
TX_FIELD('fee', FIELD_TYPES.int),
TX_FIELD('ttl', FIELD_TYPES.int)
]

const NAME_UPDATE_TX = [
...BASE_TX,
TX_FIELD('accountId', FIELD_TYPES.id, 'ak'),
Expand Down Expand Up @@ -852,7 +863,8 @@ export const TX_SERIALIZATION_SCHEMA = {
1: TX_SCHEMA_FIELD(NAME_PRE_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION)
},
[TX_TYPE.nameClaim]: {
1: TX_SCHEMA_FIELD(NAME_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION)
1: TX_SCHEMA_FIELD(NAME_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION),
2: TX_SCHEMA_FIELD(NAME_CLAIM_TX_2, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION)
},
[TX_TYPE.nameUpdate]: {
1: TX_SCHEMA_FIELD(NAME_UPDATE_TX, OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION)
Expand Down Expand Up @@ -990,7 +1002,8 @@ export const TX_DESERIALIZATION_SCHEMA = {
1: TX_SCHEMA_FIELD(NAME_PRE_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_PRECLAIM_TRANSACTION)
},
[OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION]: {
1: TX_SCHEMA_FIELD(NAME_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION)
1: TX_SCHEMA_FIELD(NAME_CLAIM_TX, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION),
2: TX_SCHEMA_FIELD(NAME_CLAIM_TX_2, OBJECT_TAG_NAME_SERVICE_CLAIM_TRANSACTION)
},
[OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION]: {
1: TX_SCHEMA_FIELD(NAME_UPDATE_TX, OBJECT_TAG_NAME_SERVICE_UPDATE_TRANSACTION)
Expand Down
10 changes: 5 additions & 5 deletions es/tx/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ async function namePreclaimTx ({ accountId, commitmentId }) {
return tx
}

async function nameClaimTx ({ accountId, name, nameSalt }) {
async function nameClaimTx ({ accountId, name, nameSalt, vsn = 2 }) {
// Calculate fee, get absolute ttl (ttl + height), get account nonce
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.nameClaim, { senderId: accountId, ...R.head(arguments) })
const { fee, ttl, nonce } = await this.prepareTxParams(TX_TYPE.nameClaim, { senderId: accountId, ...R.head(arguments), vsn })

// 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 }), TX_TYPE.nameClaim)
? buildTx(R.merge(R.head(arguments), { nonce, ttl, fee }), TX_TYPE.nameClaim, { vsn })
: await this.api.postNameClaim(R.merge(R.head(arguments), { nonce, ttl, fee: parseInt(fee) }))

return tx
Expand Down Expand Up @@ -422,7 +422,7 @@ async function getAccountNonce (accountId, nonce) {
* @param {Object} params Object which contains all tx data
* @return {Object} { ttl, nonce, fee } Object with account nonce, absolute ttl and transaction fee
*/
async function prepareTxParams (txType, { senderId, nonce: n, ttl: t, fee: f, gas, absoluteTtl }) {
async function prepareTxParams (txType, { senderId, nonce: n, ttl: t, fee: f, gas, absoluteTtl, vsn }) {
const account = await this.getAccount(senderId).catch(e => ({ nonce: 0 }))
// Is GA account
if (account.contractId) {
Expand All @@ -431,7 +431,7 @@ async function prepareTxParams (txType, { senderId, nonce: n, ttl: t, fee: f, ga
n = n || (account.nonce + 1)
}
const ttl = await (calculateTtl.bind(this)(t, !absoluteTtl))
const fee = calculateFee(f, txType, { showWarning: this.showWarning, gas, params: R.merge(R.last(arguments), { nonce: n, ttl }) })
const fee = calculateFee(f, txType, { showWarning: this.showWarning, gas, params: R.merge(R.last(arguments), { nonce: n, ttl }), vsn })
return { fee, ttl, nonce: n }
}

Expand Down
60 changes: 40 additions & 20 deletions test/integration/aens.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,65 +24,75 @@ function randomName () {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + '.test'
}

plan('10000000000000000')
plan('9000000000000000000000')

describe.skip('Aens', function () {
describe('Aens', function () {
configure(this)

let aens
let nameHash
let nameAuctionsSupported
const account = generateKeyPair()
const name = randomName()

before(async function () {
aens = await ready(this)
await aens.spend('1000000000000000', account.publicKey)
const { version } = aens.getNodeInfo()
const [majorVersion] = version.split('.')
nameAuctionsSupported = +majorVersion === 5 && version !== '5.0.0-rc.1'
})

const prelima = fn => async () => !nameAuctionsSupported ? fn() : undefined
const lima = fn => async () => nameAuctionsSupported ? fn() : undefined

describe('fails on', () => {
const name = randomName()

it('querying non-existent names', async () => {
it('querying non-existent names', prelima(async () => {
return aens.aensQuery(name).should.eventually.be.rejected
})
}))

it('updating names not owned by the account', async () => {
it('updating names not owned by the account', prelima(async () => {
const preclaim = await aens.aensPreclaim(name)
const claim = await preclaim.claim()
const newAccount = generateKeyPair()

const aens2 = await BaseAe()
aens2.setKeypair(newAccount)
return aens2.aensUpdate(claim.id, newAccount.publicKey, { blocks: 1 }).should.eventually.be.rejected
})
}))
})

it('claims names', async () => {
it('claims names', prelima(async () => {
const preclaim = await aens.aensPreclaim(name)
preclaim.should.be.an('object')
return preclaim.claim().should.eventually.be.an('object')
})
preclaim.claim().should.eventually.be.an('object')
}))

it('queries names', async () => {
it('queries names', prelima(async () => {
// For some reason the node will return 404 when name is queried
// just right after claim tx has been mined so we wait 0.5s
await new Promise(resolve => setTimeout(resolve, 500))
return aens.aensQuery(name).should.eventually.be.an('object')
})
}))

it('updates names', async () => {
it('updates names', prelima(async () => {
const claim = await aens.aensQuery(name)
nameHash = claim.id
const address = await aens.address()
return claim.update(address).should.eventually.deep.include({
pointers: [R.fromPairs([['key', 'account_pubkey'], ['id', address]])]
})
})
}))

it('Spend by name', async () => {
it('Spend by name', prelima(async () => {
const current = await aens.address()
const onAccount = aens.addresses().find(acc => acc !== current)
await aens.spend(100, name, { onAccount })
})
}))

it('transfers names', async () => {
it('transfers names', prelima(async () => {
const claim = await aens.aensQuery(name)

await claim.transfer(account.publicKey)
Expand All @@ -94,9 +104,9 @@ describe.skip('Aens', function () {
return claim2.update(account.publicKey).should.eventually.deep.include({
pointers: [R.fromPairs([['key', 'account_pubkey'], ['id', account.publicKey]])]
})
})
}))

it('revoke names', async () => {
it('revoke names', prelima(async () => {
const aens2 = await BaseAe()
aens2.setKeypair(account)

Expand All @@ -105,14 +115,24 @@ describe.skip('Aens', function () {
await aensName.revoke()

return aens2.aensQuery(name).should.be.rejectedWith(Error)
})
}))

it('PreClaim name using specific account', async () => {
it('PreClaim name using specific account', prelima(async () => {
const current = await aens.address()
const onAccount = aens.addresses().find(acc => acc !== current)

const preclaim = await aens.aensPreclaim(name, { onAccount })
preclaim.should.be.an('object')
preclaim.tx.accountId.should.be.equal(onAccount)
}))

describe('name auctions', function () {
it('claims names', lima(async () => {
const name = '1234567890123456.aet'
const preclaim = await aens.aensPreclaim(name)
preclaim.should.be.an('object')
preclaim.claim({ nameFee: '1000000000000000000000' }).should.eventually.be.an('object')
aens.aensClaim(name, 0, { nameFee: '2000000000000000000000' }).should.eventually.be.an('object')
}))
})
})
7 changes: 5 additions & 2 deletions test/integration/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const recipientId = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'
const name = 'test123test.test'
const nameHash = `nm_${encodeBase58Check(Buffer.from(name))}`
const nameId = 'nm_2sFnPHi5ziAqhdApSpRBsYdomCahtmk3YGNZKYUTtUNpVSMccC'
const nameFee = '1000000000000000000000'
const pointers = [{ key: 'account_pubkey', id: senderId }]

// Oracle
Expand Down Expand Up @@ -91,13 +92,15 @@ describe('Native Transaction', function () {
accountId: senderId,
nonce,
name: nameHash,
nameSalt: _salt
nameSalt: _salt,
nameFee
})
const nativeTx = await clientNative.nameClaimTx({
accountId: senderId,
nonce,
name: nameHash,
nameSalt: _salt
nameSalt: _salt,
nameFee
})
txFromAPI.should.be.equal(nativeTx)
})
Expand Down

0 comments on commit d3d0970

Please sign in to comment.