refactor renderer.js / universaldapp.js

pull/1/head
yann300 7 years ago
parent d9c56ee424
commit 874a8d3017
  1. 148
      src/app/contract/contractParser.js
  2. 65
      src/app/contract/publishOnSwarm.js
  3. 33
      src/app/contract/txExecution.js
  4. 153
      src/app/contract/txFormat.js
  5. 106
      src/app/contract/txHelper.js

@ -0,0 +1,148 @@
'use strict'
var $ = require('jquery')
var txHelper = require('../execution/txHelper')
module.exports = (contractName, contract, compiledSource) => {
return getDetails(contractName, contract, compiledSource)
}
var getDetails = function (contractName, contract, source) {
var detail = {}
detail.name = contractName
detail.metadata = contract.metadata
if (contract.bytecode) {
detail.bytecode = contract.bytecode
}
detail.interface = contract.interface
if (contract.bytecode) {
detail.bytecode = contract.bytecode
detail.web3Deploy = gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode)
detail.metadataHash = retrieveMetadataHash(contract.bytecode)
if (detail.metadataHash) {
detail.swarmLocation = 'bzz://' + detail.metadataHash
}
}
detail.functionHashes = {}
for (var fun in contract.functionHashes) {
detail.functionHashes[contract.functionHashes[fun]] = fun
}
detail.gasEstimates = formatGasEstimates(contract.gasEstimates)
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) {
detail['Runtime Bytecode'] = contract.runtimeBytecode
}
if (contract.opcodes !== undefined && contract.opcodes !== '') {
detail['Opcodes'] = contract.opcodes
}
if (contract.assembly !== null) {
detail['Assembly'] = formatAssemblyText(contract.assembly, '', source)
}
return detail
}
var retrieveMetadataHash = function (bytecode) {
var match = /a165627a7a72305820([0-9a-f]{64})0029$/.exec(bytecode)
if (match) {
return match[1]
}
}
var formatAssemblyText = function (asm, prefix, source) {
if (typeof asm === typeof '' || asm === null || asm === undefined) {
return prefix + asm + '\n'
}
var text = prefix + '.code\n'
$.each(asm['.code'], function (i, item) {
var v = item.value === undefined ? '' : item.value
var src = ''
if (item.begin !== undefined && item.end !== undefined) {
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g')
}
if (src.length > 30) {
src = src.slice(0, 30) + '...'
}
if (item.name !== 'tag') {
text += ' '
}
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n'
})
text += prefix + '.data\n'
if (asm['.data']) {
$.each(asm['.data'], function (i, item) {
text += ' ' + prefix + '' + i + ':\n'
text += formatAssemblyText(item, prefix + ' ', source)
})
}
return text
}
var gethDeploy = function (contractName, jsonInterface, bytecode) {
var code = ''
var funABI = txHelper.getConstructorInterface(JSON.parse(jsonInterface))
funABI.inputs.forEach(function (inp) {
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n'
})
contractName = contractName.replace(/[:./]/g, '_')
code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' +
'\nvar ' + contractName + ' = ' + contractName + 'Contract.new('
funABI.inputs.forEach(function (inp) {
code += '\n ' + inp.name + ','
})
code += '\n {' +
'\n from: web3.eth.accounts[0], ' +
"\n data: '0x" + bytecode + "', " +
"\n gas: '4700000'" +
'\n }, function (e, contract){' +
'\n console.log(e, contract);' +
"\n if (typeof contract.address !== 'undefined') {" +
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
'\n }' +
'\n })'
return code
}
var formatGasEstimates = function (data) {
// FIXME: the whole gasEstimates object should be nil instead
if (data.creation === undefined && data.external === undefined && data.internal === undefined) {
return
}
var gasToText = function (g) {
return g === null ? 'unknown' : g
}
var ret = {}
var fun
if ('creation' in data) {
ret['Creation'] = gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n'
}
if ('external' in data) {
ret['External'] = {}
for (fun in data.external) {
ret['External'][fun] = gasToText(data.external[fun])
}
}
if ('internal' in data) {
ret['Internal'] = {}
for (fun in data.internal) {
ret['Internal'][fun] = gasToText(data.internal[fun])
}
}
return ret
}

@ -0,0 +1,65 @@
'use strict'
var async = require('async')
var swarmgw = require('swarmgw')
module.exports = (contract, appAPI, cb) => {
// gather list of files to publish
var sources = []
sources.push({
content: contract.metadata,
hash: contract.metadataHash
})
var metadata
try {
metadata = JSON.parse(contract.metadata)
} catch (e) {
return cb(e)
}
if (metadata === undefined) {
return cb('No metadata')
}
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) {
// find hash
var hash
try {
hash = metadata.sources[fileName].urls[0].match('bzzr://(.+)')[1]
} catch (e) {
return cb('Metadata inconsistency')
}
appAPI.fileProviderOf(fileName).get(fileName, (error, content) => {
if (error) {
console.log(error)
} else {
sources.push({
content: content,
hash: hash
})
}
cb()
})
}, function () {
// publish the list of sources in order, fail if any failed
async.eachSeries(sources, function (item, cb) {
swarmVerifiedPublish(item.content, item.hash, cb)
}, cb)
})
}
function swarmVerifiedPublish (content, expectedHash, cb) {
swarmgw.put(content, function (err, ret) {
if (err) {
cb(err)
} else if (ret !== expectedHash) {
cb('Hash mismatch')
} else {
cb()
}
})
}

@ -0,0 +1,33 @@
'use strict'
module.exports = {
/**
* deploy the given contract
*
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Object} udap - udapp.
* @param {Function} callback - callback.
*/
createContract: function (data, udapp, callback) {
udapp.runTx({data: data, useCall: false}, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
},
/**
* call the current given contract
*
* @param {String} to - address of the contract to call.
* @param {String} data - data to send with the transaction ( return of txFormat.buildData(...) ).
* @param {Object} funAbi - abi definition of the function to call.
* @param {Object} udap - udapp.
* @param {Function} callback - callback.
*/
callFunction: function (to, data, funAbi, udapp, callback) {
udapp.runTx({to: to, data: data, useCall: funAbi.constant}, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
callback(error, txResult)
})
}
}

@ -0,0 +1,153 @@
'use strict'
var $ = require('jquery')
var ethJSABI = require('ethereumjs-abi')
var helper = require('./txHelper')
module.exports = {
/**
* build the transaction data
*
* @param {Object} contract - abi definition of the current contract.
* @param {Object} contracts - map of all compiled contracts.
* @param {Bool} isConstructor - isConstructor.
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Object} params - input paramater of the function to call
* @param {Object} udapp - udapp
* @param {Object} executionContext - executionContext
* @param {Function} callback - callback
*/
buildData: function (contract, contracts, isConstructor, funAbi, params, udapp, executionContext, callback) {
var funArgs = ''
try {
funArgs = $.parseJSON('[' + params + ']')
} catch (e) {
callback('Error encoding arguments: ' + e)
return
}
var data = ''
var dataHex = ''
if (!isConstructor || funArgs.length > 0) {
try {
data = helper.encodeParams(funAbi, funArgs)
dataHex = data.toString('hex')
} catch (e) {
callback('Error encoding arguments: ' + e)
return
}
}
if (data.slice(0, 9) === 'undefined') {
dataHex = data.slice(9)
}
if (data.slice(0, 2) === '0x') {
dataHex = data.slice(2)
}
if (isConstructor) {
var bytecodeToDeploy = contract.bytecode
if (bytecodeToDeploy.indexOf('_') >= 0) {
this.linkBytecode(contract, contracts, executionContext, udapp, (err, bytecode) => {
if (err) {
callback('Error deploying required libraries: ' + err)
} else {
bytecodeToDeploy = bytecode + dataHex
return callback(null, bytecodeToDeploy)
}
})
return
} else {
dataHex = bytecodeToDeploy + dataHex
}
} else {
dataHex = Buffer.concat([helper.encodeFunctionId(funAbi), data]).toString('hex')
}
callback(null, dataHex)
},
atAddress: function () {},
linkBytecode: function (contract, contracts, executionContext, udapp, callback) {
var bytecode = contract.bytecode
if (bytecode.indexOf('_') < 0) {
return callback(null, bytecode)
}
var m = bytecode.match(/__([^_]{1,36})__/)
if (!m) {
return callback('Invalid bytecode format.')
}
var libraryName = m[1]
var libraryabi = helper.getContractByName(libraryName, contracts)
if (!libraryabi) {
return callback('Library ' + libraryName + ' not found.')
}
this.deployLibrary(libraryabi, executionContext, udapp, (err, address) => {
if (err) {
return callback(err)
}
var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_')
var hexAddress = address.toString('hex')
if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2)
}
hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress
while (bytecode.indexOf(libLabel) >= 0) {
bytecode = bytecode.replace(libLabel, hexAddress)
}
contract.bytecode = bytecode
this.linkBytecode(contract, contracts, executionContext, udapp, callback)
})
},
deployLibrary: function (libraryName, library, executionContext, udapp, callback) {
var address = library.address
if (address) {
return callback(null, address)
}
var bytecode = library.bytecode
if (bytecode.indexOf('_') >= 0) {
this.linkBytecode(libraryName, (err, bytecode) => {
if (err) callback(err)
else this.deployLibrary(libraryName, callback)
})
} else {
udapp.runTx({ data: bytecode, useCall: false }, (err, txResult) => {
if (err) {
return callback(err)
}
var address = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress
library.address = address
callback(err, address)
})
}
},
decodeResponse: function (response, fnabi, callback) {
// Only decode if there supposed to be fields
if (fnabi.outputs && fnabi.outputs.length > 0) {
try {
var i
var outputTypes = []
for (i = 0; i < fnabi.outputs.length; i++) {
outputTypes.push(fnabi.outputs[i].type)
}
// decode data
var decodedObj = ethJSABI.rawDecode(outputTypes, response)
// format decoded data
decodedObj = ethJSABI.stringify(outputTypes, decodedObj)
for (i = 0; i < outputTypes.length; i++) {
var name = fnabi.outputs[i].name
if (name.length > 0) {
decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i]
} else {
decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i]
}
}
return callback(null, decodedObj)
} catch (e) {
return callback('Failed to decode output: ' + e)
}
}
}
}

@ -0,0 +1,106 @@
'use strict'
var ethJSABI = require('ethereumjs-abi')
var $ = require('jquery')
module.exports = {
encodeParams: function (funABI, args) {
var types = []
if (funABI.inputs && funABI.inputs.length) {
for (var i = 0; i < funABI.inputs.length; i++) {
types.push(funABI.inputs[i].type)
}
}
// NOTE: the caller will concatenate the bytecode and this
// it could be done here too for consistency
return ethJSABI.rawEncode(types, args)
},
encodeFunctionId: function (funABI) {
var types = []
if (funABI.inputs && funABI.inputs.length) {
for (var i = 0; i < funABI.inputs.length; i++) {
types.push(funABI.inputs[i].type)
}
}
return ethJSABI.methodID(funABI.name, types)
},
sortAbiFunction: function (contract) {
var abi = JSON.parse(contract.interface).sort(function (a, b) {
if (a.name > b.name) {
return -1
} else {
return 1
}
}).sort(function (a, b) {
if (a.constant === true) {
return -1
} else {
return 1
}
})
return abi
},
getConstructorInterface: function (abi) {
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] }
if (typeof abi === 'string') {
try {
abi = JSON.parse(abi)
} catch (e) {
console.log('exception retrieving ctor abi ' + abi)
return funABI
}
}
for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'constructor') {
funABI.inputs = abi[i].inputs || []
break
}
}
return funABI
},
getFunction: function (abi, fnName) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === fnName) {
return abi[i]
}
}
return null
},
getFallbackInterface: function (abi) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'fallback') {
return abi[i]
}
}
},
getContractByName: function (contractName, contracts) {
for (var c in contracts) {
if (contracts[c].name === contractName) {
return contracts[c]
}
}
return null
},
inputParametersDeclarationToString: function (abiinputs) {
var inputs = ''
if (abiinputs) {
$.each(abiinputs, function (i, inp) {
if (inputs !== '') {
inputs += ', '
}
inputs += inp.type + ' ' + inp.name
})
}
return inputs
}
}
Loading…
Cancel
Save