Merge tag '3.0.1'

update SimpleStorage contract to comply with solc 0.4.23
This commit is contained in:
Iuri Matias 2018-05-09 12:13:49 -04:00
commit a0f7a6bb81
300 changed files with 20315 additions and 91486 deletions

View File

@ -10,14 +10,35 @@ engines:
enabled: false
guard-for-in:
enabled: false
for-direction:
enabled: false
getter-return:
enabled: false
no-buffer-constructor:
enabled: false
padding-line-between-statements:
enabled: false
semi-style:
enabled: false
switch-colon-spacing:
enabled: false
array-bracket-newline:
enabled: false
ratings:
paths:
- "lib/**/*"
exclude_paths:
- "test/"
- "old_test/"
- "boilerplate/"
- "demo/"
- "templates/boilerplate/"
- "templates/demo/"
- "js/"
- "test_app/"
- "test_apps/test_app/"
- "test_apps/contracts_app/"
- "docs/"
checks:
similar-code:
enabled: false
method-lines:
config:
threshold: 35

View File

@ -6,7 +6,8 @@
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
"sourceType": "module",
"ecmaVersion": 2017
},
"rules": {
"accessor-pairs": "error",
@ -17,7 +18,7 @@
],
"array-callback-return": "off",
"array-element-newline": "off",
"arrow-body-style": "error",
"arrow-body-style": "off",
"arrow-parens": "off",
"arrow-spacing": [
"error",
@ -88,7 +89,7 @@
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "error",
"max-statements-per-line": "off",
"multiline-ternary": [
"error",
"never"
@ -132,7 +133,7 @@
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "off",
"no-loop-func": "error",
"no-loop-func": "off",
"no-magic-numbers": "off",
"no-mixed-operators": "error",
"no-mixed-requires": "error",
@ -188,6 +189,7 @@
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-escape": "off",
"no-useless-rename": "error",
"no-useless-return": "off",
"no-var": "off",

27
.gitignore vendored
View File

@ -1,17 +1,26 @@
node_modules
TODO
NOTES
.node-xmlhttprequest-sync-*
demo/dist/
demo/.embark/development/
demo/config/production/password
boilerplate/dist/
templates/demo/dist/
templates/demo/.embark/development/
templates/demo/config/production/password
templates/demo/node_modules/
templates/boilerplate/dist/
docs/_build
docs/utils/__pycache_
test_app/dist/
test_app/.embark/development/
test_app/config/production/password
test_app/node_modules/
test_app/chains.json
test_apps/test_app/dist/
test_apps/test_app/.embark/development/
test_apps/test_app/config/production/password
test_apps/test_app/node_modules/
test_apps/test_app/chains.json
test_apps/contracts_app/build/
test_apps/contracts_app/.embark/development/
test_apps/contracts_app/config/production/password
test_apps/contracts_app/node_modules/
test_apps/contracts_app/chains.json
.idea
.eslintrc.json
.embark/
NOTES
npm-debug.log

View File

@ -1,7 +1,11 @@
language: node_js
node_js:
- "7"
- "6"
addons:
code_climate:
repo_token: 7454b1a666015e244c384d19f48c34e35d1ae58c3aa428ec542f10bbcb848358
script:
- npm run lint
- npm run test
- npm run testdapp_1
- npm run testdapp_2

View File

@ -1,29 +0,0 @@
Mind Map generated by NB MindMap plugin
> __version__=`1.1`,showJumps=`true`
---
# Embark
## Config
## CLI
## Engine
> leftSide=`true`
### Plugins
#### DefaultLogger
#### DefaultPipeline
### Services
## DefaultLogger
## Utils
> leftSide=`true`
## DeployManager

View File

@ -22,19 +22,8 @@ module.exports = (grunt) ->
test:
src: ['test/**/*.js']
jshint:
all: ['bin/embark', 'lib/**/*.js']
options: grunt.file.readJSON('package.json').jshintConfig
with_overrides:
options:
undef: false
esversion: 5
files:
src: ['js/mine.js', 'js/embark.js']
grunt.loadTasks "tasks"
require('matchdep').filterAll(['grunt-*','!grunt-cli']).forEach(grunt.loadNpmTasks)
grunt.registerTask 'default', ['clean']
grunt.registerTask 'build', ['clean', 'coffee']

546
README.md
View File

@ -1,10 +1,9 @@
![Embark](https://github.com/iurimatias/embark-framework/raw/develop/logo.png)
[![npm](https://img.shields.io/npm/dm/embark.svg)](https://npmjs.com/package/embark)
[![Gitter](https://img.shields.io/gitter/room/iurimatias/embark-framework.svg)](https://gitter.im/iurimatias/embark-framework)
[![Build
Status](https://travis-ci.org/iurimatias/embark-framework.svg?branch=develop)](https://travis-ci.org/iurimatias/embark-framework)
[![Code Climate](https://codeclimate.com/github/iurimatias/embark-framework/badges/gpa.svg)](https://codeclimate.com/github/iurimatias/embark-framework)
[![Gitter](https://img.shields.io/gitter/room/embark-framework/Lobby.svg)](https://gitter.im/embark-framework/Lobby)
[![Build Status](https://travis-ci.org/embark-framework/embark.svg?branch=develop)](https://travis-ci.org/embark-framework/embark)
[![Build status](https://ci.appveyor.com/api/projects/status/nnq38x2hi3q11o44/branch/develop?svg=true)](https://ci.appveyor.com/project/iurimatias/embark/branch/develop)
What is Embark
======
@ -37,30 +36,6 @@ With Embark you can:
* Integrate with any web technology including React, Foundation, etc..
* Use any build pipeline or tool you wish, including grunt, gulp and webpack.
Table of Contents
======
* [Installation](#installation)
* [Usage Demo](#usage---demo)
* [Dashboard](#dashboard)
* [Creating a new DApp](#creating-a-new-dapp)
* [Libraries and APIs available](#libraries-and-languages-available)
* [Using and Configuring Contracts](#using-contracts)
* [EmbarkJS](#embarkjs)
* [EmbarkJS - Storage (IPFS)](#embarkjs---storage)
* [EmbarkJS - Communication (Whisper/Orbit)](#embarkjs---communication)
* [Testing Contracts](#tests)
* [Working with different chains](#working-with-different-chains)
* [Custom Application Structure](#structuring-application)
* [Deploying to IPFS](#deploying-to-ipfs-and-swarm)
* [Extending Functionality with Plugins](#plugins)
* [Donations](#donations)
Installation
======
Requirements: geth (1.6.7 or higher recommended), node (6.9.1 or higher is recommended) and npm
Optional: testrpc (3.0 or higher) if using the simulator
Further: depending on the dapp stack you choose: [IPFS](https://ipfs.io/)
```Bash
$ npm -g install embark
@ -68,518 +43,5 @@ $ npm -g install embark
$ npm -g install ethereumjs-testrpc
```
See [Complete Installation Instructions](https://github.com/iurimatias/embark-framework/wiki/Installation).
See [Complete Documentation](https://embark.status.im/docs/).
**updating from Embark 1**
Embark's npm package has changed from ```embark-framework``` to ```embark```, this sometimes can create conflicts. To update first uninstall embark-framework 1 to avoid any conflicts. ```npm uninstall -g embark-framework``` then ```npm install -g embark```
Usage - Demo
======
![Embark Demo screenshot](http://i.imgur.com/a9ddSjn.png)
You can easily create a sample working DApp with the following:
```Bash
$ embark demo
$ cd embark_demo
```
You can run a REAL ethereum node for development purposes:
```Bash
$ embark blockchain
```
Alternatively, to use an ethereum rpc simulator simply run:
```Bash
$ embark simulator
```
By default Embark blockchain will mine a minimum amount of ether and will only mine when new transactions come in. This is quite useful to keep a low CPU. The option can be configured at ```config/blockchain.json```. Note that running a real node requires at least 2GB of free ram, please take this into account if running it in a VM.
Then, in another command line:
```Bash
$ embark run
```
This will automatically deploy the contracts, update their JS bindings and deploy your DApp to a local server at http://localhost:8000
Note that if you update your code, it will automatically be re-deployed, contracts included. There is no need to restart embark, refreshing the page on the browser will do.
Dashboard
=====
Embark 2 comes with a terminal dashboard.
![Dashboard](http://i.imgur.com/s4OQZpu.jpg)
The dashboard will tell you the state of your contracts, the environment you are using, and what Embark is doing at the moment.
**available services**
Available Services will display the services available to your dapp in green. If a service is down, then it will be displayed in red.
**logs and console**
There is a console at the bottom which can be used to interact with contracts or with Embark itself. Type ```help``` to see a list of available commands. More commands will be added with each version of Embark.
Creating a new DApp
======
If you want to create a blank new app:
```Bash
$ embark new AppName
$ cd AppName
```
DApp Structure
======
```Bash
app/
|___ contracts/ #solidity or serpent contracts
|___ html/
|___ css/
|___ js/
config/
|___ blockchain.json #rpc and blockchain configuration
|___ contracts.json #ethereum contracts configuration
|___ storage.json #ipfs configuration
|___ communication.json #whisper/orbit configuration
|___ webserver.json #dev webserver configuration
test/
|___ #contracts tests
```
Solidity/Serpent files in the contracts directory will automatically be deployed with Embark run. Changes in any files will automatically be reflected in app, changes to contracts will result in a redeployment and update of their JS Bindings
Libraries and languages available
======
Embark can build and deploy contracts coded in Solidity. It will make them available on the client side using EmbarkJS and Web3.js.
Further documentation for these can be found below:
* Smart Contracts: [Solidity](https://solidity.readthedocs.io/en/develop/) and [Serpent](https://github.com/ethereum/wiki/wiki/Serpent)
* Client Side: [Web3.js](https://github.com/ethereum/wiki/wiki/JavaScript-API) and [EmbarkJS](#embarkjs)
Using Contracts
======
Embark will automatically take care of deployment for you and set all needed JS bindings. For example, the contract below:
```Javascript
# app/contracts/simple_storage.sol
pragma solidity ^0.4.17;
contract SimpleStorage {
uint public storedData;
function SimpleStorage(uint initialValue) public {
storedData = initialValue;
}
function set(uint x) public {
storedData = x;
}
function get() view returns (uint retVal) {
return storedData;
}
}
```
Will automatically be available in Javascript as:
```Javascript
# app/js/index.js
SimpleStorage.methods.set(100).send({from: web3.eth.defaultAccount});
SimpleStorage.methods.get().call().then(function(value) { console.log(value.toNumber()) });
SimpleStorage.methods.storedData().then(function(value) { console.log(value.toNumber()) });
```
You can specify for each contract and environment its gas costs and arguments:
```Json
# config/contracts.json
{
"development": {
"gas": "auto",
"contracts": {
"SimpleStorage": {
"args": [
100
]
}
}
}
}
```
If you are using multiple contracts, you can pass a reference to another contract as ```$ContractName```, Embark will automatically replace this with the correct address for the contract.
```Json
# config/contracts.json
{
...
"development": {
"contracts": {
"SimpleStorage": {
"args": [
100,
"$MyStorage"
]
},
"MyStorage": {
"args": [
"initial string"
]
},
"MyMainContract": {
"args": [
"$SimpleStorage"
]
}
}
}
...
}
```
You can now deploy many instances of the same contract. e.g
```Json
# config/contracts.json
{
"development": {
"contracts": {
"Currency": {
"deploy": false,
"args": [
100
]
},
"Usd": {
"instanceOf": "Currency",
"args": [
200
]
},
"MyCoin": {
"instanceOf": "Currency",
"args": [
200
]
}
}
}
}
...
```
Contracts addresses can be defined. If an address is defined, Embark uses the defined address instead of deploying the contract.
```Json
# config/contracts.json
{
...
"development": {
"contracts": {
"UserStorage": {
"address": "0x123456"
},
"UserManagement": {
"args": [
"$UserStorage"
]
}
}
}
...
}
```
You can Also specify which versions of solc and web3.js to use:
```Json
# config/contracts.json
{
...
"development": {
"versions": {
"web3.js": "1.0.0-beta",
"solc": "0.4.17"
}
}
...
}
```
You specify which node the contracts should be deploy to and the order of nodes
the dapp should connect to. $WEB3 means the dapp will try to use an existing
web3 object first if available.
```Json
# config/contracts.json
{
...
"development": {
"deployment": {
"host": "localhost",
"port": 8545,
"type": "rpc"
},
"dappConnection": [
"$WEB3",
"http://localhost:8545"
]
}
...
}
```
EmbarkJS
======
EmbarkJS is a javascript library meant to abstract and facilitate the development of DApps.
**promises**
```Javascript
var myContract = new EmbarkJS.Contract({abi: abiObject, address: "0x123"});
myContract.methods.get().call().then(function(value) { console.log("value is " + value.toNumber) });
```
events:
```Javascript
myContract.events.eventName({from: web3.eth.accounts}, 'latest').then(function(event) { console.log(event) });
```
**deployment**
Client side deployment will be automatically available in Embark for existing contracts:
```Javascript
SimpleStorage.deploy([args], {options}).then(function(anotherSimpleStorage) {});
```
or it can be manually definied as
```Javascript
var myContract = new EmbarkJS.Contract({abi: abiObject, code: code});
myContract.deploy([args], {options}).then(function(anotherMyContractObject) {});
```
so you can define your gas as
```Javascript
myContract.deploy([100, "seconde argument"], {gas: 800000}).then(function(anotherMyContractObject) {});
```
EmbarkJS - Storage
======
**initialization**
The current available storage is IPFS. It can be initialized as
```Javascript
EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'})
```
**Saving Text**
```Javascript
EmbarkJS.Storage.saveText("hello world").then(function(hash) {});
```
**Retrieving Data/Text**
```Javascript
EmbarkJS.Storage.get(hash).then(function(content) {});
```
**Uploading a file**
```HTML
<input type="file">
```
```Javascript
var input = $("input[type=file"]);
EmbarkJS.Storage.uploadFile(input).then(function(hash) {});
```
**Generate URL to file**
```Javascript
EmbarkJS.Storage.getUrl(hash);
```
note: if not using localhost, the cors needs to be set as ```ipfs --json API.HTTPHeaders.Access-Control-Allow-Origin '["your-host-name-port"]```
EmbarkJS - Communication
======
**initialization**
For Whisper (note: currently requires geth 1.6.0 or higher):
```Javascript
EmbarkJS.Messages.setProvider('whisper')
```
For Orbit:
You'll need to use IPFS from master and run it as: ```ipfs daemon --enable-pubsub-experiment```
then set the provider:
```Javascript
EmbarkJS.Messages.setProvider('orbit', {server: 'localhost', port: 5001})
```
**listening to messages**
```Javascript
EmbarkJS.Messages.listenTo({topic: ["topic1", "topic2"]}).then(function(message) { console.log("received: " + message); })
```
**sending messages**
you can send plain text
```Javascript
EmbarkJS.Messages.sendMessage({topic: "sometopic", data: 'hello world'})
```
or an object
```Javascript
EmbarkJS.Messages.sendMessage({topic: "sometopic", data: {msg: 'hello world'}})
```
note: array of topics are considered an AND. In Whisper you can use another array for OR combinations of several topics e.g ```["topic1", ["topic2", "topic3"]]``` => ```topic1 AND (topic2 OR topic 3)```
Tests
======
You can run specs with ```embark test```, it will run any test files under ```test/```.
Embark includes a testing lib to rapidly run & test your contracts in a EVM.
```Javascript
# test/simple_storage_spec.js
describe("SimpleStorage", function() {
before(function(done) {
this.timeout(0);
var contractsConfig = {
"SimpleStorage": {
args: [100]
}
};
EmbarkSpec.deployAll(contractsConfig, done);
});
it("should set constructor value", function(done) {
SimpleStorage.storedData(function(err, result) {
assert.equal(result.toNumber(), 100);
done();
});
});
it("set storage value", function(done) {
SimpleStorage.set(150, function() {
SimpleStorage.get(function(err, result) {
assert.equal(result.toNumber(), 150);
done();
});
});
});
});
```
Embark uses [Mocha](http://mochajs.org/) by default, but you can use any testing framework you want.
Working with different chains
======
You can specify which environment to deploy to:
```$ embark blockchain livenet```
```$ embark run livenet```
The environment is a specific blockchain configuration that can be managed at config/blockchain.json
```Json
# config/blockchain.json
...
"livenet": {
"networkType": "livenet",
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"account": {
"password": "config/livenet/password"
}
},
...
```
Structuring Application
======
Embark is quite flexible and you can configure your own directory structure using ```embark.json```
```Json
# embark.json
{
"contracts": ["app/contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"images/": ["app/images/**"],
"js/app.js": ["embark.js", "app/js/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",
"config": "config/",
"plugins": {}
}
```
Deploying to IPFS and Swarm
======
To deploy a dapp to IPFS, all you need to do is run a local IPFS node and then run ```embark upload ipfs```.
If you want to deploy to the livenet then after configuring you account on ```config/blockchain.json``` on the ```livenet``` environment then you can deploy to that chain by specifying the environment ```embark ipfs livenet```.
To deploy a dapp to SWARM, all you need to do is run a local SWARM node and then run ```embark upload swarm```.
Plugins
======
It's possible to extend Embark's functionality with plugins. For example, the following is possible:
* plugin to add support for es6, jsx, coffescript, etc (``embark.registerPipeline``)
* plugin to add standard contracts or a contract framework (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to make some contracts available in all environments for use by other contracts or the dapp itself e.g a Token, a DAO, ENS, etc.. (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to add a libraries such as react or bootstrap (``embark.addFileToPipeline``)
* plugin to specify a particular web3 initialization for special provider uses (``embark.registerClientWeb3Provider``)
* plugin to create a different contract wrapper (``embark.registerContractsGeneration``)
* plugin to add new console commands (``embark.registerConsoleCommand``)
* plugin to add support for another contract language such as viper, LLL, etc (``embark.registerCompiler``)
For more information on how to develop your own plugin, please see the [plugin documentation](http://embark.readthedocs.io/en/latest/plugins.html)
Donations
======
If you like Embark, please consider donating to 0xFA239D14c7117C3D2370B2a4c4238534391fadd9

26
appveyor.yml Normal file
View File

@ -0,0 +1,26 @@
# Test against the latest version of this Node.js version
environment:
nodejs_version: "8"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install -g npm@5.7
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
- npm run lint
- npm run test
# tmp fix due to windows npm5 issue
- cd test_apps/test_app/extensions/embark-service && npm install
- npm run testdapp_1
- npm run testdapp_2
# Don't actually build.
build: off

View File

@ -1,4 +1,14 @@
#!/usr/bin/env node
try {
eval('let __nodeTest = 123;');
} catch(e) {
if (e.name === 'SyntaxError') {
console.error("unsupported version of NodeJS. Make sure you are running nodejs 6.9.5 or above");
process.exit();
}
}
var Cmd = require('../lib/cmd');
var cli = new Cmd();
cli.process(process.argv);

View File

@ -1,11 +0,0 @@
{
"contracts": ["app/contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"js/app.js": ["embark.js", "app/js/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",
"config": "config/",
"plugins": {}
}

View File

@ -1,28 +0,0 @@
// describe("SimpleStorage", function() {
// before(function(done) {
// this.timeout(0);
// var contractsConfig = {
// "SimpleStorage": {
// args: [100, '0x123']
// }
// };
// EmbarkSpec.deployAll(contractsConfig, done);
// });
//
// it("should set constructor value", function(done) {
// SimpleStorage.storedData(function(err, result) {
// assert.equal(result.toNumber(), 100);
// done();
// });
// });
//
// it("set storage value", function(done) {
// SimpleStorage.set(150, function() {
// SimpleStorage.get(function(err, result) {
// assert.equal(result.toNumber(), 150);
// done();
// });
// });
// });
//
// });

View File

@ -1,153 +0,0 @@
/*globals $, SimpleStorage, document*/
var addToLog = function(id, txt) {
$(id + " .logs").append("<br>" + txt);
};
// ===========================
// Blockchain example
// ===========================
$(document).ready(function() {
$("#blockchain button.set").click(function() {
var value = parseInt($("#blockchain input.text").val(), 10);
// If web3.js 1.0 is being used
if (EmbarkJS.isNewWeb3()) {
SimpleStorage.methods.set(value).send({from: web3.eth.defaultAccount});
addToLog("#blockchain", "SimpleStorage.methods.set(value).send({from: web3.eth.defaultAccount})");
} else {
SimpleStorage.set(value);
addToLog("#blockchain", "SimpleStorage.set(" + value + ")");
}
});
$("#blockchain button.get").click(function() {
// If web3.js 1.0 is being used
if (EmbarkJS.isNewWeb3()) {
SimpleStorage.methods.get().call(function(err, value) {
$("#blockchain .value").html(value);
});
addToLog("#blockchain", "SimpleStorage.methods.get(console.log)");
} else {
SimpleStorage.get().then(function(value) {
$("#blockchain .value").html(value.toNumber());
});
addToLog("#blockchain", "SimpleStorage.get()");
}
});
});
// ===========================
// Storage (IPFS) example
// ===========================
$(document).ready(function() {
// automatic set if config/storage.json has "enabled": true and "provider": "ipfs"
//EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'});
$("#storage .error").hide();
EmbarkJS.Storage.ipfsConnection.ping()
.then(function(){
$("#status-storage").addClass('status-online');
$("#storage-controls").show();
})
.catch(function(err) {
if(err){
console.log("IPFS Connection Error => " + err.message);
$("#storage .error").show();
$("#status-storage").addClass('status-offline');
$("#storage-controls").hide();
}
});
$("#storage button.setIpfsText").click(function() {
var value = $("#storage input.ipfsText").val();
EmbarkJS.Storage.saveText(value).then(function(hash) {
$("span.textHash").html(hash);
$("input.textHash").val(hash);
addToLog("#storage", "EmbarkJS.Storage.saveText('" + value + "').then(function(hash) { })");
})
.catch(function(err) {
if(err){
console.log("IPFS saveText Error => " + err.message);
}
});
});
$("#storage button.loadIpfsHash").click(function() {
var value = $("#storage input.textHash").val();
EmbarkJS.Storage.get(value).then(function(content) {
$("span.ipfsText").html(content);
addToLog("#storage", "EmbarkJS.Storage.get('" + value + "').then(function(content) { })");
})
.catch(function(err) {
if(err){
console.log("IPFS get Error => " + err.message);
}
});
});
$("#storage button.uploadFile").click(function() {
var input = $("#storage input[type=file]");
EmbarkJS.Storage.uploadFile(input).then(function(hash) {
$("span.fileIpfsHash").html(hash);
$("input.fileIpfsHash").val(hash);
addToLog("#storage", "EmbarkJS.Storage.uploadFile($('input[type=file]')).then(function(hash) { })");
})
.catch(function(err) {
if(err){
console.log("IPFS uploadFile Error => " + err.message);
}
});
});
$("#storage button.loadIpfsFile").click(function() {
var hash = $("#storage input.fileIpfsHash").val();
var url = EmbarkJS.Storage.getUrl(hash);
var link = '<a href="' + url + '" target="_blank">' + url + '</a>';
$("span.ipfsFileUrl").html(link);
$(".ipfsImage").attr('src', url);
addToLog("#storage", "EmbarkJS.Storage.getUrl('" + hash + "')");
});
});
// ===========================
// Communication (Whisper) example
// ===========================
$(document).ready(function() {
$("#communication .error").hide();
$("#communication .errorVersion").hide();
if (EmbarkJS.Messages.providerName === 'whisper') {
EmbarkJS.Messages.getWhisperVersion(function(err, version) {
if (err) {
$("#communication .error").show();
$("#communication-controls").hide();
$("#status-communication").addClass('status-offline');
} else {
EmbarkJS.Messages.setProvider('whisper');
$("#status-communication").addClass('status-online');
}
});
}
$("#communication button.listenToChannel").click(function() {
var channel = $("#communication .listen input.channel").val();
$("#communication #subscribeList").append("<br> subscribed to " + channel + " now try sending a message");
EmbarkJS.Messages.listenTo({topic: [channel]}).then(function(message) {
$("#communication #messagesList").append("<br> channel: " + channel + " message: " + message);
});
addToLog("#communication", "EmbarkJS.Messages.listenTo({topic: ['" + channel + "']}).then(function(message) {})");
});
$("#communication button.sendMessage").click(function() {
var channel = $("#communication .send input.channel").val();
var message = $("#communication .send input.message").val();
EmbarkJS.Messages.sendMessage({topic: channel, data: message});
addToLog("#communication", "EmbarkJS.Messages.sendMessage({topic: '" + channel + "', data: '" + message + "'})");
});
});

View File

@ -1,13 +0,0 @@
{
"contracts": ["app/contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"images/": ["app/images/**"],
"js/app.js": ["embark.js", "app/js/_vendor/jquery.min.js", "app/js/_vendor/bootstrap.min.js", "app/js/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",
"config": "config/",
"plugins": {
}
}

View File

@ -1,28 +0,0 @@
describe("SimpleStorage", function() {
before(function(done) {
this.timeout(0);
var contractsConfig = {
"SimpleStorage": {
args: [100]
}
};
EmbarkSpec.deployAll(contractsConfig, done);
});
it("should set constructor value", function(done) {
SimpleStorage.storedData(function(err, result) {
assert.equal(result.toNumber(), 100);
done();
});
});
it("set storage value", function(done) {
SimpleStorage.set(150, function() {
SimpleStorage.get(function(err, result) {
assert.equal(result.toNumber(), 150);
done();
});
});
});
});

View File

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = embark
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -1,160 +0,0 @@
# -*- coding: utf-8 -*-
#
# ENS documentation build configuration file, created by
# sphinx-quickstart on Thu Dec 15 16:45:41 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from recommonmark.parser import CommonMarkParser
source_parsers = {
'.md': CommonMarkParser,
}
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.mathjax']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Embark'
copyright = u'2017, Iuri Matias'
author = u'Iuri Matias'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'2.5'
# The full version, including alpha/beta/rc tags.
release = u'2.5.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'Embarkdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'Embark.tex', u'Embark Documentation',
u'Iuri Matias \\textless{}\\textgreater{}', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'embark', u'Embark Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'Embark', u'Embark Documentation',
author, 'Embark', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -1,22 +0,0 @@
Configuring Communication (Whisper, Orbit)
==========================
Embark will check your prefered communication configuration in the file ``config/communication.json``. This file will contain the prefered configuration for each environment. With ``default`` being the configuration fields that applies to every environment. Each of those can be individually overriden in a per environment basis.
e.g :
.. code:: javascript
{
"default": {
"enabled": true,
"provider": "whisper",
"available_providers": ["whisper", "orbit"]
}
}
options available:
* ``enabled`` (boolean: true/false) to enable or completly disable communication support
* ``provider`` (string: "wisper" or "orbit") desired provider to automatically connect to on the dapp. e.g in the example above, seting this to ``"whisper"`` will automaticaly add ``EmbarkJS.setProvider('whisper')`` to the generated code
* ``available_providers`` (array: ["whisper", "orbit"]) list of communication platforms to be supported on the dapp. This will affect what's available with the EmbarkJS library on the dapp so if you don't need Orbit for e.g, removing it from this will considerably reduce the file size of the generated JS code.

View File

@ -1,33 +0,0 @@
Configuring Storage (IPFS)
==========================
Embark will check your prefered storage configuration in the file ``config/storage.json``. This file will contain the prefered configuration for each environment. With ``default`` being the configuration fields that applies to every environment. Each of those can be individually overriden in a per environment basis.
e.g :
.. code:: javascript
{
"default": {
"enabled": true,
"ipfs_bin": "ipfs",
"provider": "ipfs",
"available_providers": ["ipfs"],
"host": "localhost",
"port": 5001
},
"development": {
"enabled": true,
"provider": "ipfs",
"host": "localhost",
"port": 5001
}
}
options available:
* ``enabled`` (boolean: true/false) to enable or completly disable storage support
* ``ipfs_bin`` (string) name or desired path to the ipfs binary
* ``provider`` (string: "ipfs") desired provider to automatically connect to on the dapp. e.g in the example above, seting this to ``"ipfs"`` will automaticaly add ``EmbarkJS.setProvider('ipfs', {server: 'localhost', 5001})`` to the generated code
* ``available_providers`` (array: ["ipfs"]) list of storages to be supported on the dapp. This will affect what's available with the EmbarkJS library on the dapp.
* ``host`` and ``port`` of the ipfs node to connect to.

View File

@ -1,42 +0,0 @@
Creating a new DApp
===================
If you want to create a blank new app.
.. code:: bash
$ embark new AppName
$ cd AppName
To run Embark then in one console run:
.. code:: bash
$ embark blockchain
And in another console run:
.. code:: bash
$ embark run
DApp Structure
==============
.. code:: bash
app/
|___ contracts/ #solidity smart contracts
|___ html/
|___ css/
|___ js/
config/
|___ blockchain.json #rpc and blockchain configuration
|___ contracts.json #ethereum contracts configuration
|___ storage.json #ipfs configuration
|___ communication.json #whisper/orbit configuration
|___ webserver.json #dev webserver configuration
test/
|___ #contracts tests
Solidity files in the contracts directory will automatically be deployed with ``embark run``. Changes in any files will automatically be reflected in app, changes to contracts will result in a redeployment and update of their JS Bindings

View File

@ -1,24 +0,0 @@
Dashboard
=========
Embark 2 comes with a terminal dashboard.
.. figure:: http://i.imgur.com/s4OQZpu.jpg
:alt: Dashboard
Dashboard
The dashboard will tell you the state of your contracts, the enviroment
you are using, and what embark is doing at the moment.
**available services**
Available Services will display the services available to your dapp in
green, if one of these is down then it will be displayed in red.
**logs and console**
There is a console at the bottom which can be used to interact with
contracts or with embark itself. type ``help`` to see a list of
available commands, more commands will be added with each version of
Embark.

View File

@ -1,8 +0,0 @@
Deploying to IPFS
=================
To deploy a dapp to IPFS, all you need to do is run a local IPFS node
and then run ``embark upload ipfs``. If you want to deploy to the livenet then
after configuring you account on ``config/blockchain.json`` on the
``livenet`` environment then you can deploy to that chain by
specifying the environment ``embark ipfs livenet``.

View File

@ -1,4 +0,0 @@
Deploying to SWARM
==================
To deploy a dapp to SWARM, all you need to do is run a local SWARM node and then run ``embark upload swarm``.

View File

@ -1,4 +0,0 @@
Donations
======
If you like Embark please consider donating to 0xFA239D14c7117C3D2370B2a4c4238534391fadd9

View File

@ -1,4 +0,0 @@
Donations
=========
If you like Embark please consider donating to 0xFA239D14c7117C3D2370B2a4c4238534391fadd9

View File

@ -1,46 +0,0 @@
EmbarkJS - Communication (Whisper, Orbit)
=========================================
**initialization**
For Whisper:
.. code:: javascript
EmbarkJS.Messages.setProvider('whisper')
For Orbit:
You'll need to use IPFS from master and run it as:
``ipfs daemon --enable-pubsub-experiment``
then set the provider:
.. code:: javascript
EmbarkJS.Messages.setProvider('orbit', {server: 'localhost', port: 5001})
**listening to messages**
.. code:: javascript
EmbarkJS.Messages.listenTo({topic: ["topic1", "topic2"]}).then(function(message) { console.log("received: " + message); })
**sending messages**
you can send plain text
.. code:: javascript
EmbarkJS.Messages.sendMessage({topic: "sometopic", data: 'hello world'})
or an object
.. code:: javascript
EmbarkJS.Messages.sendMessage({topic: "sometopic", data: {msg: 'hello world'}})
note: array of topics are considered an AND. In Whisper you can use
another array for OR combinations of several topics e.g
``["topic1", ["topic2", "topic3"]]`` =>
``topic1 AND (topic2 OR topic 3)``

View File

@ -1,57 +0,0 @@
EmbarkJS - Storage (IPFS)
=========================
**initialization**
The current available storage is IPFS. it can be initialized as
.. code:: javascript
EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'})
**Saving Text**
.. code:: javascript
EmbarkJS.Storage.saveText("hello world")
.then(function(hash) {})
.catch(function(err) {
if(err){
console.log("IPFS saveText Error => " + err.message);
}
});
**Retrieving Data/Text**
.. code:: javascript
EmbarkJS.Storage.get(hash)
.then(function(content) {})
.catch(function(err) {
if(err){
console.log("IPFS get Error => " + err.message);
}
});
**Uploading a file**
.. code:: html
<input type="file">
.. code:: javascript
var input = $("input[type=file"]);
EmbarkJS.Storage.uploadFile(input)
.then(function(hash) {})
.catch(function(err) {
if(err){
console.log("IPFS uploadFile Error => " + err.message);
}
});
**Generate URL to file**
.. code:: javascript
EmbarkJS.Storage.getUrl(hash);

View File

@ -1,36 +0,0 @@
EmbarkJS
========
EmbarkJS is a javascript library meant to abstract and facilitate the
development of DApps.
**promises**
methods in EmbarkJS contracts will be converted to promises.
.. code:: javascript
var myContract = new EmbarkJS.Contract({abi: abiObject, address: "0x123"});
myContract.get().then(function(value) { console.log("value is " + value.toNumber()) });
**deployment**
Client side deployment will be automatically available in Embark for
existing contracts:
.. code:: javascript
SimpleStorage.deploy([args], {options}).then(function(anotherSimpleStorage) {});
or it can be manually definied as
.. code:: javascript
var myContract = new EmbarkJS.Contract({abi: abiObject, code: code});
myContract.deploy([args], {options}).then(function(anotherMyContractObject) {});
so you can define your gas as
.. code:: javascript
myContract.deploy([100, "seconde argument"], {gas: 800000}).then(function(anotherMyContractObject) {});

View File

@ -1,40 +0,0 @@
.. embark documentation master file, created by
sphinx-quickstart on Tue Jan 10 06:39:27 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to embark's documentation!
==================================
This is a work in progress, feel free to contribute!
.. toctree::
:maxdepth: 2
installation.rst
usage.rst
dashboard.rst
creating-a-new-dapp.rst
libraries-and-languages-available.rst
using-contracts.rst
configuring-storage.rst
configuring-communication.rst
embarkjs.rst
embarkjs-storage.rst
embarkjs-communication.rst
tests.rst
working-with-different-chains.rst
structuring-application.rst
deploying-to-ipfs.rst
deploying-to-swarm.rst
plugins.rst
using-embark-with-grunt.rst
donations.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,24 +0,0 @@
Installation
============
Requirements: geth (1.6.5 or higher recommended), node (6.9.1 or higher is recommended) and npm
serpent (develop) if using contracts with Serpent, testrpc (3.0 or higher)
if using the simulator or the test functionality. Further: depending on
the dapp stack you choose: `IPFS <https://ipfs.io/>`__
.. code:: bash
$ npm -g install embark
# If you plan to use the simulator instead of a real ethereum node.
$ npm -g install ethereumjs-testrpc
See `Complete Installation
Instructions <https://github.com/iurimatias/embark-framework/wiki/Installation>`__.
**updating from embark 1**
Embark's npm package has changed from ``embark-framework`` to
``embark``, this sometimes can create conflicts. To update first
uninstall embark-framework 1 to avoid any conflicts.
``npm uninstall -g embark-framework`` then ``npm install -g embark``

View File

@ -1,14 +0,0 @@
Libraries and languages available
=================================
Embark can build and deploy contracts coded in Solidity or Serpent. It
will make them available on the client side using EmbarkJS and Web3.js.
Further documentation for these can be found below:
- Smart Contracts:
`Solidity <https://solidity.readthedocs.io/en/develop/>`__ and
`Serpent <https://github.com/ethereum/wiki/wiki/Serpent>`__
- Client Side:
`Web3.js <https://github.com/ethereum/wiki/wiki/JavaScript-API>`__
and `EmbarkJS <#embarkjs>`__

View File

@ -1,36 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=embark
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View File

@ -1,296 +0,0 @@
Extending functionality with plugins
====================================
**To add a plugin to embark:**
1. Add the npm package to package.json
e.g ``npm install embark-babel --save``
2. Then add the package to ``plugins:`` in embark.json
e.g ``"plugins": { "embark-babel": {} }``
**Creating a plugin:**
1. ``mkdir yourpluginname``
2. ``cd yourpluginname``
3. ``npm init``
4. create and edit ``index.js``
5. add the following code:
.. code:: javascript
module.exports = function(embark) {
}
The ``embark`` object then provides an api to extend different functionality of embark.
**Usecases examples**
* plugin to add support for es6, jsx, coffescript, etc (``embark.registerPipeline``)
* plugin to add standard contracts or a contract framework (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to make some contracts available in all environments for use by other contracts or the dapp itself e.g a Token, a DAO, ENS, etc.. (``embark.registerContractConfiguration`` and ``embark.addContractFile``)
* plugin to add a libraries such as react or boostrap (``embark.addFileToPipeline``)
* plugin to specify a particular web3 initialization for special provider uses (``embark.registerClientWeb3Provider``)
* plugin to create a different contract wrapper (``embark.registerContractsGeneration``)
* plugin to add new console commands (``embark.registerConsoleCommand``)
* plugin to add support for another contract language such as viper, LLL, etc (``embark.registerCompiler``)
* plugin that executes certain actions when contracts are deployed (``embark.events.on``)
**embark.pluginConfig**
Object containing the config for the plugin specified in embark.json, for e.g with:
.. code:: json
"plugins": {
"embark-babel": { "files": ["**/*.js", "!**/jquery.min.js"], "presets": ["es2015", "react"] }
}
``embark.pluginConfig`` will contain ``{ "files": ["**/*.js", "!**/jquery.min.js"], "presets": ["es2015", "react"] }``
**embark.registerPipeline(matchingFiles, callback(options))**
This call will return the content of the current asset file so the plugin can transform it in some way. Typically this is used to implement pipeline plugins such as Babel, JSX, sass to css, etc..
``matchingFiles`` is an array of matching files the plugin should be called for e.g [``**/*.js``, ``!vendor/jquery.js``] matches all javascript files except vendor/jquery.js
options available:
* targetFile - filename to be generated
* source - content of the file
expected return: ``string``
.. code:: javascript
var babel = require("babel-core");
require("babel-preset-react");
module.exports = function(embark) {
embark.registerPipeline(["**/*.js", "**/*.jsx"], function(options) {
return babel.transform(options.source, {minified: true, presets: ['react']}).code;
});
}
**embark.registerContractConfiguration(contractsConfig)**
This call is used to specify a configure of one or more contracts in one or
several environments. This is useful for specifying the different configurations
a contract might have depending on the enviroment. For instance in the code
bellow, the ``DGDToken`` contract code will redeployed with the arguments
``100`` in any environment, except for the livenet since it's already deployed
there at a particular address.
Typically this call is used in combination with ``embark.addContractFile``
``contractsConfig`` is an object in the same structure as the one found in the
contracts configuration at ``config/contracts.json``. The users own
configuration will be merged with the one specified in the plugins.
.. code:: javascript
module.exports = function(embark) {
embark.registerContractConfiguration({
"default": {
"contracts": {
"DGDToken": {
"args": [
100
]
}
}
},
"livenet": {
"contracts": {
"DGDToken": {
"address": "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a"
}
}
}
});
}
**embark.addContractFile(file)**
Typically this call is used in combination with ``embark.registerContractConfiguration``. If you want to make the contract available but not automatically deployed without the user specifying so you can use ``registerContractConfiguration`` to set the contract config to ``deploy: false``, this is particularly useful for when the user is meant to extend the contract being given (e.g ``contract MyToken is StandardToken``)
``file`` is the contract file to add to embark, the path should relative to the plugin.
.. code:: javascript
module.exports = function(embark) {
embark.addContractFile("./DGDToken.sol");
}
**embark.addFileToPipeline(file, options)**
This call is used to add a file to the pipeline so it's included with the dapp on the client side.
``file`` is the file to add to the pipeline, the path should relative to the plugin.
``options`` available:
* skipPipeline - If true it will not apply transformations to the file. For
example if you have a babel plugin to transform es6 code or a minifier plugin, setting this to
true will not apply the plugin on this file.
.. code:: javascript
module.exports = function(embark) {
embark.addFileToPipeline("./jquery.js", {skipPipeline: true});
}
**embark.registerClientWeb3Provider(callback(options))**
This call can be used to override the default web3 object generation in the dapp. it's useful if you want to add a plugin to interact with services like http://infura.io or if you want to use your own web3.js library extension.
options available:
* rpcHost - configured rpc Host to connect to
* rpcPort - configured rpc Port to connect to
* blockchainConfig - object containing the full blockchain configuration for the current environment
expected return: ``string``
example:
.. code:: javascript
module.exports = function(embark) {
embark.registerClientWeb3Provider(function(options) {
return "web3 = new Web3(new Web3.providers.HttpProvider('http://" + options.rpcHost + ":" + options.rpcPort + "');";
});
}
**embark.registerContractsGeneration(callback(options))**
By default Embark will use EmbarkJS to declare contracts in the dapp. You can override and use your own client side library.
options available:
* contracts - Hash of objects containing all the deployed contracts. (key: contractName, value: contract object)
* abiDefinition
* code
* deployedAddress
* gasEstimates
* gas
* gasPrice
* runtimeByteCode
expected return: ``string``
.. code:: javascript
module.exports = function(embark) {
embark.registerContractsGeneration(function(options) {
for(var className in this.contractsManager.contracts) {
var abi = JSON.stringify(contract.abiDefinition);
return className + " = " + web3.eth.contract(" + abi + ").at('" + contract.deployedAddress + "');";
}
});
}
**embark.registerConsoleCommand(callback(options))**
This call is used to extend the console with custom commands.
expected return: ``string`` (output to print in console) or ``boolean`` (skip command if false)
.. code:: javascript
module.exports = function(embark) {
embark.registerConsoleCommand(function(cmd, options) {
if (cmd === "hello") {
return "hello there!";
}
// continue to embark or next plugin;
return false;
});
}
**embark.registerCompiler(extension, callback(contractFiles, doneCallback))**
expected doneCallback arguments: ``err`` and ``hash`` of compiled contracts
* Hash of objects containing the compiled contracts. (key: contractName, value: contract object)
* code - contract bytecode (string)
* runtimeBytecode - contract runtimeBytecode (string)
* gasEstimates - gas estimates for constructor and methods (hash)
* e.g ``{"creation":[20131,38200],"external":{"get()":269,"set(uint256)":20163,"storedData()":224},"internal":{}}``
* functionHashes - object with methods and their corresponding hash identifier (hash)
* e.g ``{"get()":"6d4ce63c","set(uint256)":"60fe47b1","storedData()":"2a1afcd9"}``
* abiDefinition - contract abi (array of objects)
* e.g ``[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}, etc...``
below a possible implementation of a solcjs plugin:
.. code:: javascript
var solc = require('solc');
module.exports = function(embark) {
embark.registerCompiler(".sol", function(contractFiles, cb) {
// prepare input for solc
var input = {};
for (var i = 0; i < contractFiles.length; i++) {
var filename = contractFiles[i].filename.replace('app/contracts/','');
input[filename] = contractFiles[i].content.toString();
}
// compile files
var output = solc.compile({sources: input}, 1);
// generate the compileObject expected by embark
var json = output.contracts;
var compiled_object = {};
for (var className in json) {
var contract = json[className];
compiled_object[className] = {};
compiled_object[className].code = contract.bytecode;
compiled_object[className].runtimeBytecode = contract.runtimeBytecode;
compiled_object[className].gasEstimates = contract.gasEstimates;
compiled_object[className].functionHashes = contract.functionHashes;
compiled_object[className].abiDefinition = JSON.parse(contract.interface);
}
cb(null, compiled_object);
});
}
**embark.logger**
To print messages to the embark log is it better to use ``embark.logger``
instead of ``console``.
e.g ``embark.logger.info("hello")``
**embark.events.on(eventName, callback(*args))**
This call is used to listen and react to events that happen in Embark such as contract deployment
* eventName - name of event to listen to
* available events:
* "contractsDeployed" - triggered when contracts have been deployed
* "file-add", "file-change", "file-remove", "file-event" - triggered on
a file change, args is (filetype, path)
* "abi", "abi-vanila", "abi-contracts-vanila" - triggered when contracts
have been deployed and returns the generated JS code
* "outputDone" - triggered when dapp is (re)generated
* "firstDeploymentDone" - triggered when the dapp is deployed and generated
for the first time
.. code:: javascript
module.exports = function(embark) {
embark.events.on("contractsDeployed", function() {
embark.logger.info("plugin says: your contracts have been deployed");
});
embark.events.on("file-changed", function(filetype, path) {
if (type === 'contract') {
embark.logger.info("plugin says: you just changed the contract at " + path);
}
});
}

View File

@ -1,22 +0,0 @@
Structuring Application
=======================
Embark is quite flexible and you can configure you're own directory
structure using ``embark.json``
.. code:: json
# embark.json
{
"contracts": ["app/contracts/**"],
"app": {
"css/app.css": ["app/css/**"],
"images/": ["app/images/**"],
"js/app.js": ["embark.js", "app/js/**"],
"index.html": "app/index.html"
},
"buildDir": "dist/",
"config": "config/",
"plugins": {}
}

View File

@ -1,48 +0,0 @@
Testing Ethereum Contracts
==========================
You can run specs with ``embark test``, it will run any test files under
``test/``.
Embark includes a testing lib to fastly run & test your contracts in a
EVM.
.. code:: javascript
# test/simple_storage_spec.js
var assert = require('assert');
var Embark = require('embark');
var EmbarkSpec = Embark.initTests();
var web3 = EmbarkSpec.web3;
describe("SimpleStorage", function() {
before(function(done) {
var contractsConfig = {
"SimpleStorage": {
args: [100]
}
};
EmbarkSpec.deployAll(contractsConfig, done);
});
it("should set constructor value", function(done) {
SimpleStorage.storedData(function(err, result) {
assert.equal(result.toNumber(), 100);
done();
});
});
it("set storage value", function(done) {
SimpleStorage.set(150, function() {
SimpleStorage.get(function(err, result) {
assert.equal(result.toNumber(), 150);
done();
});
});
});
});
Embark uses `Mocha <http://mochajs.org/>`__ by default, but you can use
any testing framework you want.

View File

@ -1,44 +0,0 @@
Usage
=====
Usage - Demo
============
You can easily create a sample working DApp with the following:
.. code:: bash
$ embark demo
$ cd embark_demo
You can run a REAL ethereum node for development purposes:
.. code:: bash
$ embark blockchain
Alternatively, to use an ethereum rpc simulator simply run:
.. code:: bash
$ embark simulator
By default embark blockchain will mine a minimum amount of ether and
will only mine when new transactions come in. This is quite usefull to
keep a low CPU. The option can be configured at
``config/blockchain.json``. Note that running a real node requires at
least 2GB of free ram, please take this into account if running it in a
VM.
Then, in another command line:
.. code:: bash
$ embark run
This will automatically deploy the contracts, update their JS bindings
and deploy your DApp to a local server at http://localhost:8000
Note that if you update your code it will automatically be re-deployed,
contracts included. There is no need to restart embark, refreshing the
page on the browser will do.

View File

@ -1,138 +0,0 @@
Configuring & Using Contracts
===============
Embark will automatically take care of deployment for you and set all
needed JS bindings. For example, the contract below:
.. code:: javascript
# app/contracts/simple_storage.sol
contract SimpleStorage {
uint public storedData;
function SimpleStorage(uint initialValue) {
storedData = initialValue;
}
function set(uint x) {
storedData = x;
}
function get() constant returns (uint retVal) {
return storedData;
}
}
Will automatically be available in Javascript as:
.. code:: javascript
# app/js/index.js
SimpleStorage.set(100);
SimpleStorage.get().then(function(value) { console.log(value.toNumber()) });
SimpleStorage.storedData().then(function(value) { console.log(value.toNumber()) });
You can specify for each contract and environment its gas costs and
arguments:
.. code:: json
# config/contracts.json
{
"development": {
"gas": "auto",
"contracts": {
"SimpleStorage": {
"args": [
100
]
}
}
}
}
If you are using multiple contracts, you can pass a reference to another
contract as ``$ContractName``, Embark will automatically replace this
with the correct address for the contract.
.. code:: json
# config/contracts.json
{
...
"development": {
"contracts": {
"SimpleStorage": {
"args": [
100,
"$MyStorage"
]
},
"MyStorage": {
"args": [
"initial string"
]
},
"MyMainContract": {
"args": [
"$SimpleStorage"
]
}
}
}
...
}
You can now deploy many instances of the same contract. e.g
.. code:: json
# config/contracts.json
{
"development": {
"contracts": {
"Currency": {
"deploy": false,
"args": [
100
]
},
"Usd": {
"instanceOf": "Currency",
"args": [
200
]
},
"MyCoin": {
"instanceOf": "Currency",
"args": [
200
]
}
}
}
}
...
Contracts addresses can be defined, If an address is defined the
contract wouldn't be deployed but its defined address will be used
instead.
.. code:: json
# config/contracts.json
{
...
"development": {
"contracts": {
"UserStorage": {
"address": "0x123456"
},
"UserManagement": {
"args": [
"$UserStorage"
]
}
}
}
...
}

View File

@ -1,34 +0,0 @@
Using Embark with Grunt
====================================
**1. Edit embark.json**
Edit ``embark.json`` to have the line ``"js/app.js": ["embark.js"]``, this will make embark create the file containing the contracts initilization to ``dist/app.js``.
.. code:: json
{
"contracts": ["app/contracts/**"],
"app": {
"app.js": ["embark.js"]
},
"buildDir": "dist/",
"config": "config/",
"plugins": {
}
}
**2. add the generated file to Grunt config file so it's included with the other assets**
.. code:: coffee
module.exports = (grunt) ->
grunt.initConfig(
files:
js:
src: [
"dist/app.js"
"app/js/**/*.js"
]

View File

@ -1,26 +0,0 @@
Working with different chains
=============================
You can specify which environment to deploy to:
``$ embark blockchain livenet``
``$ embark run livenet``
The environment is a specific blockchain configuration that can be
managed at config/blockchain.json
.. code:: json
# config/blockchain.json
...
"livenet": {
"networkType": "livenet",
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"account": {
"password": "config/livenet/password"
}
},
...

View File

@ -1,766 +0,0 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["EmbarkJS"] = factory();
else
root["EmbarkJS"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
/*jshint esversion: 6 */
//var Ipfs = require('./ipfs.js');
//=========================================================
// Embark Smart Contracts
//=========================================================
var EmbarkJS = {};
EmbarkJS.isNewWeb3 = function() {
var _web3 = new Web3();
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Contract = function(options) {
var self = this;
var i, abiElement;
var ContractClass;
this.abi = options.abi;
this.address = options.address;
this.code = '0x' + options.code;
this.web3 = options.web3 || web3;
if (EmbarkJS.isNewWeb3()) {
// TODO:
// add default **from** address
// add gasPrice
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
}
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function(p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
self[p] = function() {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, result) {
if (err) {
promise.error(err);
} else {
promise.cb(result);
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function(_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
var promise = new Promise(function(resolve, reject) {
args.push(function(err, transaction) {
promise.tx = transaction;
if (err) {
return reject(err);
}
var getConfirmation = function() {
self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof(transaction) !== "string" || props.constant) {
resolve(transaction);
} else {
getConfirmation();
}
});
fn.apply(fn, args);
});
return promise;
};
return true;
}
return false;
});
}
};
EmbarkJS.Contract.prototype.deploy = function(args, _options) {
var self = this;
var contractParams;
var options = _options || {};
contractParams = args || [];
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function(resolve, reject) {
contractParams.push(function(err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
return promise;
};
EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy;
EmbarkJS.Contract.prototype.at = function(address) {
return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address });
};
EmbarkJS.Contract.prototype.send = function(value, unit, _options) {
var options, wei;
if (typeof unit === 'object') {
options = unit;
wei = value;
} else {
options = _options || {};
wei = this.web3.toWei(value, unit);
}
options.to = this.address;
options.value = wei;
console.log(options);
this.web3.eth.sendTransaction(options);
};
//=========================================================
// Embark Storage
//=========================================================
EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {
IPFS: 'ipfs',
SWARM: 'swarm'
};
EmbarkJS.Storage.IPFS = {};
EmbarkJS.Storage.saveText = function(text) {
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function(hash) {
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function(inputSelector) {
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function(hash) {
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.setProvider = function(provider, options) {
var self = this;
var promise = new Promise(function(resolve, reject) {
if (provider.toLowerCase() === EmbarkJS.Storage.Providers.IPFS) {
//I don't think currentStorage is used anywhere, this might not be needed
//for now until additional storage providers are supported. But keeping it
//anyways
self.currentStorage = EmbarkJS.Storage.IPFS;
try {
if (options === undefined) {
self.ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/";
} else {
self.ipfsConnection = IpfsApi(options.server, options.port);
self._getUrl = options.getUrl || "http://localhost:8080/ipfs/";
}
resolve(self);
} catch (err) {
self.ipfsConnection = null;
reject(new Error('Failed to connect to IPFS'));
}
} else if (provider.toLowerCase() === EmbarkJS.Storage.SWARM) {
reject('Swarm not implemented');
// TODO Implement Swarm
// this.currentStorage = EmbarkJS.Storage.SWARM;
// if (options === undefined) {
// //Connect to default Swarm node
// } else {
// //Connect using options
// }
} else {
reject('Unknown storage provider');
}
});
return promise;
};
EmbarkJS.Storage.IPFS.saveText = function(text) {
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
EmbarkJS.Storage.ipfsConnection.add((new EmbarkJS.Storage.ipfsConnection.Buffer(text)), function(err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
});
return promise;
};
EmbarkJS.Storage.IPFS.get = function(hash) {
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
EmbarkJS.Storage.ipfsConnection.object.get([hash]).then(function(node) {
resolve(node.data);
}).catch(function(err) {
reject(err);
});
});
return promise;
};
EmbarkJS.Storage.IPFS.uploadFile = function(inputSelector) {
var file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
var reader = new FileReader();
reader.onloadend = function() {
var fileContent = reader.result;
var buffer = EmbarkJS.Storage.ipfsConnection.Buffer.from(fileContent);
EmbarkJS.Storage.ipfsConnection.add(buffer, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
};
reader.readAsArrayBuffer(file);
});
return promise;
};
EmbarkJS.Storage.IPFS.getUrl = function(hash) {
return (self._getUrl || "http://localhost:8080/ipfs/") + hash;
};
//=========================================================
// Embark Messaging
//=========================================================
EmbarkJS.Messages = {};
EmbarkJS.Messages.web3CompatibleWithV5 = function() {
var _web3 = new Web3();
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[1], 10) >= 20;
};
EmbarkJS.Messages.isNewWeb3 = function() {
var _web3 = new Web3();
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Messages.getWhisperVersion = function(cb) {
if (this.isNewWeb3()) {
this.currentMessages.web3.shh.getVersion(function(err, version) {
cb(err, version);
});
} else {
this.currentMessages.web3.version.getWhisper(function(err, res) {
cb(err, web3.version.whisper);
});
}
};
EmbarkJS.Messages.setProvider = function(provider, options) {
var self = this;
var ipfs;
if (provider === 'whisper') {
this.providerName = 'whisper';
this.currentMessages = EmbarkJS.Messages.Whisper;
let provider;
if (options === undefined) {
provider = "localhost:8546";
} else {
provider = options.server + ':' + options.port;
}
if (this.isNewWeb3()) {
self.currentMessages.web3 = new Web3(new Web3.providers.WebsocketProvider("ws://" + provider));
} else {
self.currentMessages.web3 = new Web3(new Web3.providers.HttpProvider("http://" + provider));
}
self.getWhisperVersion(function(err, version) {
if (err) {
console.log("whisper not available");
} else if (version >= 5) {
if (self.web3CompatibleWithV5()) {
self.currentMessages.web3.shh.newSymKey().then((id) => {self.currentMessages.symKeyID = id;});
self.currentMessages.web3.shh.newKeyPair().then((id) => {self.currentMessages.sig = id;});
} else {
console.log("this version of whisper in this node");
}
} else {
self.currentMessages.identity = self.currentMessages.web3.shh.newIdentity();
}
self.currentMessages.whisperVersion = self.currentMessages.web3.version.whisper;
});
} else if (provider === 'orbit') {
this.providerName = 'orbit';
this.currentMessages = EmbarkJS.Messages.Orbit;
if (options === undefined) {
ipfs = HaadIpfsApi('localhost', '5001');
} else {
ipfs = HaadIpfsApi(options.host, options.port);
}
this.currentMessages.orbit = new Orbit(ipfs);
if (typeof(web3) === "undefined") {
this.currentMessages.orbit.connect(Math.random().toString(36).substring(2));
} else {
this.currentMessages.orbit.connect(web3.eth.accounts[0]);
}
} else {
throw Error('Unknown message provider');
}
};
EmbarkJS.Messages.sendMessage = function(options) {
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function(options) {
return this.currentMessages.listenTo(options);
};
EmbarkJS.Messages.Whisper = {};
EmbarkJS.Messages.Whisper.sendMessage = function(options) {
var topics, data, ttl, priority, payload;
if (EmbarkJS.Messages.isNewWeb3()) {
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
priority = options.priority || 1000;
var powTime = options.powTime || 3;
var powTarget = options.powTarget || 0.5;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
topics = this.web3.utils.toHex(topics).slice(0, 10);
payload = JSON.stringify(data);
let message = {
symKeyID: this.symKeyID, // encrypts using the sym key ID
sig: this.sig, // signs the message using the keyPair ID
ttl: ttl,
topic: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
powTime: powTime,
powTarget: powTarget
};
this.web3.shh.post(message, function() { });
} else {
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
priority = options.priority || 1000;
var identity = options.identity || this.identity || web3.shh.newIdentity();
var _topics;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
_topics = [EmbarkJS.Utils.fromAscii(topics)];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
payload = JSON.stringify(data);
var message;
message = {
from: identity,
topics: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
ttl: ttl,
priority: priority
};
return EmbarkJS.Messages.currentMessages.web3.shh.post(message, function() { });
}
};
EmbarkJS.Messages.Whisper.listenTo = function(options) {
var topics, _topics, messageEvents;
if (EmbarkJS.Messages.isNewWeb3()) {
messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
messageEvents.prototype.stop = function() {
this.filter.stopWatching();
};
topics = options.topic || options.topics;
_topics = [];
let promise = new messageEvents();
// listenTo
if (typeof topics === 'string') {
topics = [this.web3.utils.toHex(topics).slice(0, 10)];
} else {
topics = topics.map((t) => this.web3.utils.toHex(t).slice(0, 10));
}
let filter = this.web3.shh.subscribe("messages", {
symKeyID: this.symKeyID,
topics: topics
}).on('data', function(result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
data = {
topic: result.topic,
data: payload,
//from: result.from,
time: result.timestamp
};
promise.cb(payload, data, result);
});
promise.filter = filter;
return promise;
} else {
topics = options.topic || options.topics;
_topics = [];
messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
messageEvents.prototype.stop = function() {
this.filter.stopWatching();
};
if (typeof topics === 'string') {
_topics = [topics];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
var filterOptions = {
topics: topics
};
let promise = new messageEvents();
let filter = this.web3.shh.filter(filterOptions, function(err, result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
if (err) {
promise.error(err);
} else {
data = {
topic: topics,
data: payload,
from: result.from,
time: (new Date(result.sent * 1000))
};
promise.cb(payload, data, result);
}
});
promise.filter = filter;
return promise;
}
};
EmbarkJS.Messages.Orbit = {};
EmbarkJS.Messages.Orbit.sendMessage = function(options) {
var topics = options.topic || options.topics;
var data = options.data || options.payload;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
topics = topics;
} else {
// TODO: better to just send to different channels instead
topics = topics.join(',');
}
this.orbit.join(topics);
var payload = JSON.stringify(data);
this.orbit.send(topics, data);
};
EmbarkJS.Messages.Orbit.listenTo = function(options) {
var self = this;
var topics = options.topic || options.topics;
if (typeof topics === 'string') {
topics = topics;
} else {
topics = topics.join(',');
}
this.orbit.join(topics);
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
var promise = new messageEvents();
this.orbit.events.on('message', (channel, message) => {
// TODO: looks like sometimes it's receving messages from all topics
if (topics !== channel) return;
self.orbit.getPost(message.payload.value, true).then((post) => {
var data = {
topic: channel,
data: post.content,
from: post.meta.from.name,
time: (new Date(post.meta.ts))
};
promise.cb(post.content, data, post);
});
});
return promise;
};
EmbarkJS.Utils = {
fromAscii: function(str) {
var _web3 = new Web3();
return _web3.utils ? _web3.utils.fromAscii(str) : _web3.fromAscii(str);
},
toAscii: function(str) {
var _web3 = new Web3();
return _web3.utils.toAscii(str);
}
};
module.exports = EmbarkJS;
/***/ })
/******/ ]);
});

View File

@ -1,10 +1,3 @@
/*jshint esversion: 6 */
//var Ipfs = require('./ipfs.js');
//=========================================================
// Embark Smart Contracts
//=========================================================
var EmbarkJS = {};
EmbarkJS.isNewWeb3 = function() {
@ -23,7 +16,8 @@ EmbarkJS.Contract = function(options) {
this.abi = options.abi;
this.address = options.address;
this.code = '0x' + options.code;
this.web3 = options.web3 || web3;
//this.web3 = options.web3 || web3;
this.web3 = options.web3 || window.web3;
if (EmbarkJS.isNewWeb3()) {
// TODO:
@ -31,6 +25,7 @@ EmbarkJS.Contract = function(options) {
// add gasPrice
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
return ContractClass;
} else {
@ -181,488 +176,98 @@ EmbarkJS.Contract.prototype.send = function(value, unit, _options) {
options.to = this.address;
options.value = wei;
console.log(options);
this.web3.eth.sendTransaction(options);
};
//=========================================================
// Embark Storage
//=========================================================
EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {
IPFS: 'ipfs',
SWARM: 'swarm'
};
EmbarkJS.Storage.IPFS = {};
EmbarkJS.Storage.Providers = {};
EmbarkJS.Storage.saveText = function(text) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function(inputSelector) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.registerProvider = function(providerName, obj) {
EmbarkJS.Storage.Providers[providerName] = obj;
};
EmbarkJS.Storage.setProvider = function(provider, options) {
var self = this;
var promise = new Promise(function(resolve, reject) {
if (provider.toLowerCase() === EmbarkJS.Storage.Providers.IPFS) {
//I don't think currentStorage is used anywhere, this might not be needed
//for now until additional storage providers are supported. But keeping it
//anyways
self.currentStorage = EmbarkJS.Storage.IPFS;
let providerObj = this.Providers[provider];
try {
if (options === undefined) {
self.ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/";
} else {
self.ipfsConnection = IpfsApi(options.server, options.port);
self._getUrl = options.getUrl || "http://localhost:8080/ipfs/";
if (!providerObj) {
throw new Error('Unknown storage provider');
}
resolve(self);
} catch (err) {
self.ipfsConnection = null;
reject(new Error('Failed to connect to IPFS'));
}
} else if (provider.toLowerCase() === EmbarkJS.Storage.SWARM) {
reject('Swarm not implemented');
// TODO Implement Swarm
// this.currentStorage = EmbarkJS.Storage.SWARM;
// if (options === undefined) {
// //Connect to default Swarm node
// } else {
// //Connect using options
// }
} else {
reject('Unknown storage provider');
}
});
return promise;
this.currentStorage = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Storage.IPFS.saveText = function(text) {
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
EmbarkJS.Storage.ipfsConnection.add((new EmbarkJS.Storage.ipfsConnection.Buffer(text)), function(err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
});
return promise;
EmbarkJS.Storage.isAvailable = function(){
return this.currentStorage.isAvailable();
};
EmbarkJS.Storage.IPFS.get = function(hash) {
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
EmbarkJS.Storage.ipfsConnection.object.get([hash]).then(function(node) {
resolve(node.data);
}).catch(function(err) {
reject(err);
});
});
return promise;
};
EmbarkJS.Storage.IPFS.uploadFile = function(inputSelector) {
var file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
var promise = new Promise(function(resolve, reject) {
if (!EmbarkJS.Storage.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
var reader = new FileReader();
reader.onloadend = function() {
var fileContent = reader.result;
var buffer = EmbarkJS.Storage.ipfsConnection.Buffer.from(fileContent);
EmbarkJS.Storage.ipfsConnection.add(buffer, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
};
reader.readAsArrayBuffer(file);
});
return promise;
};
EmbarkJS.Storage.IPFS.getUrl = function(hash) {
return (self._getUrl || "http://localhost:8080/ipfs/") + hash;
};
//=========================================================
// Embark Messaging
//=========================================================
EmbarkJS.Messages = {};
EmbarkJS.Messages.web3CompatibleWithV5 = function() {
var _web3 = new Web3();
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[1], 10) >= 20;
};
EmbarkJS.Messages.Providers = {};
EmbarkJS.Messages.isNewWeb3 = function() {
var _web3 = new Web3();
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Messages.getWhisperVersion = function(cb) {
if (this.isNewWeb3()) {
this.currentMessages.web3.shh.getVersion(function(err, version) {
cb(err, version);
});
} else {
this.currentMessages.web3.version.getWhisper(function(err, res) {
cb(err, web3.version.whisper);
});
}
EmbarkJS.Messages.registerProvider = function(providerName, obj) {
EmbarkJS.Messages.Providers[providerName] = obj;
};
EmbarkJS.Messages.setProvider = function(provider, options) {
var self = this;
var ipfs;
if (provider === 'whisper') {
this.providerName = 'whisper';
this.currentMessages = EmbarkJS.Messages.Whisper;
let provider;
if (options === undefined) {
provider = "localhost:8546";
} else {
provider = options.server + ':' + options.port;
}
if (this.isNewWeb3()) {
self.currentMessages.web3 = new Web3(new Web3.providers.WebsocketProvider("ws://" + provider));
} else {
self.currentMessages.web3 = new Web3(new Web3.providers.HttpProvider("http://" + provider));
}
self.getWhisperVersion(function(err, version) {
if (err) {
console.log("whisper not available");
} else if (version >= 5) {
if (self.web3CompatibleWithV5()) {
self.currentMessages.web3.shh.newSymKey().then((id) => {self.currentMessages.symKeyID = id;});
self.currentMessages.web3.shh.newKeyPair().then((id) => {self.currentMessages.sig = id;});
} else {
console.log("this version of whisper in this node");
}
} else {
self.currentMessages.identity = self.currentMessages.web3.shh.newIdentity();
}
self.currentMessages.whisperVersion = self.currentMessages.web3.version.whisper;
});
} else if (provider === 'orbit') {
this.providerName = 'orbit';
this.currentMessages = EmbarkJS.Messages.Orbit;
if (options === undefined) {
ipfs = HaadIpfsApi('localhost', '5001');
} else {
ipfs = HaadIpfsApi(options.host, options.port);
}
this.currentMessages.orbit = new Orbit(ipfs);
if (typeof(web3) === "undefined") {
this.currentMessages.orbit.connect(Math.random().toString(36).substring(2));
} else {
this.currentMessages.orbit.connect(web3.eth.accounts[0]);
}
} else {
throw Error('Unknown message provider');
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown messages provider');
}
this.currentMessages = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Messages.isAvailable = function(){
return this.currentMessages.isAvailable();
};
EmbarkJS.Messages.sendMessage = function(options) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function(options) {
return this.currentMessages.listenTo(options);
};
EmbarkJS.Messages.Whisper = {};
EmbarkJS.Messages.Whisper.sendMessage = function(options) {
var topics, data, ttl, priority, payload;
if (EmbarkJS.Messages.isNewWeb3()) {
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
priority = options.priority || 1000;
var powTime = options.powTime || 3;
var powTarget = options.powTarget || 0.5;
if (topics === undefined) {
throw new Error("missing option: topic");
EmbarkJS.Messages.listenTo = function(options, callback) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
if (data === undefined) {
throw new Error("missing option: data");
}
topics = this.web3.utils.toHex(topics).slice(0, 10);
payload = JSON.stringify(data);
let message = {
symKeyID: this.symKeyID, // encrypts using the sym key ID
sig: this.sig, // signs the message using the keyPair ID
ttl: ttl,
topic: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
powTime: powTime,
powTarget: powTarget
};
this.web3.shh.post(message, function() { });
} else {
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
priority = options.priority || 1000;
var identity = options.identity || this.identity || web3.shh.newIdentity();
var _topics;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
_topics = [EmbarkJS.Utils.fromAscii(topics)];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
payload = JSON.stringify(data);
var message;
message = {
from: identity,
topics: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
ttl: ttl,
priority: priority
};
return EmbarkJS.Messages.currentMessages.web3.shh.post(message, function() { });
}
};
EmbarkJS.Messages.Whisper.listenTo = function(options) {
var topics, _topics, messageEvents;
if (EmbarkJS.Messages.isNewWeb3()) {
messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
messageEvents.prototype.stop = function() {
this.filter.stopWatching();
};
topics = options.topic || options.topics;
_topics = [];
let promise = new messageEvents();
// listenTo
if (typeof topics === 'string') {
topics = [this.web3.utils.toHex(topics).slice(0, 10)];
} else {
topics = topics.map((t) => this.web3.utils.toHex(t).slice(0, 10));
}
let filter = this.web3.shh.subscribe("messages", {
symKeyID: this.symKeyID,
topics: topics
}).on('data', function(result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
data = {
topic: result.topic,
data: payload,
//from: result.from,
time: result.timestamp
};
promise.cb(payload, data, result);
});
promise.filter = filter;
return promise;
} else {
topics = options.topic || options.topics;
_topics = [];
messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
messageEvents.prototype.stop = function() {
this.filter.stopWatching();
};
if (typeof topics === 'string') {
_topics = [topics];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
var filterOptions = {
topics: topics
};
let promise = new messageEvents();
let filter = this.web3.shh.filter(filterOptions, function(err, result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
if (err) {
promise.error(err);
} else {
data = {
topic: topics,
data: payload,
from: result.from,
time: (new Date(result.sent * 1000))
};
promise.cb(payload, data, result);
}
});
promise.filter = filter;
return promise;
}
};
EmbarkJS.Messages.Orbit = {};
EmbarkJS.Messages.Orbit.sendMessage = function(options) {
var topics = options.topic || options.topics;
var data = options.data || options.payload;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
topics = topics;
} else {
// TODO: better to just send to different channels instead
topics = topics.join(',');
}
this.orbit.join(topics);
var payload = JSON.stringify(data);
this.orbit.send(topics, data);
};
EmbarkJS.Messages.Orbit.listenTo = function(options) {
var self = this;
var topics = options.topic || options.topics;
if (typeof topics === 'string') {
topics = topics;
} else {
topics = topics.join(',');
}
this.orbit.join(topics);
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
var promise = new messageEvents();
this.orbit.events.on('message', (channel, message) => {
// TODO: looks like sometimes it's receving messages from all topics
if (topics !== channel) return;
self.orbit.getPost(message.payload.value, true).then((post) => {
var data = {
topic: channel,
data: post.content,
from: post.meta.from.name,
time: (new Date(post.meta.ts))
};
promise.cb(post.content, data, post);
});
});
return promise;
return this.currentMessages.listenTo(options, callback);
};
EmbarkJS.Utils = {
@ -676,4 +281,4 @@ EmbarkJS.Utils = {
}
};
module.exports = EmbarkJS;
export default EmbarkJS;

95
js/embarkjs/orbit.js Normal file
View File

@ -0,0 +1,95 @@
EmbarkJS.Messages.Orbit = {};
EmbarkJS.Messages.Orbit.setProvider = function(options) {
this.providerName = 'orbit';
this.currentMessages = EmbarkJS.Messages.Orbit;
if (options === undefined) {
ipfs = HaadIpfsApi('localhost', '5001');
} else {
ipfs = HaadIpfsApi(options.host, options.port);
}
this.currentMessages.orbit = new Orbit(ipfs);
if (typeof(web3) === "undefined") {
this.currentMessages.orbit.connect(Math.random().toString(36).substring(2));
} else {
this.currentMessages.orbit.connect(web3.eth.accounts[0]);
}
};
EmbarkJS.Messages.Orbit.sendMessage = function(options) {
var topics = options.topic || options.topics;
var data = options.data || options.payload;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
topics = topics;
} else {
// TODO: better to just send to different channels instead
topics = topics.join(',');
}
this.orbit.join(topics);
var payload = JSON.stringify(data);
this.orbit.send(topics, data);
};
EmbarkJS.Messages.Orbit.listenTo = function(options) {
var self = this;
var topics = options.topic || options.topics;
if (typeof topics === 'string') {
topics = topics;
} else {
topics = topics.join(',');
}
this.orbit.join(topics);
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
var promise = new messageEvents();
this.orbit.events.on('message', (channel, message) => {
// TODO: looks like sometimes it's receving messages from all topics
if (topics !== channel) return;
self.orbit.getPost(message.payload.value, true).then((post) => {
var data = {
topic: channel,
data: post.content,
from: post.meta.from.name,
time: (new Date(post.meta.ts))
};
promise.cb(post.content, data, post);
});
});
return promise;
};
// TODO: needs a real check for availability
// TODO: not tested as orbit is not loaded and therefore the provider is not available
EmbarkJS.Messages.Orbit.isAvailable = function(){
return new Promise((resolve) => {
if(!this.orbit) resolve(false);
resolve(true);
});
}

61
js/ipfs-api.min.js vendored

File diff suppressed because one or more lines are too long

61676
js/ipfs.js

File diff suppressed because one or more lines are too long

60
js/orbit.min.js vendored

File diff suppressed because one or more lines are too long

16285
js/web3.js

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
const program = require('commander');
const promptly = require('promptly');
const utils = require('./utils/utils.js');
const Embark = require('../lib/index');
let embark = new Embark;
@ -16,7 +17,10 @@ class Cmd {
this.blockchain();
this.simulator();
this.test();
this.reset();
this.graph();
this.upload();
this.versionCmd();
this.otherCommands();
//If no arguments are passed display help by default
@ -40,7 +44,8 @@ class Cmd {
program
.command('new [name]')
.description('new application')
.action(function (name) {
.option('--simple', 'create a barebones project meant only for contract development')
.action(function (name, options) {
if (name === undefined) {
return promptly.prompt("Name your app (default is embarkDApp):", {
default: "embarkDApp",
@ -53,13 +58,20 @@ class Cmd {
err.retry();
} else {
//slightly different assignment of name since it comes from child prompt
if (options.simple) {
embark.generateTemplate('simple', './', inputvalue);
} else {
embark.generateTemplate('boilerplate', './', inputvalue);
}
}
});
} else {
if (options.simple) {
embark.generateTemplate('simple', './', name);
} else {
embark.generateTemplate('boilerplate', './', name);
}
}
});
}
@ -75,9 +87,14 @@ class Cmd {
build() {
program
.command('build [environment]')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('deploy and build dapp at dist/ (default: development)')
.action(function (env, _options) {
embark.build({env: env || 'development'});
_options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
embark.build(_options);
});
}
@ -89,6 +106,8 @@ class Cmd {
.option('--noserver', 'disable the development webserver')
.option('--nodashboard', 'simple mode, disables the dashboard')
.option('--no-color', 'no colors in case it\'s needed for compatbility purposes')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('run dapp (default: development)')
.action(function (env, options) {
embark.run({
@ -96,7 +115,9 @@ class Cmd {
serverPort: options.port,
serverHost: options.host,
runWebserver: !options.noserver,
useDashboard: !options.nodashboard
useDashboard: !options.nodashboard,
logFile: options.logfile,
logLevel: options.loglevel
});
});
}
@ -104,7 +125,7 @@ class Cmd {
blockchain() {
program
.command('blockchain [environment]')
.option('-c, --client [client]', 'Use a specific ethereum client or simulator (supported: geth, parity, ethersim, testrpc')
.option('-c, --client [client]', 'Use a specific ethereum client or simulator (supported: geth, testrpc)')
.description('run blockchain server (default: development)')
.action(function (env, options) {
embark.initConfig(env || 'development', {
@ -120,14 +141,24 @@ class Cmd {
.command('simulator [environment]')
.description('run a fast ethereum rpc simulator')
.option('--testrpc', 'use testrpc as the rpc simulator [default]')
.option('-p, --port [port]', 'port to run the rpc simulator (default: 8000)')
.option('-p, --port [port]', 'port to run the rpc simulator (default: 8545)')
.option('-h, --host [host]', 'host to run the rpc simulator (default: localhost)')
.option('-a, --accounts [numAccounts]', 'number of accounts (default: 10)')
.option('-e, --defaultBalanceEther [balance]', 'Amount of ether to assign each test account (default: 100)')
.option('-l, --gasLimit [gasLimit]', 'custom gas limit (default: 8000000)')
.action(function (env, options) {
embark.initConfig(env || 'development', {
embarkConfig: 'embark.json',
interceptLogs: false
});
embark.simulator({port: options.port, host: options.host});
embark.simulator({
port: options.port,
host: options.host,
numAccounts: options.numAccounts,
defaultBalance: options.balance,
gasLimit: options.gasLimit
});
});
}
@ -145,26 +176,67 @@ class Cmd {
upload() {
program
.command('upload [platform] [environment]')
.description('upload your dapp to a decentralized storage. possible options: ipfs, swarm (e.g embark upload swarm)')
.command('upload <platform> [environment]')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('Upload your dapp to a decentralized storage (e.g embark upload ipfs).')
.action(function (platform, env, _options) {
// TODO: get env in cmd line as well
embark.initConfig(env || 'development', {
_options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
embark.upload(platform, _options);
});
}
graph() {
program
.command('graph [environment]')
.description('generates documentation based on the smart contracts configured')
.action(function (env, options) {
embark.graph({
env: env || 'development',
logFile: options.logfile
});
});
}
reset() {
program
.command('reset')
.description('resets embarks state on this dapp including clearing cache')
.action(function () {
embark.initConfig('development', {
embarkConfig: 'embark.json', interceptLogs: false
});
embark.upload(platform);
embark.reset();
});
}
versionCmd() {
program
.command('version')
.description('output the version number')
.action(function () {
console.log(embark.version);
process.exit(0);
});
}
otherCommands() {
program
.action(function (env) {
console.log('unknown command "%s"'.red, env);
.action(function (cmd) {
console.log('unknown command "%s"'.red, cmd);
let dictionary = ['new', 'demo', 'build', 'run', 'blockchain', 'simulator', 'test', 'upload', 'version'];
let suggestion = utils.proposeAlternative(cmd, dictionary);
if (suggestion) {
console.log('did you mean "%s"?'.green, suggestion);
}
console.log("type embark --help to see the available commands");
process.exit(0);
});
}
}
module.exports = Cmd;

View File

@ -4,12 +4,16 @@ var fs = require('../../core/fs.js');
var GethCommands = require('./geth_commands.js');
/*eslint complexity: ["error", 22]*/
/*eslint complexity: ["error", 35]*/
var Blockchain = function(options) {
this.blockchainConfig = options.blockchainConfig;
this.env = options.env || 'development';
this.client = options.client;
if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') {
console.log("===> warning: running default config on a non-development environment");
}
this.config = {
geth_bin: this.blockchainConfig.geth_bin || 'geth',
networkType: this.blockchainConfig.networkType || 'custom',
@ -28,13 +32,24 @@ var Blockchain = function(options) {
maxpeers: ((this.blockchainConfig.maxpeers === 0) ? 0 : (this.blockchainConfig.maxpeers || 25)),
bootnodes: this.blockchainConfig.bootnodes || "",
rpcApi: (this.blockchainConfig.rpcApi || ['eth', 'web3', 'net']),
wsRPC: (this.blockchainConfig.wsRPC === undefined) || this.blockchainConfig.wsRPC,
wsHost: this.blockchainConfig.wsHost || 'localhost',
wsPort: this.blockchainConfig.wsPort || 8546,
wsOrigins: this.blockchainConfig.wsOrigins || false,
wsApi: (this.blockchainConfig.wsApi || ['eth', 'web3', 'net', 'shh']),
vmdebug: this.blockchainConfig.vmdebug || false
vmdebug: this.blockchainConfig.vmdebug || false,
targetGasLimit: this.blockchainConfig.targetGasLimit || false,
light: this.blockchainConfig.light || false,
fast: this.blockchainConfig.fast || false
};
if (this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') {
this.config.account = {};
this.config.account.password = fs.embarkPath("templates/boilerplate/config/development/password");
this.config.genesisBlock = fs.embarkPath("templates/boilerplate/config/development/genesis.json");
this.config.datadir = fs.embarkPath(".embark/development/datadir");
}
this.client = new options.client({config: this.config, env: this.env});
};
@ -50,12 +65,26 @@ Blockchain.prototype.run = function() {
console.log(("Embark Blockchain Using: " + this.client.name.underline).magenta);
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
if (!this.isClientInstalled()) {
console.log(("could not find " + this.config.geth_bin + " command; is " + this.client.name + " installed or in the PATH?").green);
return;
}
var address = this.initChainAndGetAddress();
this.client.mainCommand(address, function(cmd) {
self.runCommand(cmd, {async: true});
});
};
Blockchain.prototype.isClientInstalled = function() {
let versionCmd = this.client.determineVersion();
let result = this.runCommand(versionCmd);
if (result.output === undefined || result.output.indexOf("not found") >= 0) {
return false;
}
return true;
};
Blockchain.prototype.initChainAndGetAddress = function() {
var address = null, result;
@ -94,4 +123,3 @@ var BlockchainClient = function(blockchainConfig, client, env) {
};
module.exports = BlockchainClient;

View File

@ -34,6 +34,10 @@ class GethCommands {
return cmd;
}
determineVersion() {
return this.geth_bin + " version";
}
determineNetworkType(config) {
let cmd = "";
if (config.networkType === 'testnet') {
@ -92,22 +96,24 @@ class GethCommands {
determineWsOptions(config) {
let cmd = "";
if (config.wsRPC) {
cmd += "--ws ";
cmd += "--wsport " + config.wsPort + " ";
cmd += "--wsaddr " + config.wsHost + " ";
if (config.wsOrigins) {
if (config.wsOrigins === '*') {
console.log('==================================');
console.log('rpcCorsDomain set to *');
console.log('wsOrigins set to *');
console.log('make sure you know what you are doing');
console.log('==================================');
}
cmd += "--wsorigins \"" + config.wsOrigins + "\" ";
} else {
console.log('==================================');
console.log('warning: cors is not set');
console.log('warning: wsOrigins is not set');
console.log('==================================');
}
}
return cmd;
}
@ -187,6 +193,12 @@ class GethCommands {
}
callback(null, "");
},
function gasLimit(callback) {
if (config.targetGasLimit) {
return callback(null, "--targetgaslimit " + config.targetGasLimit);
}
callback(null, "");
},
function mineWhenNeeded(callback) {
if (config.mineWhenNeeded) {
return callback(null, "js .embark/" + self.env + "/js/mine.js");
@ -203,4 +215,3 @@ class GethCommands {
}
module.exports = GethCommands;

110
lib/cmds/graph.js Normal file
View File

@ -0,0 +1,110 @@
const Viz = require('viz.js');
const fs = require('fs');
class GraphGenerator {
constructor(engine) {
this.engine = engine;
}
generate() {
let id = 0;
let contractString = "";
let relationshipString = "";
let idMapping = {};
let contractInheritance = {};
for (let contract in this.engine.contractsManager.contracts) {
id++;
idMapping[contract] = id;
let contractLabel = "";
contractLabel += `${contract}`;
let tooltip = contract;
if(this.engine.contractsManager.contracts[contract].instanceOf !== undefined &&
this.engine.contractsManager.contracts[this.engine.contractsManager.contracts[contract].instanceOf] !== undefined){
contractInheritance[contract] = this.engine.contractsManager.contracts[contract].instanceOf;
contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf;
tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf;
} else {
contractLabel += "|";
for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){
switch(this.engine.contractsManager.contracts[contract].abiDefinition[i].type){
case 'fallback':
contractLabel += "«fallback»()\\l";
break;
case 'constructor':
contractLabel += "«constructor»(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
case 'event':
contractLabel += "«event»" + this.engine.contractsManager.contracts[contract].abiDefinition[i].name + "(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
default: break;
}
}
let fHashes = this.engine.contractsManager.contracts[contract].functionHashes;
if(fHashes != {} && fHashes != undefined){
for(let method in this.engine.contractsManager.contracts[contract].functionHashes){
contractLabel += method + '\\l';
}
}
}
let others = '';
if(!this.engine.contractsManager.contracts[contract].deploy){
others = 'fontcolor="#c3c3c3", color="#a0a0a0"';
tooltip += " (not deployed)";
}
contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`;
}
for (let c in this.engine.contractsManager.contractDependencies){
let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c]));
contractDependencies.forEach(function(d){
if(idMapping[c] !== undefined && idMapping[d] !== undefined){
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
}
});
}
for (let c in contractInheritance){
relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`;
}
let dot = `
digraph Contracts {
node[shape=record,style=filled]
edge[dir=back, arrowtail=empty]
${contractString}
${relationshipString}
}`;
let svg = Viz(dot);
let filename = "diagram.svg";
fs.writeFileSync(filename, svg, (err) => {
if (err) throw err;
});
}
}
module.exports = GraphGenerator;

8
lib/cmds/reset.js Normal file
View File

@ -0,0 +1,8 @@
var fs = require('../core/fs.js');
module.exports = function() {
fs.removeSync('./chains.json');
fs.removeSync('.embark/');
fs.removeSync('dist/');
console.log("reset done!".green);
};

View File

@ -3,18 +3,41 @@ let shelljs = require('shelljs');
class Simulator {
constructor(options) {
this.blockchainConfig = options.blockchainConfig;
this.logger = options.logger;
}
run(options) {
let cmds = [];
const testrpc = shelljs.which('testrpc');
const ganache = shelljs.which('ganache-cli');
if (!testrpc && !ganache) {
this.logger.warn('Ganache CLI (TestRPC) is not installed on your machine');
this.logger.info('You can install it by running: npm -g install ganache-cli');
process.exit();
}
cmds.push("-p " + (this.blockchainConfig.rpcPort || options.port || 8545));
cmds.push("-h " + (this.blockchainConfig.rpcHost || options.host || 'localhost'));
cmds.push("-a " + (options.num || 10));
cmds.push("-a " + (options.numAccounts || 10));
cmds.push("-e " + (options.defaultBalance || 100));
cmds.push("-l " + (options.gasLimit || 8000000));
shelljs.exec('testrpc ' + cmds.join(' '), {async : true});
// adding mnemonic only if it is defined in the blockchainConfig or options
let simulatorMnemonic = this.blockchainConfig.simulatorMnemonic || options.simulatorMnemonic;
if (simulatorMnemonic) {
cmds.push("--mnemonic \"" + (simulatorMnemonic) +"\"");
}
// adding blocktime only if it is defined in the blockchainConfig or options
let simulatorBlocktime = this.blockchainConfig.simulatorBlocktime || options.simulatorBlocktime;
if (simulatorBlocktime) {
cmds.push("-b \"" + (simulatorBlocktime) +"\"");
}
const program = ganache ? 'ganache-cli' : 'testrpc';
shelljs.exec(`${program} ${cmds.join(' ')}`, {async : true});
}
}
module.exports = Simulator;

View File

@ -7,7 +7,7 @@ class TemplateGenerator {
}
generate(destinationFolder, name) {
let templatePath = fs.embarkPath(this.templateName);
let templatePath = fs.embarkPath(utils.joinPath('templates', this.templateName));
console.log('Initializing Embark Template....'.green);
let fspath = utils.joinPath(destinationFolder, name);
@ -15,6 +15,11 @@ class TemplateGenerator {
utils.cd(fspath);
utils.sed('package.json', '%APP_NAME%', name);
if (name === 'embark_demo') {
console.log('Installing packages...'.green);
utils.runCmd('npm install');
}
console.log('Init complete'.green);
console.log('\nApp ready at '.green + fspath);
@ -25,7 +30,7 @@ class TemplateGenerator {
console.log('-> '.green + 'embark blockchain'.bold.cyan + ' or '.green + 'embark simulator'.bold.cyan);
console.log('open another console in the same directory and run'.green);
console.log('-> '.green + 'embark run'.bold.cyan);
console.log('For more info go to http://github.com/iurimatias/embark-framework'.green);
console.log('For more info go to http://embark.status.im'.green);
}
}
}

19
lib/constants.json Normal file
View File

@ -0,0 +1,19 @@
{
"httpContractsDirectory": ".embark/contracts/",
"contexts": {
"simulator": "simulator",
"blockchain": "blockchain",
"templateGeneration": "templateGeneration",
"run": "run",
"upload": "upload",
"build": "build",
"graph": "graph",
"test": "test",
"reset": "reset",
"any": "any"
},
"events": {
"contractFilesChanged": "contractFilesChanged",
"contractConfigChanged": "contractConfigChanged"
}
}

View File

@ -1,3 +1,6 @@
let async = require('async');
let fs = require('../core/fs.js');
require('ejs');
const Templates = {
utils: require('./code_templates/utils.js.ejs'),
@ -16,6 +19,8 @@ const Templates = {
class CodeGenerator {
constructor(options) {
this.blockchainConfig = options.blockchainConfig || {};
this.rpcHost = this.blockchainConfig.rpcHost || '';
this.rpcPort = this.blockchainConfig.rpcPort || '';
this.contractsConfig = options.contractsConfig || {};
this.storageConfig = options.storageConfig || {};
this.communicationConfig = options.communicationConfig || {};
@ -27,6 +32,12 @@ class CodeGenerator {
listenToCommands() {
let self = this;
this.events.setCommandHandlerOnce('provider-code', function(cb) {
let providerCode = self.generateProvider(false);
cb(providerCode);
});
// deprecated events; to remove in embark 2.7.0
this.events.setCommandHandlerOnce('abi-vanila', function(cb) {
let vanillaABI = self.generateABI({useEmbarkJS: false});
@ -87,6 +98,13 @@ class CodeGenerator {
}
generateContext() {
let result = "";
result += Templates.main_context();
result += Templates.load_manager();
return result;
}
generateProvider(isDeployment) {
let self = this;
let result = "";
@ -138,7 +156,7 @@ class CodeGenerator {
for (let className in this.contractsManager.contracts) {
let contract = this.contractsManager.contracts[className];
let abi = JSON.stringify(contract.abiDefinition);
result += Templates.vanilla_contract({className: className, abi: abi, contract: contract});
result += Templates.vanilla_contract({className: className, abi: abi, contract: contract, gasLimit: 6000000});
}
return result;
}
@ -168,7 +186,7 @@ class CodeGenerator {
let contractAddress = contract.deployedAddress ? ("'" + contract.deployedAddress + "'") : "undefined";
block += Templates.embarkjs_contract({className: className, abi: abi, contract: contract, contractAddress: contractAddress, gasEstimates: gasEstimates});
} else {
block += Templates.vanilla_contract({className: className, abi: abi, contract: contract});
block += Templates.vanilla_contract({className: className, abi: abi, contract: contract, gasLimit: (isDeployment ? 6000000 : false)});
}
result += Templates.exec_when_ready({block: block});
@ -178,49 +196,46 @@ class CodeGenerator {
return result;
}
generateStorageInitialization(useEmbarkJS) {
let self = this;
let result = "\n";
generateContractCode(contract, gasLimit) {
let abi = JSON.stringify(contract.abiDefinition);
if (!useEmbarkJS || self.storageConfig === {}) return "";
result += Templates.define_when_env_loaded();
if (self.storageConfig.provider === 'ipfs' && self.storageConfig.enabled === true) {
let block = "\nEmbarkJS.Storage.setProvider('" + self.storageConfig.provider + "', {server: '" + self.storageConfig.host + "', port: '" + self.storageConfig.port + "', getUrl: '" + self.storageConfig.getUrl + "'});";
result += Templates.exec_when_env_loaded({block: block});
let block = "";
block += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit});
return block;
}
generateStorageInitialization(useEmbarkJS) {
if (!useEmbarkJS || this.storageConfig === {}) return "";
let result = "\n";
result += Templates.define_when_env_loaded();
result += this._getInitCode('storage', this.storageConfig);
return result;
}
generateCommunicationInitialization(useEmbarkJS) {
let self = this;
if (!useEmbarkJS || this.communicationConfig === {}) return "";
let result = "\n";
if (!useEmbarkJS || self.communicationConfig === {}) return "";
// TODO: don't repeat this twice; should have 'requirements' generator first
result += Templates.define_when_env_loaded();
result += this._getInitCode('communication', this.communicationConfig);
let block;
// TODO: refactor this
if (self.communicationConfig.provider === 'whisper' && self.communicationConfig.enabled === true) {
if (self.communicationConfig.connection === undefined) {
block = "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "');";
} else {
block = "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "', {server: '" + self.communicationConfig.connection.host + "', port: '" + self.communicationConfig.connection.port + "', type: '" + self.communicationConfig.connection.type + "'});";
}
result += Templates.exec_when_env_loaded({block: block});
} else if (self.communicationConfig.provider === 'orbit' && self.communicationConfig.enabled === true) {
if (self.communicationConfig.connection === undefined) {
block = "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "');";
} else {
block = "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "', {server: '" + self.communicationConfig.connection.host + "', port: '" + self.communicationConfig.connection.port + "', type: '" + self.communicationConfig.connection.type + "'});";
}
result += Templates.exec_when_env_loaded({block: block});
return result;
}
_getInitCode(codeType, config) {
let result = "";
let pluginsWithCode = this.plugins.getPluginsFor('initCode');
for (let plugin of pluginsWithCode) {
let initCodes = plugin.embarkjs_init_code[codeType] || [];
for (let initCode of initCodes) {
let [block, shouldInit] = initCode;
if (shouldInit.call(plugin, config)) {
result += Templates.exec_when_env_loaded({block: block});
}
}
}
return result;
}
@ -257,6 +272,52 @@ class CodeGenerator {
return contracts;
}
buildEmbarkJS(cb) {
const self = this;
let embarkjsCode = fs.readFileSync(fs.embarkPath('js/embark.js')).toString();
let code = "";
async.waterfall([
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, fs.embarkPath("js/web3-1.0.min.js"));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
}
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += "\nimport web3 from 'Embark/web3';\n";
next();
},
function getJSCode(next) {
code += "\n" + embarkjsCode + "\n";
let pluginsWithCode = self.plugins.getPluginsFor('embarkjsCode');
for (let plugin of pluginsWithCode) {
code += plugin.embarkjs_code.join('\n');
}
//code += "\n" + fs.readFileSync(fs.embarkPath('js/embarkjs/orbit.js')).toString();
code += self.generateCommunicationInitialization(true);
code += self.generateStorageInitialization(true);
next();
},
function writeFile(next) {
fs.mkdirpSync(fs.dappPath(".embark"));
fs.writeFileSync(fs.dappPath(".embark", 'embark.js'), code);
next();
}
], function(_err, _result) {
cb();
});
}
}
module.exports = CodeGenerator;

View File

@ -1,3 +1,9 @@
__mainContext.web3 = undefined;
web3 = new Web3(new Web3.providers.HttpProvider("<%- url -%>"));
<%- done %>
web3.eth.getAccounts(function(err, accounts) {
if (err) {
return done(new Error(err));
}
web3.defaultAccount = accounts[0];
<%- done %>
});

View File

@ -1,5 +1,5 @@
var whenEnvIsLoaded = function(cb) {
if (typeof document !== 'undefined' && document !== null) {
if (typeof document !== 'undefined' && document !== null && !/comp|inter|loaded/.test(document.readyState)) {
document.addEventListener('DOMContentLoaded', cb);
} else {
cb();

View File

@ -1,3 +1,9 @@
<%- className %>Abi = <%- abi %>;
<%- className %>Contract = web3.eth.contract(<%- className %>Abi);
<%- className %> = <%= className %>Contract.at('<%- contract.deployedAddress %>');
<%- className %> = new web3.eth.Contract(<%- className %>Abi);
<%- className %>.options.address = '<%- contract.deployedAddress %>';
<%- className %>.address = '<%- contract.deployedAddress %>';
<%- className %>.options.from = web3.eth.defaultAccount;
<% if (gasLimit != false) { %>
<%- className %>.options.gas = <%- gasLimit %>;
<%- className %>.options.gasLimit = <%- gasLimit %>;
<% } %>

View File

@ -5,10 +5,13 @@ __reduce(<%- connectionList %>,function(prev, value, next) {
}
if (value === '$WEB3' && (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined')) {
web3 = new Web3(web3.currentProvider);
web3.setProvider(web3.givenProvider);
} else if (value !== '$WEB3' && (typeof Web3 !== 'undefined' && ((typeof web3 === 'undefined') || (typeof web3 !== 'undefined' && (!web3.isConnected || (web3.isConnected && !web3.isConnected())))))) {
web3 = new Web3(new Web3.providers.HttpProvider(value));
if (value.indexOf('ws://') >= 0) {
web3.setProvider(new Web3.providers.WebsocketProvider(value));
} else {
web3.setProvider(new Web3.providers.HttpProvider(value));
}
} else if (value === '$WEB3') {
return next(null, '');
}

View File

@ -1,40 +1,35 @@
/*jshint esversion: 6, loopfunc: true */
let async = require('../utils/async_extend.js');
let SolcW = require('./solcW.js');
class Compiler {
constructor(options) {
this.plugins = options.plugins;
this.logger = options.logger;
this.solcVersion = options.solcVersion;
}
compile_contracts(contractFiles, cb) {
const self = this;
let available_compilers = {};
let available_compilers = {
//".se": this.compile_serpent
".sol": this.compile_solidity.bind(this)
};
if (contractFiles.length === 0) {
return cb(null, {});
}
if (this.plugins) {
let compilerPlugins = this.plugins.getPluginsFor('compilers');
if (compilerPlugins.length > 0) {
compilerPlugins.forEach(function (plugin) {
plugin.compilers.forEach(function (compilerObject) {
let pluginCompilers = self.plugins.getPluginsProperty('compilers', 'compilers');
pluginCompilers.forEach(function (compilerObject) {
available_compilers[compilerObject.extension] = compilerObject.cb;
});
});
}
}
let compiledObject = {};
async.eachObject(available_compilers,
function (extension, compiler, callback) {
// TODO: warn about files it doesn't know how to compile
let matchingFiles = contractFiles.filter(function (file) {
let fileMatch = file.filename.match(/\.[0-9a-z]+$/);
return (fileMatch && (fileMatch[0] === extension));
if (fileMatch && (fileMatch[0] === extension)) {
file.compiled = true;
return true;
}
return false;
});
compiler.call(compiler, matchingFiles || [], function (err, compileResult) {
@ -43,89 +38,16 @@ class Compiler {
});
},
function (err) {
contractFiles.forEach(file => {
if (!file.compiled) {
self.logger.warn(`${file.filename} doesn't have a compatible contract compiler. Maybe a plugin exists for it.`);
}
});
cb(err, compiledObject);
}
);
}
compile_solidity(contractFiles, cb) {
let self = this;
let input = {};
let solcW;
async.waterfall([
function prepareInput(callback) {
async.each(contractFiles,
function(file, fileCb) {
let filename = file.filename.replace('app/contracts/', '');
file.content(function(fileContent) {
input[filename] = fileContent;
fileCb();
});
},
function (err) {
callback(err);
}
);
},
function loadCompiler(callback) {
// TODO: there ino need to load this twice
solcW = new SolcW({logger: self.logger, solcVersion: self.solcVersion});
if (solcW.isCompilerLoaded()) {
return callback();
}
self.logger.info("loading solc compiler..");
solcW.load_compiler(function () {
callback();
});
},
function compileContracts(callback) {
self.logger.info("compiling contracts...");
solcW.compile({sources: input}, 1, function (output) {
if (output.errors) {
for (let i=0; i<output.errors; i++) {
if (output.errors[i].indexOf('Warning:') >= 0) {
return callback(new Error("Solidity errors: " + output.errors).message);
}
}
self.logger.warn(output.errors.join('\n'));
}
callback(null, output);
});
},
function createCompiledObject(output, callback) {
let json = output.contracts;
let compiled_object = {};
for (let contractName in json) {
let contract = json[contractName];
// Pull out filename:classname
// [0] filename:classname
// [1] filename
// [2] classname
const regex = /(.*):(.*)/;
const className = contractName.match(regex)[2];
const filename = contractName.match(regex)[1];
compiled_object[className] = {};
compiled_object[className].code = contract.bytecode;
compiled_object[className].runtimeBytecode = contract.runtimeBytecode;
compiled_object[className].realRuntimeBytecode = contract.runtimeBytecode.slice(0, -68);
compiled_object[className].swarmHash = contract.runtimeBytecode.slice(-68).slice(0, 64);
compiled_object[className].gasEstimates = contract.gasEstimates;
compiled_object[className].functionHashes = contract.functionHashes;
compiled_object[className].abiDefinition = JSON.parse(contract.interface);
compiled_object[className].filename = filename;
}
callback(null, compiled_object);
}
], function (err, result) {
cb(err, result);
});
}
}
module.exports = Compiler;

View File

@ -2,6 +2,8 @@ let toposort = require('toposort');
let async = require('async');
let Compiler = require('./compiler.js');
let utils = require('../utils/utils.js');
const constants = require('../constants');
// TODO: create a contract object
@ -12,20 +14,24 @@ class ContractsManager {
this.contracts = {};
this.logger = options.logger;
this.plugins = options.plugins;
if (!options.contractsConfig.versions) {
this.solcVersion = "0.4.17";
} else {
this.solcVersion = options.contractsConfig.versions.solc;
}
this.contractDependencies = {};
this.gasLimit = options.gasLimit;
this.deployOnlyOnConfig = false;
this.events = options.events;
this.events.on(constants.events.contractFilesChanged, (newContractFiles) => {
this.contractFiles = newContractFiles;
});
this.events.on(constants.events.contractConfigChanged, (newContracts) => {
this.contractsConfig = newContracts;
});
}
build(done) {
let self = this;
async.waterfall([
function compileContracts(callback) {
let compiler = new Compiler({plugins: self.plugins, logger: self.logger, solcVersion: self.solcVersion});
let compiler = new Compiler({plugins: self.plugins, logger: self.logger});
compiler.compile_contracts(self.contractFiles, function (err, compiledObject) {
self.compiledContracts = compiledObject;
callback(err);
@ -43,14 +49,6 @@ class ContractsManager {
}
callback();
},
function setDeployIntention(callback) {
let className, contract;
for (className in self.contracts) {
contract = self.contracts[className];
contract.deploy = (contract.deploy === undefined) || contract.deploy;
}
callback();
},
function prepareContractsFromCompilation(callback) {
let className, compiledContract, contractConfig, contract;
for (className in self.compiledContracts) {
@ -69,7 +67,6 @@ class ContractsManager {
contract.filename = compiledContract.filename;
contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto';
self.adjustGas(contract);
contract.gasPrice = contract.gasPrice || self.contractsConfig.gasPrice;
contract.type = 'file';
@ -79,9 +76,26 @@ class ContractsManager {
}
callback();
},
function setDeployIntention(callback) {
let className, contract;
for (className in self.contracts) {
contract = self.contracts[className];
contract.deploy = (contract.deploy === undefined) || contract.deploy;
if (self.deployOnlyOnConfig && !self.contractsConfig.contracts[className]) {
contract.deploy = false;
}
if (contract.code === "") {
self.logger.info("assuming " + className + " to be an interface");
contract.deploy = false;
}
}
callback();
},
/*eslint complexity: ["error", 11]*/
function dealWithSpecialConfigs(callback) {
let className, contract, parentContractName, parentContract;
let dictionary = Object.keys(self.contracts);
for (className in self.contracts) {
contract = self.contracts[className];
@ -100,6 +114,10 @@ class ContractsManager {
if (parentContract === undefined) {
self.logger.error(className + ": couldn't find instanceOf contract " + parentContractName);
let suggestion = utils.proposeAlternative(parentContractName, dictionary, [className, parentContractName]);
if (suggestion) {
self.logger.warn('did you mean "' + suggestion + '"?');
}
continue;
}
@ -126,17 +144,25 @@ class ContractsManager {
},
function removeContractsWithNoCode(callback) {
let className, contract;
let dictionary = Object.keys(self.contracts);
for (className in self.contracts) {
contract = self.contracts[className];
if (contract.code === undefined) {
self.logger.error(className + " has no code associated");
let suggestion = utils.proposeAlternative(className, dictionary, [className]);
if (suggestion) {
self.logger.warn('did you mean "' + suggestion + '"?');
}
delete self.contracts[className];
}
}
self.logger.trace(self.contracts);
callback();
},
// TODO: needs refactoring, has gotten too complex
/*eslint complexity: ["error", 16]*/
/*eslint max-depth: ["error", 16]*/
function determineDependencies(callback) {
let className, contract;
for (className in self.contracts) {
@ -151,15 +177,41 @@ class ContractsManager {
// look in arguments for dependencies
if (contract.args === []) continue;
let ref = contract.args;
let ref;
if (Array.isArray(contract.args)) {
ref = contract.args;
} else {
let keys = Object.keys(contract.args);
ref = keys.map((k) => contract.args[k]).filter((x) => !x);
}
for (let j = 0; j < ref.length; j++) {
let arg = ref[j];
if (arg[0] === "$") {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(arg.substr(1));
}
if (Array.isArray(arg)) {
for (let sub_arg of arg) {
if (sub_arg[0] === "$") {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(sub_arg.substr(1));
}
}
}
}
// look in onDeploy for dependencies
if (contract.onDeploy === [] || contract.onDeploy === undefined) continue;
let regex = /\$\w+/g;
contract.onDeploy.map((cmd) => {
cmd.replace(regex, (match) => {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(match.substr(1));
});
});
}
callback();
}
], function (err, _result) {
@ -185,7 +237,17 @@ class ContractsManager {
}
}
let orderedDependencies = toposort(converted_dependencies).reverse();
let orderedDependencies;
try {
orderedDependencies = toposort(converted_dependencies.filter((x) => x[0] != x[1])).reverse();
} catch(e) {
this.logger.error(("Error: " + e.message).red);
this.logger.error("there are two or more contracts that depend on each other in a cyclic manner".bold.red);
this.logger.error("Embark couldn't determine which one to deploy first".red);
throw new Error("CyclicDependencyError");
//process.exit(0);
}
let newList = contractList.sort(function (a, b) {
let order_a = orderedDependencies.indexOf(a.className);
@ -223,7 +285,7 @@ class ContractsManager {
} else if (contract.error) {
contractData = [
className.green,
(contract.error).red,
(contract.error).split("\n")[0].replace(/Error: /g, '').substring(0, 32).red,
'\t\tError'.red
];
} else {
@ -239,26 +301,6 @@ class ContractsManager {
return data;
}
adjustGas(contract) {
let maxGas, adjustedGas;
if (contract.gas === 'auto') {
if (contract.deploy || contract.deploy === undefined) {
if (contract.gasEstimates.creation !== undefined) {
// TODO: should sum it instead
maxGas = Math.max(contract.gasEstimates.creation[0], contract.gasEstimates.creation[1], 500000);
} else {
maxGas = 500000;
}
} else {
maxGas = 500000;
}
// TODO: put a check so it doesn't go over the block limit
adjustedGas = Math.round(maxGas * 1.40);
adjustedGas += 25000;
contract.gas = adjustedGas;
}
}
}
module.exports = ContractsManager;

View File

@ -1,4 +1,5 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let RunCode = require('../core/runCode.js');
@ -10,22 +11,55 @@ class Deploy {
this.web3 = options.web3;
this.contractsManager = options.contractsManager;
this.logger = options.logger;
this.events = options.events;
this.env = options.env;
this.deployTracker = new DeployTracker({
logger: options.logger, chainConfig: options.chainConfig, web3: options.web3, env: this.env
});
this.chainConfig = options.chainConfig;
this.plugins = options.plugins;
this.gasLimit = options.gasLimit;
}
determineArguments(suppliedArgs) {
initTracker(cb) {
this.deployTracker = new DeployTracker({
logger: this.logger, chainConfig: this.chainConfig, web3: this.web3, env: this.env
}, cb);
}
determineArguments(suppliedArgs, contract) {
let realArgs = [], l, arg, contractName, referedContract;
for (l = 0; l < suppliedArgs.length; l++) {
arg = suppliedArgs[l];
let args = suppliedArgs;
if (!Array.isArray(args)) {
args = [];
let abi = contract.abiDefinition.find((abi) => abi.type === 'constructor');
for (let input of abi.inputs) {
let inputValue = suppliedArgs[input.name];
if (!inputValue) {
this.logger.error(input.name + " has not been defined for " + contract.className + " constructor");
}
args.push(inputValue || "");
}
}
for (l = 0; l < args.length; l++) {
arg = args[l];
if (arg[0] === "$") {
contractName = arg.substr(1);
referedContract = this.contractsManager.getContract(contractName);
realArgs.push(referedContract.deployedAddress);
} else if (Array.isArray(arg)) {
let subRealArgs = [];
for (let sub_arg of arg) {
if (sub_arg[0] === "$") {
contractName = sub_arg.substr(1);
referedContract = this.contractsManager.getContract(contractName);
subRealArgs.push(referedContract.deployedAddress);
} else {
subRealArgs.push(sub_arg);
}
}
realArgs.push(subRealArgs);
} else {
realArgs.push(arg);
}
@ -40,31 +74,67 @@ class Deploy {
contract.error = false;
if (contract.deploy === false) {
self.logger.contractsState(self.contractsManager.contractsState());
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback();
}
realArgs = self.determineArguments(params || contract.args, contract);
if (contract.address !== undefined) {
realArgs = self.determineArguments(params || contract.args);
try {
this.web3.utils.toChecksumAddress(contract.address);
} catch(e) {
self.logger.error("error deploying " + contract.className);
self.logger.error(e.message);
contract.error = e.message;
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback(e.message);
}
contract.deployedAddress = contract.address;
self.logger.info(contract.className.bold.cyan + " already deployed at ".green + contract.address.bold.cyan);
if (this.deployTracker) {
self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, contract.address);
self.deployTracker.save();
self.logger.contractsState(self.contractsManager.contractsState());
}
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback();
}
let trackedContract = self.deployTracker.getContract(contract.className, contract.realRuntimeBytecode, contract.args);
if (!this.deployTracker) {
return self.contractToDeploy(contract, params, callback);
}
if (trackedContract && this.web3.eth.getCode(trackedContract.address) !== "0x") {
let trackedContract = self.deployTracker.getContract(contract.className, contract.realRuntimeBytecode, realArgs);
if (!trackedContract) {
return self.contractToDeploy(contract, params, callback);
}
this.web3.eth.getCode(trackedContract.address, function(_getCodeErr, codeInChain) {
if (codeInChain !== "0x") {
self.contractAlreadyDeployed(contract, trackedContract, callback);
} else {
self.contractToDeploy(contract, params, callback);
}
});
}
contractAlreadyDeployed(contract, trackedContract, callback) {
const self = this;
self.logger.info(contract.className.bold.cyan + " already deployed at ".green + trackedContract.address.bold.cyan);
contract.deployedAddress = trackedContract.address;
self.logger.contractsState(self.contractsManager.contractsState());
return callback();
} else {
self.events.emit('contractsState', self.contractsManager.contractsState());
realArgs = self.determineArguments(params || contract.args);
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator({contractsManager: self.contractsManager});
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
return callback();
}
contractToDeploy(contract, params, callback) {
const self = this;
let realArgs = self.determineArguments(params || contract.args, contract);
this.deployContract(contract, realArgs, function (err, address) {
if (err) {
@ -72,35 +142,110 @@ class Deploy {
}
self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, address);
self.deployTracker.save();
self.logger.contractsState(self.contractsManager.contractsState());
self.events.emit('contractsState', self.contractsManager.contractsState());
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator({contractsManager: self.contractsManager});
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
if (contract.onDeploy !== undefined) {
self.logger.info('executing onDeploy commands');
let codeGenerator = new CodeGenerator({contractsManager: self.contractsManager});
let code = codeGenerator.generateContracts(false, true, true);
let cmds = contract.onDeploy.join(';\n');
RunCode.doEval(code + "\n" + cmds, self.web3);
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
let withErrors = false;
let regex = /\$\w+/g;
let onDeployCode = contract.onDeploy.map((cmd) => {
let realCmd = cmd.replace(regex, (match) => {
let referedContractName = match.slice(1);
let referedContract = self.contractsManager.getContract(referedContractName);
if (!referedContract) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error(referedContractName + ' does not exist');
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && referedContract.deploy === false) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error(referedContractName + " exists but has been set to not deploy");
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && !referedContract.deployedAddress) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error("couldn't find a valid address for " + referedContractName + ". has it been deployed?");
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
return referedContract.deployedAddress;
});
return realCmd;
});
if (withErrors) {
contract.error = "onDeployCmdError";
return callback(new Error("error running onDeploy"));
}
// TODO: convert to for to avoid repeated callback
for(let cmd of onDeployCode) {
self.logger.info("executing: " + cmd);
try {
RunCode.doEval(cmd, self.web3);
} catch(e) {
if (e.message.indexOf("invalid opcode") >= 0) {
self.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation');
}
return callback(new Error(e));
}
}
}
callback();
});
}
}
deployContract(contract, params, callback) {
let self = this;
let contractObject = this.web3.eth.contract(contract.abiDefinition);
let accounts = [];
let contractParams = (params || contract.args).slice();
let contractCode = contract.code;
let deploymentAccount = self.web3.eth.defaultAccount;
let deployObject;
this.web3.eth.getAccounts(function (err, accounts) {
async.waterfall([
function getAccounts(next) {
self.web3.eth.getAccounts(function (err, _accounts) {
if (err) {
return callback(new Error(err));
return next(new Error(err));
}
accounts = _accounts;
// applying deployer account configuration, if any
if (typeof contract.fromIndex == 'number') {
deploymentAccount = accounts[contract.fromIndex];
if (deploymentAccount === undefined) {
return next("error deploying " + contract.className + ": no account found at index " + contract.fromIndex + " check the config");
}
}
if (typeof contract.from == 'string' && typeof contract.fromIndex != 'undefined') {
self.logger.warn('Both "from" and "fromIndex" are defined for contract "'+contract.className+'". Using "from" as deployer account.');
}
if (typeof contract.from == 'string') {
deploymentAccount = contract.from;
}
let contractCode = contract.code;
deploymentAccount = deploymentAccount || accounts[0];
next();
});
},
function doLinking(next) {
// Applying linked contracts
let contractsList = self.contractsManager.listContracts();
for (let contractObj of contractsList) {
let filename = contractObj.filename;
@ -109,52 +254,107 @@ class Deploy {
deployedAddress = deployedAddress.substr(2);
}
let linkReference = '__' + filename + ":" + contractObj.className;
if (contractCode.indexOf(linkReference) < 0) {
continue;
}
if (linkReference.length > 40) {
return next(new Error(linkReference + " is too long, try reducing the path of the contract (" + filename + ") and/or its name " + contractObj.className));
}
let toReplace = linkReference + "_".repeat(40 - linkReference.length);
if (deployedAddress === undefined) {
let libraryName = contractObj.className;
return next(new Error(contract.className + " needs " + libraryName + " but an address was not found, did you deploy it or configured an address?"));
}
contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
}
// saving code changes back to contract object
contract.code = contractCode;
next();
},
function applyBeforeDeploy(next) {
let beforeDeployPlugins = self.plugins.getPluginsFor('beforeDeploy');
// TODO: probably needs to be defaultAccount
// TODO: it wouldn't necessary be the first address
// use defined blockchain address or first address
contractParams.push({
//from: this.web3.eth.coinbase,
from: accounts[0],
data: "0x" + contractCode,
gas: contract.gas,
gasPrice: contract.gasPrice
//self.logger.info("applying beforeDeploy plugins...", beforeDeployPlugins.length);
async.eachSeries(beforeDeployPlugins, (plugin, eachPluginCb) => {
self.logger.info("running beforeDeploy plugin " + plugin.name + " .");
// calling each beforeDeploy handler declared by the plugin
async.eachSeries(plugin.beforeDeploy, (beforeDeployFn, eachCb) => {
function beforeDeployCb(resObj){
contract.code = resObj.contractCode;
eachCb();
}
beforeDeployFn({
embarkDeploy: self,
pluginConfig: plugin.pluginConfig,
deploymentAccount: deploymentAccount,
contract: contract,
callback: beforeDeployCb
}, beforeDeployCb);
}, () => {
//self.logger.info('All beforeDeploy handlers of the plugin has processed.');
eachPluginCb();
});
}, () => {
//self.logger.info('All beforeDeploy plugins has been processed.');
contractCode = contract.code;
next();
});
},
function createDeployObject(next) {
let contractObject = new self.web3.eth.Contract(contract.abiDefinition);
try {
const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
deployObject = contractObject.deploy({arguments: contractParams, data: dataCode});
} catch(e) {
if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return next(new Error("attempted to deploy " + contract.className + " without specifying parameters"));
} else {
return next(new Error(e));
}
}
next();
},
function estimateCorrectGas(next) {
if (contract.gas === 'auto') {
return deployObject.estimateGas().then((gasValue) => {
contract.gas = gasValue;
next();
}).catch(next);
}
next();
},
function deployTheContract(next) {
self.logger.info("deploying " + contract.className.bold.cyan + " with ".green + contract.gas + " gas".green);
contractParams.push(function (err, transaction) {
self.logger.contractsState(self.contractsManager.contractsState());
if (err) {
self.logger.error("error deploying contract: " + contract.className.cyan);
let errMsg = err.toString();
if (errMsg === 'Error: The contract code couldn\'t be stored, please check your gas amount.') {
errMsg = 'The contract code couldn\'t be stored, out of gas or constructor error';
deployObject.send({
from: deploymentAccount,
gas: contract.gas,
gasPrice: contract.gasPrice
}).on('receipt', function(receipt) {
if (receipt.contractAddress !== undefined) {
self.logger.info(contract.className.bold.cyan + " deployed at ".green + receipt.contractAddress.bold.cyan);
contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash;
self.events.emit('contractsState', self.contractsManager.contractsState());
return next(null, receipt.contractAddress);
}
self.logger.error(errMsg);
contract.error = errMsg;
return callback(new Error(err));
} else if (transaction.address !== undefined) {
self.logger.info(contract.className.bold.cyan + " deployed at ".green + transaction.address.bold.cyan);
contract.deployedAddress = transaction.address;
contract.transactionHash = transaction.transactionHash;
return callback(null, transaction.address);
self.events.emit('contractsState', self.contractsManager.contractsState());
}).on('error', function(error) {
self.events.emit('contractsState', self.contractsManager.contractsState());
return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
});
}
});
contractObject["new"].apply(contractObject, contractParams);
});
], callback);
}
deployAll(done) {
let self = this;
this.logger.info("deploying contracts");
let contracts = this.contractsManager.listContracts();
async.eachOfSeries(this.contractsManager.listContracts(),
async.eachOfSeries(contracts,
function (contract, key, callback) {
self.logger.trace(arguments);
self.checkAndDeployContract(contract, null, callback);
@ -165,9 +365,13 @@ class Deploy {
self.logger.error(err.message);
self.logger.debug(err.stack);
}
if (contracts.length === 0) {
self.logger.info("no contracts found");
return done();
}
self.logger.info("finished deploying contracts");
self.logger.trace(arguments);
done();
done(err);
}
);

View File

@ -1,16 +1,23 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let Deploy = require('./deploy.js');
let ContractsManager = require('./contracts.js');
let RunCode = require('../core/runCode.js');
class DeployManager {
constructor(options) {
this.config = options.config;
this.logger = options.logger;
this.blockchainConfig = this.config.blockchainConfig;
this.events = options.events;
this.plugins = options.plugins;
this.web3 = options.web3;
this.chainConfig = (options.trackContracts !== false) ? this.config.chainTracker : false;
this.contractsManager = options.contractsManager;
this.gasLimit = false;
this.fatalErrors = false;
this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
}
deployContracts(done) {
@ -24,37 +31,40 @@ class DeployManager {
async.waterfall([
function buildContracts(callback) {
let contractsManager = new ContractsManager({
contractFiles: self.config.contractsFiles,
contractsConfig: self.config.contractsConfig,
logger: self.logger,
plugins: self.plugins
});
contractsManager.build(callback);
self.contractsManager.deployOnlyOnConfig = self.deployOnlyOnConfig; // temporary, should refactor
self.contractsManager.build(callback);
},
function checkCompileOnly(contractsManager, callback){
if(self.onlyCompile){
self.events.emit('contractsDeployed', contractsManager);
return done();
}
return callback(null, contractsManager);
},
function checkWeb3IsConnected(contractsManager, callback) {
if (!self.web3) {
return callback(Error("no web3 instance found"));
}
if (self.web3.currentProvider.isConnected !== undefined && !self.web3.isConnected()) {
if (self.web3.currentProvider === undefined) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
return callback(Error("error connecting to blockchain node"));
}
if (self.web3.currentProvider.isConnected === undefined) {
self.web3.version.getNode(function (err, _version) {
self.web3.eth.getAccounts(function(err, _accounts) {
if (err) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
return callback(Error("error connecting to blockchain node"));
}
return callback(null, contractsManager, self.web3);
});
} else {
return callback(null, contractsManager, self.web3);
}
},
function setDefaultAccount(contractsManager, web3, callback) {
web3.eth.getAccounts(function (err, accounts) {
if (err) {
self.logger.error(err);
return callback(new Error(err));
}
let accountConfig = self.config.blockchainConfig.account;
@ -68,13 +78,75 @@ class DeployManager {
web3: web3,
contractsManager: contractsManager,
logger: self.logger,
events: self.events,
chainConfig: self.chainConfig,
env: self.config.env
env: self.config.env,
plugins: self.plugins,
gasLimit: self.gasLimit
});
deploy.deployAll(function () {
deploy.initTracker(function() {
deploy.deployAll(function (err) {
if (!err) {
self.events.emit('contractsDeployed', contractsManager);
callback(null, contractsManager);
}
if (err && self.fatalErrors) {
return callback(err);
}
callback(null, contractsManager, web3);
});
});
},
function runAfterDeployCommands(contractsManager, web3, callback) {
let afterDeployCmds = self.config.contractsConfig.afterDeploy || [];
let withErrors = false;
let regex = /\$\w+/g;
let onDeployCode = afterDeployCmds.map((cmd) => {
let realCmd = cmd.replace(regex, (match) => {
let referedContractName = match.slice(1);
let referedContract = contractsManager.getContract(referedContractName);
if (!referedContract) {
self.logger.error(referedContractName + ' does not exist');
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && referedContract.deploy === false) {
self.logger.error(referedContractName + " exists but has been set to not deploy");
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && !referedContract.deployedAddress) {
self.logger.error("couldn't find a valid address for " + referedContractName + ". has it been deployed?");
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
return referedContract.deployedAddress;
});
return realCmd;
});
if (withErrors) {
return callback(new Error("error running afterDeploy"));
}
// TODO: convert to for to avoid repeated callback
for(let cmd of onDeployCode) {
self.logger.info("executing: " + cmd);
try {
RunCode.doEval(cmd, web3);
} catch(e) {
if (e.message.indexOf("invalid opcode") >= 0) {
self.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation');
}
return callback(new Error(e));
}
}
callback(null, contractsManager);
}
], function (err, result) {
if (err) {

View File

@ -1,7 +1,8 @@
let fs = require('../core/fs.js');
class DeployTracker {
constructor(options) {
constructor(options, cb) {
const self = this;
this.logger = options.logger;
this.env = options.env;
this.chainConfig = options.chainConfig;
@ -9,20 +10,22 @@ class DeployTracker {
if (this.chainConfig === false) {
this.currentChain = {contracts: []};
return;
return cb();
}
// TODO: need to make this async
let block = this.web3.eth.getBlock(0);
this.web3.eth.getBlock(0, function(err, block) {
let chainId = block.hash;
if (this.chainConfig[chainId] === undefined) {
this.chainConfig[chainId] = {contracts: {}};
if (self.chainConfig[chainId] === undefined) {
self.chainConfig[chainId] = {contracts: {}};
}
this.currentChain = this.chainConfig[chainId];
self.currentChain = self.chainConfig[chainId];
self.currentChain.name = self.env;
cb();
});
this.currentChain.name = this.env;
// TODO: add other params
//this.currentChain.networkId = "";
//this.currentChain.networkType = "custom"
@ -34,14 +37,14 @@ class DeployTracker {
}
trackContract(contractName, code, args, address) {
this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))] = {
this.currentChain.contracts[this.web3.utils.sha3(code + contractName + args.join(','))] = {
name: contractName,
address: address
};
}
getContract(contractName, code, args) {
let contract = this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))];
let contract = this.currentChain.contracts[this.web3.utils.sha3(code + contractName + args.join(','))];
if (contract && contract.address === undefined) {
return false;
}
@ -59,4 +62,3 @@ class DeployTracker {
}
module.exports = DeployTracker;

View File

@ -1,18 +0,0 @@
let solc;
process.on('message', function (msg) {
if (msg.action === 'loadCompiler') {
solc = require(msg.solcLocation);
process.send({result: "loadedCompiler"});
}
if (msg.action === 'compile') {
let output = solc.compile(msg.obj, msg.optimize);
process.send({result: "compilation", output: output});
}
});
process.on('exit', function () {
process.exit(0);
});

View File

@ -1,56 +0,0 @@
let utils = require('../utils/utils.js');
let solcProcess;
let compilerLoaded = false;
var Npm = require('../pipeline/npm.js');
let path = require('path');
let currentSolcVersion = require('../../package.json').dependencies.solc;
class SolcW {
constructor(options) {
this.solcVersion = options.solcVersion;
this.logger = options.logger;
}
load_compiler(done) {
if (compilerLoaded) {
done();
}
solcProcess = require('child_process').fork(utils.joinPath(__dirname, '/solcP.js'));
solcProcess.once('message', function (msg) {
if (msg.result !== 'loadedCompiler') {
return;
}
compilerLoaded = true;
done();
});
if (this.solcVersion === currentSolcVersion) {
solcProcess.send({action: 'loadCompiler', solcLocation: 'solc'});
} else {
let npm = new Npm({logger: this.logger});
npm.getPackageVersion('solc', this.solcVersion, false, false, function(location) {
let requirePath = path.join(process.env.PWD, location);
solcProcess.send({action: 'loadCompiler', solcLocation: requirePath});
});
}
}
isCompilerLoaded() {
return (compilerLoaded === true);
}
compile(obj, optimize, done) {
solcProcess.once('message', function (msg) {
if (msg.result !== 'compilation') {
return;
}
done(msg.output);
});
solcProcess.send({action: 'compile', obj: obj, optimize: optimize});
}
}
module.exports = SolcW;

View File

@ -1,12 +1,11 @@
var fs = require('./fs.js');
var File = require('./file.js');
var Plugins = require('./plugins.js');
var utils = require('../utils/utils.js');
var Npm = require('../pipeline/npm.js');
let currentWeb3Version = require('../../package.json').dependencies.web3.replace("^","");
const fs = require('./fs.js');
const File = require('./file.js');
const Plugins = require('./plugins.js');
const utils = require('../utils/utils.js');
const path = require('path');
const deepEqual = require('deep-equal');
const constants = require('../constants');
// TODO: add wrapper for fs so it can also work in the browser
// can work with both read and save
var Config = function(options) {
this.env = options.env;
this.blockchainConfig = {};
@ -21,6 +20,8 @@ var Config = function(options) {
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
this.embarkConfig = {};
this.context = options.context || [constants.contexts.any];
};
Config.prototype.loadConfigFiles = function(options) {
@ -29,9 +30,7 @@ Config.prototype.loadConfigFiles = function(options) {
interceptLogs = true;
}
//Check if the config file exists
var embarkConfigExists = fs.existsSync(options.embarkConfig);
if(!embarkConfigExists){
if (!fs.existsSync(options.embarkConfig)){
this.logger.error('Cannot find file ' + options.embarkConfig + '. Please ensure you are running this command inside the Dapp folder');
process.exit(1);
}
@ -39,7 +38,7 @@ Config.prototype.loadConfigFiles = function(options) {
this.embarkConfig = fs.readJSONSync(options.embarkConfig);
this.embarkConfig.plugins = this.embarkConfig.plugins || {};
this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this});
this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context});
this.plugins.loadPlugins();
this.loadEmbarkConfigFile();
@ -50,6 +49,8 @@ Config.prototype.loadConfigFiles = function(options) {
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
this.loadContractsConfigFile();
this.loadExternalContractsFiles();
this.loadWebServerConfigFile();
this.loadChainTrackerFile();
this.loadPluginContractFiles();
@ -62,97 +63,139 @@ Config.prototype.reloadConfig = function() {
this.loadCommunicationConfigFile();
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
this.loadContractsConfigFile();
this.loadExternalContractsFiles();
this.loadChainTrackerFile();
};
Config.prototype.loadBlockchainConfigFile = function() {
var defaultBlockchainConfig = fs.readJSONSync(this.configDir + "blockchain.json");
this.blockchainConfig = defaultBlockchainConfig[this.env] || {};
Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, enabledByDefault) {
if (!configFilePath) {
let configToReturn = defaultConfig['default'] || {};
configToReturn.enabled = enabledByDefault || false;
return configToReturn;
}
if (this.blockchainConfig.enabled === undefined) {
this.blockchainConfig.enabled = true;
if (!fs.existsSync(configFilePath)) {
// TODO: remove this if
if (this.logger) {
this.logger.warn("no config file found at " + configFilePath + ". using default config");
}
return defaultConfig['default'] || {};
}
let config = fs.readJSONSync(configFilePath);
let configObject = utils.recursiveMerge(defaultConfig, config);
if (env) {
return utils.recursiveMerge(configObject['default'] || {}, configObject[env]);
} else {
return configObject;
}
};
Config.prototype._getFileOrOject = function(object, filePath, property) {
if (typeof (this.configDir) === 'object') {
return this.configDir[property];
}
return this.configDir + filePath;
};
Config.prototype.loadBlockchainConfigFile = function() {
var configObject = {
"default": {
"enabled": true,
"rpcCorsDomain": "auto",
"wsOrigins": "auto"
}
};
let configFilePath = this._getFileOrOject(this.configDir, 'blockchain.json', 'blockchain');
this.blockchainConfig = this._mergeConfig(configFilePath, configObject, this.env, true);
};
Config.prototype.loadContractsConfigFile = function() {
var defaultVersions = {
"web3": "1.0.0-beta",
"solc": "0.4.17"
};
var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {});
var configObject = {
"versions": {
"web3.js": "0.19.1",
"solc": "0.4.17"
},
"default": {
"versions": versions,
"deployment": {
"host": "localhost",
"port": 8545,
"type": "rpc"
"host": "localhost", "port": 8545, "type": "rpc"
},
"dappConnection": [
"$WEB3",
"localhost:8545"
]
],
"gas": "auto",
"contracts": {
}
}
};
var configPlugins = this.plugins.getPluginsFor('contractsConfig');
if (configPlugins.length > 0) {
configPlugins.forEach(function(plugin) {
plugin.contractsConfigs.forEach(function(pluginConfig) {
var contractsConfigs = this.plugins.getPluginsProperty('contractsConfig', 'contractsConfigs');
contractsConfigs.forEach(function(pluginConfig) {
configObject = utils.recursiveMerge(configObject, pluginConfig);
});
});
let configFilePath = this._getFileOrOject(this.configDir, 'contracts.json', 'contracts');
const newContractsConfig = this._mergeConfig(configFilePath, configObject, this.env);
if (!deepEqual(newContractsConfig, this.contractsConfig)) {
this.events.emit(constants.events.contractConfigChanged, newContractsConfig);
this.contractsConfig = newContractsConfig;
}
var contractsConfig = fs.readJSONSync(this.configDir + "contracts.json");
configObject = utils.recursiveMerge(configObject, contractsConfig);
var defaultContractsConfig = configObject['default'];
var envContractsConfig = configObject[this.env];
var mergedConfig = utils.recursiveMerge(defaultContractsConfig, envContractsConfig);
this.contractsConfig = mergedConfig;
};
Config.prototype.loadExternalContractsFiles = function() {
let contracts = this.contractsConfig.contracts;
for (let contractName in contracts) {
let contract = contracts[contractName];
if (!contract.file) {
continue;
}
if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
const fileObj = utils.getExternalContractUrl(contract.file);
if (!fileObj) {
return this.logger.error("HTTP contract file not found: " + contract.file);
}
const localFile = fileObj.filePath;
this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
} else if (fs.existsSync(contract.file)) {
this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file}));
} else if (fs.existsSync(path.join('./node_modules/', contract.file))) {
this.contractsFiles.push(new File({filename: path.join('./node_modules/', contract.file), type: File.types.dapp_file, basedir: '', path: path.join('./node_modules/', contract.file)}));
} else {
this.logger.error("contract file not found: " + contract.file);
}
}
};
Config.prototype.loadStorageConfigFile = function() {
var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {});
var configObject = {
"default": {
"versions": versions,
"enabled": true,
"available_providers": ["ipfs"],
"available_providers": ["ipfs", "swarm"],
"ipfs_bin": "ipfs",
"provider": "ipfs",
"protocol": "http",
"host": "localhost",
"port": 5001,
"getUrl": "http://localhost:8080/ipfs/"
},
"development": {
}
};
//var configPlugins = this.plugins.getPluginsFor('storageConfig');
//if (configPlugins.length > 0) {
// configPlugins.forEach(function(plugin) {
// plugin.contractsConfigs.forEach(function(pluginConfig) {
// configObject = utils.recursiveMerge(configObject, pluginConfig);
// });
// });
//}
let configFilePath = this._getFileOrOject(this.configDir, 'storage.json', 'storage');
var storageConfig;
if (fs.existsSync(this.configDir + "storage.json")) {
storageConfig = fs.readJSONSync(this.configDir + "storage.json");
configObject = utils.recursiveMerge(configObject, storageConfig);
}
var defaultStorageConfig = configObject['default'];
var envStorageConfig = configObject[this.env];
var mergedConfig = utils.recursiveMerge(defaultStorageConfig, envStorageConfig);
this.storageConfig = mergedConfig || {};
if (this.storageConfig.enabled === undefined) {
this.storageConfig.enabled = true;
}
if (this.storageConfig.available_providers === undefined) {
this.storageConfig.available_providers = [];
}
this.storageConfig = this._mergeConfig(configFilePath, configObject, this.env);
};
Config.prototype.loadCommunicationConfigFile = function() {
@ -162,62 +205,40 @@ Config.prototype.loadCommunicationConfigFile = function() {
"provider": "whisper",
"available_providers": ["whisper", "orbit"],
"connection": {
"host": "localhost",
"port": 8546,
"type": "ws"
"host": "localhost", "port": 8546, "type": "ws"
}
}
};
//var configPlugins = this.plugins.getPluginsFor('communicationConfig');
//if (configPlugins.length > 0) {
// configPlugins.forEach(function(plugin) {
// plugin.contractsConfigs.forEach(function(pluginConfig) {
// configObject = utils.recursiveMerge(configObject, pluginConfig);
// });
// });
//}
let configFilePath = this._getFileOrOject(this.configDir, 'communication.json', 'communication');
var communicationConfig;
if (fs.existsSync(this.configDir + "communication.json")) {
communicationConfig = fs.readJSONSync(this.configDir + "communication.json");
configObject = utils.recursiveMerge(configObject, communicationConfig);
}
var defaultCommunicationConfig = configObject['default'];
var envCommunicationConfig = configObject[this.env];
var mergedConfig = utils.recursiveMerge(defaultCommunicationConfig, envCommunicationConfig);
this.communicationConfig = mergedConfig || {};
// TODO: probably not necessary if the default object is done right
if (this.communicationConfig.enabled === undefined) {
this.communicationConfig.enabled = true;
}
if (this.communicationConfig.available_providers === undefined) {
this.communicationConfig.available_providers = [];
}
this.communicationConfig = this._mergeConfig(configFilePath, configObject, this.env);
};
Config.prototype.loadWebServerConfigFile = function() {
var webServerConfigJSON;
if (fs.existsSync(this.configDir + "webserver.json")) {
webServerConfigJSON = fs.readJSONSync(this.configDir + "webserver.json");
} else {
webServerConfigJSON = {};
}
var defaultWebConfig = {
"enabled": true,
"host": "localhost",
"port": 8000
var configObject = {
"enabled": true, "host": "localhost", "port": 8000
};
this.webServerConfig = utils.recursiveMerge(defaultWebConfig, webServerConfigJSON);
let configFilePath = this._getFileOrOject(this.configDir, 'webserver.json', 'webserver');
this.webServerConfig = this._mergeConfig(configFilePath, configObject, false);
};
Config.prototype.loadEmbarkConfigFile = function() {
var contracts = this.embarkConfig.contracts;
this.contractsFiles = this.loadFiles(contracts);
const contracts = this.embarkConfig.contracts;
const newContractsFiles = this.loadFiles(contracts);
if (!this.contractFiles || newContractsFiles.length !== this.contractFiles.length || !deepEqual(newContractsFiles, this.contractFiles)) {
this.events.emit(constants.events.contractFilesChanged, newContractsFiles);
this.contractsFiles = newContractsFiles;
}
// determine contract 'root' directories
this.contractDirectories = contracts.map((dir) => {
return dir.split("**")[0];
}).map((dir) => {
return dir.split("*.")[0];
});
this.contractDirectories.push(constants.httpContractsDirectory);
this.buildDir = this.embarkConfig.buildDir;
this.configDir = this.embarkConfig.config;
@ -231,76 +252,40 @@ Config.prototype.loadPipelineConfigFile = function() {
};
Config.prototype.loadChainTrackerFile = function() {
//var self = this;
var chainTracker;
try {
chainTracker = fs.readJSONSync(this.chainsFile);
}
catch(err) {
//self.logger.info(this.chainsFile + ' file not found, creating it...');
chainTracker = {};
if (!fs.existsSync(this.chainsFile)) {
this.logger.info(this.chainsFile + ' file not found, creating it...');
fs.writeJSONSync(this.chainsFile, {});
}
this.chainTracker = chainTracker;
this.chainTracker = fs.readJSONSync(this.chainsFile);
};
function findMatchingExpression(filename, filesExpressions) {
for (let fileExpression of filesExpressions) {
var matchingFiles = utils.filesMatchingPattern(fileExpression);
for (let matchFile of matchingFiles) {
if (matchFile === filename) {
return path.dirname(fileExpression).replace(/\*/g, '');
}
}
}
return path.dirname(filename);
}
Config.prototype.loadFiles = function(files) {
var self = this;
var originalFiles = utils.filesMatchingPattern(files);
var readFiles = [];
// get embark.js object first
originalFiles.filter(function(file) {
return (file[0] === '$' || file.indexOf('.') >= 0);
}).filter(function(file) {
if (file === 'embark.js') {
if (self.blockchainConfig.enabled || self.communicationConfig.provider === 'whisper' || self.communicationConfig.available_providers.indexOf('whisper') >= 0) {
let web3Version = self.contractsConfig.versions["web3.js"];
if (web3Version && web3Version != currentWeb3Version) {
//if (false) {
//readFiles.push(new File({filename: 'web3-' + web3Version + '.js', type: 'custom', resolver: function(callback) {
readFiles.push(new File({filename: 'web3.js', type: 'custom', resolver: function(callback) {
if (web3Version === "1.0.0-beta") {
return callback(fs.readFileSync(fs.embarkPath('js/web3-1.0.min.js')).toString());
} else {
let npm = new Npm({logger: self.logger});
npm.getPackageVersion('web3', web3Version, 'dist/web3.min.js', true, function(web3Content) {
callback(web3Content);
});
}
}}));
} else {
readFiles.push(new File({filename: 'web3.js', type: 'embark_internal', path: "js/web3.js"}));
}
}
if (self.storageConfig.enabled && (self.storageConfig.provider === 'ipfs' || self.storageConfig.available_providers.indexOf('ipfs') >= 0)) {
readFiles.push(new File({filename: 'ipfs.js', type: 'embark_internal', path: "js/ipfs.js"}));
}
if (self.communicationConfig.enabled && (self.communicationConfig.provider === 'orbit' || self.communicationConfig.available_providers.indexOf('orbit') >= 0)) {
// TODO: remove duplicated files if functionality is the same for storage and orbit
readFiles.push(new File({filename: 'ipfs-api.js', type: 'embark_internal', path: "js/ipfs-api.min.js"}));
readFiles.push(new File({filename: 'orbit.js', type: 'embark_internal', path: "js/orbit.min.js"}));
}
readFiles.push(new File({filename: 'embark.js', type: 'embark_internal', path: "js/build/embark.bundle.js"}));
}
if (file === '$EMBARK_JS') {
readFiles.push(new File({filename: '$EMBARK_JS', type: 'embark_internal', path: "js/build/embark.bundle.js"}));
}
//if (file === "web3.js") {
// readFiles.push(new File({filename: 'web3.js', type: 'embark_internal', path: "js/web3.js"));
//}
let basedir = findMatchingExpression(file, files);
readFiles.push(new File({filename: file, type: File.types.dapp_file, basedir: basedir, path: file}));
});
// get plugins
var filesFromPlugins = [];
var filePlugins = self.plugins.getPluginsFor('pipelineFiles');
if (filePlugins.length > 0) {
filePlugins.forEach(function(plugin) {
try {
var fileObjects = plugin.runFilePipeline();
@ -313,33 +298,12 @@ Config.prototype.loadFiles = function(files) {
self.logger.error(err.message);
}
});
}
filesFromPlugins.filter(function(file) {
if (utils.fileMatchesPattern(files, file.intendedPath)) {
if ((file.intendedPath && utils.fileMatchesPattern(files, file.intendedPath)) || utils.fileMatchesPattern(files, file.file)) {
readFiles.push(file);
}
});
// get user files
originalFiles.filter(function(file) {
if (file === 'embark.js') {
return;
} else if (file.indexOf("web3-") === 0) {
return;
} else if (file.indexOf("web3") === 0) {
return;
//} else if (file === 'abi.js') {
// readFiles.push({filename: file, content: "", path: file});
} else {
if (file.indexOf('.') >= 0) {
readFiles.push(new File({filename: file, type: "dapp_file", path: file}));
} else if (file[0] === '$') {
//readFiles.push(new File({filename: file, type: 'embark_internal', path: file}));
}
}
});
return readFiles;
};
@ -347,17 +311,14 @@ Config.prototype.loadPluginContractFiles = function() {
var self = this;
var contractsPlugins = this.plugins.getPluginsFor('contractFiles');
if (contractsPlugins.length > 0) {
contractsPlugins.forEach(function(plugin) {
plugin.contractsFiles.forEach(function(file) {
var filename = file.replace('./','');
//self.contractsFiles.push({filename: filename, content: plugin.loadPluginFile(file), path: plugin.pathToFile(file)});
self.contractsFiles.push(new File({filename: filename, type: 'custom', resolver: function(callback) {
self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
callback(plugin.loadPluginFile(file));
}}));
});
});
}
};
module.exports = Config;

View File

@ -1,14 +1,14 @@
let Web3 = require('web3');
let utils = require('../utils/utils.js');
let Events = require('./events.js');
let Logger = require('./logger.js');
let Config = require('./config.js');
let ContractsManager = require('../contracts/contracts.js');
let DeployManager = require('../contracts/deploy_manager.js');
let CodeGenerator = require('../contracts/code_generator.js');
let ServicesMonitor = require('./services_monitor.js');
let Pipeline = require('../pipeline/pipeline.js');
let Server = require('../pipeline/server.js');
let Watch = require('../pipeline/watch.js');
let LibraryManager = require('../versions/library_manager.js');
class Engine {
constructor(options) {
@ -16,14 +16,18 @@ class Engine {
this.embarkConfig = options.embarkConfig;
this.interceptLogs = options.interceptLogs;
this.version = options.version;
this.logFile = options.logFile;
this.logLevel = options.logLevel;
this.events = options.events;
this.context = options.context;
}
init(_options) {
let self = this;
let options = _options || {};
this.events = new Events();
this.logger = options.logger || new Logger({logLevel: options.logLevel || 'debug'});
this.config = new Config({env: this.env, logger: this.logger, events: this.events});
this.events = options.events || this.events || new Events();
this.logger = options.logger || new Logger({logLevel: options.logLevel || this.logLevel || 'debug', events: this.events, logFile: this.logFile});
this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context});
this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs});
this.plugins = this.config.plugins;
@ -31,11 +35,61 @@ class Engine {
this.servicesMonitor.addCheck('embarkVersion', function (cb) {
return cb({name: 'Embark ' + self.version, status: 'on'});
}, 0);
if (this.interceptLogs || this.interceptLogs === undefined) {
this.doInterceptLogs();
}
}
doInterceptLogs() {
var self = this;
let context = {};
context.console = console;
let normalizeInput = function(input) {
let args = Object.values(input);
if (args.length === 0) {
return "";
}
if (args.length === 1) {
if (Array.isArray(args[0])) { return args[0].join(','); }
return args[0] || "";
}
return ('[' + args.map((x) => {
if (x === null) { return "null"; }
if (x === undefined) { return "undefined"; }
if (Array.isArray(x)) { return x.join(','); }
return x;
}).toString() + ']');
};
context.console.log = function() {
self.logger.info(normalizeInput(arguments));
};
context.console.warn = function() {
self.logger.warn(normalizeInput(arguments));
};
context.console.info = function() {
self.logger.info(normalizeInput(arguments));
};
context.console.debug = function() {
// TODO: ue JSON.stringify
self.logger.debug(normalizeInput(arguments));
};
context.console.trace = function() {
self.logger.trace(normalizeInput(arguments));
};
context.console.dir = function() {
self.logger.dir(normalizeInput(arguments));
};
}
startMonitor() {
let self = this;
if (this.plugins) {
// --------
// TODO: this only works for services done on startup
// --------
let servicePlugins = this.plugins.getPluginsFor('serviceChecks');
servicePlugins.forEach(function (plugin) {
plugin.serviceChecks.forEach(function (pluginCheck) {
@ -46,6 +100,10 @@ class Engine {
this.servicesMonitor.startMonitor();
}
registerModule(moduleName, options) {
this.plugins.loadInternalPlugin(moduleName, options);
}
startService(serviceName, _options) {
let options = _options || {};
@ -56,7 +114,9 @@ class Engine {
"fileWatcher": this.fileWatchService,
"webServer": this.webServerService,
"ipfs": this.ipfsService,
"web3": this.web3Service
"web3": this.web3Service,
"libraryManager": this.libraryManagerService,
"swarm": this.swarmService
};
let service = services[serviceName];
@ -72,11 +132,12 @@ class Engine {
pipelineService(_options) {
let self = this;
this.logger.setStatus("Building Assets");
this.events.emit("status", "Building Assets");
let pipeline = new Pipeline({
buildDir: this.config.buildDir,
contractsFiles: this.config.contractsFiles,
assetFiles: this.config.assetFiles,
events: this.events,
logger: this.logger,
plugins: this.plugins
});
@ -85,23 +146,18 @@ class Engine {
self.currentAbi = abi;
self.contractsJSON = contractsJSON;
pipeline.build(abi, contractsJSON, null, function() {
if (self.watch) {
self.watch.restart(); // Necessary because changing a file while it is writing can stop it from being watched
}
self.events.emit('outputDone');
});
});
});
// TODO: still need to redeploy contracts because the original contracts
// config is being corrupted
//this.events.on('file-event', function(fileType, path) {
// if (fileType === 'asset') {
// self.config.reloadConfig();
// pipeline.build(self.abi, self.contractsJSON, path);
// self.events.emit('outputDone');
// }
//});
}
codeGeneratorService(_options) {
let self = this;
let generateCode = function (contractsManager) {
let codeGenerator = new CodeGenerator({
blockchainConfig: self.config.blockchainConfig,
@ -113,104 +169,87 @@ class Engine {
events: self.events
});
codeGenerator.listenToCommands();
codeGenerator.buildEmbarkJS(function() {
self.events.emit('code-generator-ready');
});
};
this.events.on('contractsDeployed', generateCode);
this.events.on('blockchainDisabled', generateCode);
this.events.on('asset-changed', generateCode);
}
deploymentService(options) {
let self = this;
this.registerModule('solidity', {
contractDirectories: self.config.contractDirectories
});
this.registerModule('vyper', {
contractDirectories: self.config.contractDirectories
});
this.contractsManager = new ContractsManager({
contractFiles: this.config.contractsFiles,
contractsConfig: this.config.contractsConfig,
logger: this.logger,
plugins: this.plugins,
gasLimit: false,
events: this.events
});
this.deployManager = new DeployManager({
web3: options.web3 || self.web3,
trackContracts: options.trackContracts,
config: this.config,
logger: this.logger,
plugins: this.plugins,
events: this.events
events: this.events,
contractsManager: this.contractsManager,
onlyCompile: options.onlyCompile
});
this.events.on('file-event', function (fileType, _path) {
this.events.on('file-event', function (fileType) {
// TODO: still need to redeploy contracts because the original contracts
// config is being corrupted
if (fileType === 'asset') {
self.events.emit('asset-changed', self.contractsManager);
}
// TODO: for now need to deploy on asset chanes as well
// because the contractsManager config is corrupted after a deploy
//if (fileType === 'contract' || fileType === 'config') {
if (fileType === 'contract' || fileType === 'config') {
self.config.reloadConfig();
self.deployManager.deployContracts(function () {
});
//}
}
});
}
fileWatchService(_options) {
this.logger.setStatus("Watching for changes");
let watch = new Watch({logger: this.logger, events: this.events});
watch.start();
this.events.emit("status", "Watching for changes");
this.watch = new Watch({logger: this.logger, events: this.events});
this.watch.start();
}
webServerService(options) {
let self = this;
let webServerConfig = this.config.webServerConfig;
if (!webServerConfig.enabled) {
return;
}
let host = options.host || webServerConfig.host;
let port = options.port || webServerConfig.port;
this.logger.setStatus("Starting Server");
let server = new Server({
logger: this.logger,
host: host,
port: port
});
self.servicesMonitor.addCheck('Webserver', function (cb) {
let devServer = 'Webserver (http://' + host + ':' + port + ')';
return cb({name: devServer, status: 'on'});
});
server.start(function () {
webServerService() {
this.registerModule('webserver', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor)
});
}
ipfsService(_options) {
let self = this;
self.servicesMonitor.addCheck('IPFS', function (cb) {
utils.checkIsAvailable('http://localhost:5001', function (available) {
if (available) {
//Ideally this method should be in an IPFS API JSONRPC wrapper
//The URL should also be flexible to accept non-default IPFS url
self.logger.trace("Checking IPFS version...");
utils.httpGet('http://localhost:5001/api/v0/version', function (res) {
let body = '';
res.on('data', function (d) {
body += d;
});
res.on('end', function () {
try {
let parsed = JSON.parse(body);
if (parsed.Version) {
return cb({name: ("IPFS " + parsed.Version), status: 'on'});
}
else {
return cb({name: "IPFS ", status: 'on'});
}
}
catch (e) {
return cb({name: "IPFS ", status: 'off'});
}
});
res.on('error', function (err) {
self.logger.trace("Check IPFS version error: " + err);
return cb({name: "IPFS ", status: 'off'});
});
this.registerModule('ipfs', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
storageConfig: this.config.storageConfig,
host: _options.host,
port: _options.port
});
}
else {
return cb({name: "IPFS ", status: 'off'});
}
});
swarmService(_options) {
this.registerModule('swarm', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
storageConfig: this.config.storageConfig,
bzz: _options.bzz
});
}
@ -228,28 +267,46 @@ class Engine {
}
self.servicesMonitor.addCheck('Ethereum', function (cb) {
if (self.web3.isConnected()) {
return cb({
name: (self.web3.version.node.split("/")[0] + " " + self.web3.version.node.split("/")[1].split("-")[0] + " (Ethereum)"),
status: 'on'
});
} else {
if (self.web3.currentProvider === undefined) {
return cb({name: "No Blockchain node found", status: 'off'});
}
self.web3.eth.getAccounts(function(err, _accounts) {
if (err) {
return cb({name: "No Blockchain node found", status: 'off'});
}
// TODO: web3_clientVersion method is currently not implemented in web3.js 1.0
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, version) => {
if (err) {
return cb({name: "Ethereum node (version unknown)", status: 'on'});
}
if (version.indexOf("/") < 0) {
return cb({name: version, status: 'on'});
}
let nodeName = version.split("/")[0];
let versionNumber = version.split("/")[1].split("-")[0];
let name = nodeName + " " + versionNumber + " (Ethereum)";
return cb({name: name, status: 'on'});
});
});
});
self.servicesMonitor.addCheck('Whisper', function (cb) {
self.web3.version.getWhisper(function (err, version) {
if (err) {
return cb({name: 'Whisper', status: 'off'});
} else if (version >= 5) {
return cb({name: 'Whisper (version ' + version + ') - unsupported', status: 'warn'});
} else {
return cb({name: 'Whisper (version ' + version + ')', status: 'on'});
}
});
this.registerModule('whisper', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
communicationConfig: this.config.communicationConfig,
web3: this.web3
});
}
libraryManagerService(_options) {
this.libraryManager = new LibraryManager({
plugins: this.plugins,
config: this.config
});
}
}
module.exports = Engine;

View File

@ -29,23 +29,26 @@ EventEmitter.prototype.setHandler = function(requestName, cb) {
return _setHandler.call(this, requestName, cb);
};
EventEmitter.prototype.request = function(requestName, cb) {
EventEmitter.prototype.request = function() {
let requestName = arguments[0];
let other_args = [].slice.call(arguments, 1);
log("requesting: ", requestName);
warnIfLegacy(requestName);
return this.emit('request:' + requestName, cb);
return this.emit('request:' + requestName, ...other_args);
};
EventEmitter.prototype.setCommandHandler = function(requestName, cb) {
log("setting command handler for: ", requestName);
return this.on('request:' + requestName, function(_cb) {
cb.call(this, _cb);
cb.call(this, ...arguments);
});
};
EventEmitter.prototype.setCommandHandlerOnce = function(requestName, cb) {
log("setting command handler for: ", requestName);
return this.once('request:' + requestName, function(_cb) {
cb.call(this, _cb);
cb.call(this, ...arguments);
});
};

View File

@ -1,26 +1,141 @@
let fs = require('./fs.js');
const async = require('async');
const fs = require('./fs.js');
const path = require('path');
const request = require('request');
const utils = require('../utils/utils');
class File {
constructor(options) {
this.filename = options.filename;
constructor (options) {
this.filename = options.filename.replace(/\\/g, '/');
this.type = options.type;
this.path = options.path;
this.basedir = options.basedir;
this.resolver = options.resolver;
}
content(callback) {
if (this.type === 'embark_internal') {
return callback(fs.readFileSync(fs.embarkPath(this.path)).toString());
} else if (this.type === 'dapp_file') {
return callback(fs.readFileSync(this.path).toString());
} else if (this.type === 'custom') {
return this.resolver(callback);
parseFileForImport(content, isHttpContract, callback) {
const self = this;
if (typeof isHttpContract === 'function') {
callback = isHttpContract;
isHttpContract = false;
}
if (self.filename.indexOf('.sol') < 0) {
// Only supported in Solidity
return callback(null, content);
}
const regex = /import ["|']([-a-zA-Z0-9@:%_+.~#?&\/=]+)["|'];/g;
let matches;
const filesToDownload = [];
const pathWithoutFile = path.dirname(self.path);
while ((matches = regex.exec(content))) {
const httpFileObj = utils.getExternalContractUrl(matches[1]);
const fileObj = {
fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
url: `${pathWithoutFile}/${matches[1]}`
};
if (httpFileObj) {
// Replace http import by filePath import in content
content = content.replace(matches[1], httpFileObj.filePath);
fileObj.fileRelativePath = httpFileObj.filePath;
fileObj.url = httpFileObj.url;
} else if (!isHttpContract) {
// Just a normal import
continue;
}
filesToDownload.push(fileObj);
}
if (self.downloadedImports) {
// We already parsed this file
return callback(null, content);
}
self.downloadedImports = true;
async.each(filesToDownload, ((fileObj, eachCb) => {
self.downloadFile(fileObj.fileRelativePath, fileObj.url, (_content) => {
eachCb();
});
}), (err) => {
callback(err, content);
});
}
downloadFile (filename, url, callback) {
const self = this;
async.waterfall([
function makeTheDir(next) {
fs.mkdirp(path.dirname(filename), (err) => {
if (err) {
return next(err);
}
next();
});
},
function downloadTheFile(next) {
request(url)
.on('response', function (response) {
if (response.statusCode !== 200) {
next('Getting file returned code ' + response.statusCode);
}
})
.on('error', next)
.pipe(fs.createWriteStream(filename))
.on('finish', next);
},
function readFile(next) {
fs.readFile(filename, next);
},
function parseForImports(content, next) {
self.parseFileForImport(content, true, (err) => {
next(err, content);
});
}
], (err, content) => {
if (err) {
console.error('Error while downloading the file', err);
return callback('');
}
callback(content.toString());
});
}
content (callback) {
let content;
if (this.type === File.types.embark_internal) {
content = fs.readFileSync(fs.embarkPath(this.path)).toString();
} else if (this.type === File.types.dapp_file) {
content = fs.readFileSync(this.path).toString();
} else if (this.type === File.types.custom) {
return this.resolver((theContent) => {
this.parseFileForImport(theContent, (err, newContent) => {
callback(newContent);
});
});
} else if (this.type === File.types.http) {
return this.downloadFile(this.filename, this.path, (content) => {
if (!content) {
return callback(content);
}
this.path = this.filename;
this.type = File.types.dapp_file;
callback(content);
});
} else {
throw new Error("unknown file: " + this.filename);
}
return this.parseFileForImport(content, (err, newContent) => {
callback(newContent);
});
}
}
File.types = {
embark_internal: 'embark_internal',
dapp_file: 'dapp_file',
custom: 'custom',
http: 'http'
};
module.exports = File;

View File

@ -1,24 +1,44 @@
const parseJson = require('parse-json');
let fs = require('fs-extra');
let utils = require('../utils/utils.js');
require('colors');
function mkdirpSync() {
return fs.mkdirpSync.apply(fs.mkdirpSync, arguments);
}
function mkdirp() {
return fs.mkdirp.apply(fs.mkdirp, arguments);
}
function copySync() {
return fs.copySync.apply(fs.copySync, arguments);
}
function appendFileSync() {
return fs.appendFileSync.apply(fs.writeFileSync, arguments);
}
function writeFileSync() {
return fs.writeFileSync.apply(fs.writeFileSync, arguments);
}
function readFile() {
return fs.readFile.apply(fs.readFile, arguments);
}
function readFileSync() {
return fs.readFileSync.apply(fs.readFileSync, arguments);
}
function readJSONSync() {
return fs.readJSONSync.apply(fs.readJSONSync, arguments);
let content = readFileSync.apply(readFileSync, arguments);
try {
return parseJson(content);
} catch(e) {
console.error("error: ".red + arguments[0].green.underline + " " + e.message.green);
process.exit(0);
}
}
function writeJSONSync() {
@ -29,22 +49,36 @@ function existsSync() {
return fs.existsSync.apply(fs.existsSync, arguments);
}
function removeSync() {
return fs.removeSync.apply(fs.removeSync, arguments);
}
// returns embarks root directory
function embarkPath(fileOrDir) {
return utils.joinPath(__dirname, '/../../', fileOrDir);
}
function dappPath() {
return process.env.PWD;
return utils.joinPath(utils.pwd(), ...arguments);
}
function createWriteStream() {
return fs.createWriteStream.apply(fs.createWriteStream, arguments);
}
module.exports = {
mkdirpSync: mkdirpSync,
mkdirp,
copySync: copySync,
readFile,
readFileSync: readFileSync,
appendFileSync: appendFileSync,
writeFileSync: writeFileSync,
readJSONSync: readJSONSync,
writeJSONSync: writeJSONSync,
existsSync: existsSync,
embarkPath: embarkPath
removeSync: removeSync,
embarkPath: embarkPath,
dappPath: dappPath,
createWriteStream
};

View File

@ -1,49 +1,77 @@
let colors = require('colors');
require('colors');
let fs = require('./fs.js');
class Logger {
constructor(options) {
this.events = options.events || {emit: function(){}};
this.logLevels = ['error', 'warn', 'info', 'debug', 'trace'];
this.logLevel = options.logLevel || 'info';
this.logFunction = options.logFunction || console.log;
this.contractsState = options.contractsState || function () {
};
this.setStatus = options.setStatus || console.log;
this.logFile = options.logFile;
}
}
Logger.prototype.writeToFile = function (txt) {
if (!this.logFile) {
return;
}
fs.appendFileSync(this.logFile, "\n" + txt);
};
Logger.prototype.error = function (txt) {
if (!txt || !(this.shouldLog('error'))) {
return;
}
this.events.emit("log", "error", txt);
this.logFunction(txt.red);
this.writeToFile("[error]: " + txt);
};
Logger.prototype.warn = function (txt) {
if (!txt || !(this.shouldLog('warn'))) {
return;
}
this.events.emit("log", "warning", txt);
this.logFunction(txt.yellow);
this.writeToFile("[warning]: " + txt);
};
Logger.prototype.info = function (txt) {
if (!txt || !(this.shouldLog('info'))) {
return;
}
this.events.emit("log", "info", txt);
this.logFunction(txt.green);
this.writeToFile("[info]: " + txt);
};
Logger.prototype.debug = function (txt) {
if (!txt || !(this.shouldLog('debug'))) {
return;
}
this.events.emit("log", "debug", txt);
this.logFunction(txt);
this.writeToFile("[debug]: " + txt);
};
Logger.prototype.trace = function (txt) {
if (!txt || !(this.shouldLog('trace'))) {
return;
}
this.events.emit("log", "trace", txt);
this.logFunction(txt);
this.writeToFile("[trace]: " + txt);
};
Logger.prototype.dir = function (txt) {
if (!txt || !(this.shouldLog('info'))) {
return;
}
this.events.emit("log", "dir", txt);
this.logFunction(txt);
this.writeToFile("[dir]: ");
this.writeToFile(txt);
};
Logger.prototype.shouldLog = function (level) {

View File

@ -1,15 +1,17 @@
/*jshint esversion: 6, loopfunc: true */
var fs = require('./fs.js');
var utils = require('../utils/utils.js');
const fs = require('./fs.js');
const utils = require('../utils/utils.js');
const constants = require('../constants');
// TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) {
this.name = options.name;
this.isInternal = options.isInternal;
this.pluginModule = options.pluginModule;
this.pluginPath = options.pluginPath;
this.pluginConfig = options.pluginConfig;
this.shouldInterceptLogs = options.interceptLogs;
this.clientWeb3Providers = [];
this.beforeDeploy = [];
this.contractsGenerators = [];
this.pipeline = [];
this.pipelineFiles = [];
@ -19,23 +21,63 @@ var Plugin = function(options) {
this.compilers = [];
this.serviceChecks = [];
this.pluginTypes = [];
this.uploadCmds = [];
this.imports = [];
this.embarkjs_code = [];
this.embarkjs_init_code = {};
this.logger = options.logger;
this.events = options.events;
this.config = options.config;
this.loaded = false;
this.currentContext = options.context;
this.acceptedContext = options.pluginConfig.context || [constants.contexts.any];
if (!Array.isArray(this.currentContext)) {
this.currentContext = [this.currentContext];
}
if (!Array.isArray(this.acceptedContext)) {
this.acceptedContext = [this.acceptedContext];
}
};
Plugin.prototype.isContextValid = function() {
if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) {
return true;
}
return this.acceptedContext.some(context => {
return this.currentContext.includes(context);
});
};
Plugin.prototype.hasContext = function(context) {
return this.currentContext.includes(context);
};
Plugin.prototype.loadPlugin = function() {
if (!this.isContextValid()) {
console.log(this.acceptedContext);
this.logger.warn(`Plugin ${this.name} can only be loaded in the context of "${this.acceptedContext.join(', ')}"`);
return false;
}
this.loaded = true;
if (this.shouldInterceptLogs) {
this.interceptLogs(this.pluginModule);
}
(this.pluginModule.call(this, this));
};
Plugin.prototype.loadInternalPlugin = function() {
new this.pluginModule(this, this.pluginConfig); /*eslint no-new: "off"*/
};
Plugin.prototype.loadPluginFile = function(filename) {
return fs.readFileSync(this.pathToFile(filename)).toString();
};
Plugin.prototype.pathToFile = function(filename) {
if (!this.pluginPath) {
throw new Error('pluginPath not defined for plugin: ' + this.name);
}
return utils.joinPath(this.pluginPath, filename);
};
@ -65,6 +107,9 @@ Plugin.prototype.interceptLogs = function(context) {
context.console.trace = function(txt) {
self.logger.trace(self.name + " > " + txt);
};
context.console.dir = function(txt) {
self.logger.dir(txt);
};
};
// TODO: add deploy provider
@ -73,6 +118,11 @@ Plugin.prototype.registerClientWeb3Provider = function(cb) {
this.pluginTypes.push('clientWeb3Provider');
};
Plugin.prototype.registerBeforeDeploy = function(cb) {
this.beforeDeploy.push(cb);
this.pluginTypes.push('beforeDeploy');
};
Plugin.prototype.registerContractsGeneration = function(cb) {
this.contractsGenerators.push(cb);
this.pluginTypes.push('contractGeneration');
@ -99,6 +149,7 @@ Plugin.prototype.registerConsoleCommand = function(cb) {
this.pluginTypes.push('console');
};
// TODO: this only works for services done on startup
Plugin.prototype.registerServiceCheck = function(checkName, checkFn, time) {
this.serviceChecks.push({checkName: checkName, checkFn: checkFn, time: time});
this.pluginTypes.push('serviceChecks');
@ -130,10 +181,25 @@ Plugin.prototype.registerCompiler = function(extension, cb) {
this.pluginTypes.push('compilers');
};
Plugin.prototype.runCommands = function(cmd, options) {
return this.console.map(function(cb) {
return cb.call(this, cmd, options);
}).join("\n");
Plugin.prototype.registerUploadCommand = function(cmd, cb) {
this.uploadCmds.push({cmd: cmd, cb: cb});
this.pluginTypes.push('uploadCmds');
};
Plugin.prototype.addCodeToEmbarkJS = function(code) {
this.embarkjs_code.push(code);
this.pluginTypes.push('embarkjsCode');
};
Plugin.prototype.addProviderInit = function(providerType, code, initCondition) {
this.embarkjs_init_code[providerType] = this.embarkjs_init_code[providerType] || [];
this.embarkjs_init_code[providerType].push([code, initCondition]);
this.pluginTypes.push('initCode');
};
Plugin.prototype.registerImportFile = function(importName, importLocation) {
this.imports.push([importName, importLocation]);
this.pluginTypes.push('imports');
};
Plugin.prototype.runFilePipeline = function() {

View File

@ -9,6 +9,7 @@ var Plugins = function(options) {
this.logger = options.logger;
this.events = options.events;
this.config = options.config;
this.context = options.context;
};
Plugins.prototype.loadPlugins = function() {
@ -20,18 +21,71 @@ Plugins.prototype.loadPlugins = function() {
};
Plugins.prototype.listPlugins = function() {
var list = [];
for (var className in this.pluginList) {
list.push(className);
const list = [];
this.plugins.forEach(plugin => {
if (plugin.loaded) {
list.push(plugin.name);
}
});
return list;
};
Plugins.prototype.loadPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath(process.env.PWD, 'node_modules', pluginName);
// for services that act as a plugin but have core functionality
Plugins.prototype.createPlugin = function(pluginName, pluginConfig) {
let plugin = {};
let pluginPath = false;
var pluginWrapper = new Plugin({
name: pluginName,
pluginModule: plugin,
pluginConfig: pluginConfig,
logger: this.logger,
pluginPath: pluginPath,
interceptLogs: this.interceptLogs,
events: this.events,
config: this.config,
isInternal: true,
context: this.context
});
this.plugins.push(pluginWrapper);
return pluginWrapper;
};
Plugins.prototype.loadInternalPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath('../modules/', pluginName, 'index.js');
var plugin = require(pluginPath);
var pluginWrapper = new Plugin({name: pluginName, pluginModule: plugin, pluginConfig: pluginConfig, logger: this.logger, pluginPath: pluginPath, interceptLogs: this.interceptLogs, events: this.events, config: this.config});
var pluginWrapper = new Plugin({
name: pluginName,
pluginModule: plugin,
pluginConfig: pluginConfig,
logger: this.logger,
pluginPath: pluginPath,
interceptLogs: this.interceptLogs,
events: this.events,
config: this.config,
isInternal: true,
context: this.context
});
pluginWrapper.loadInternalPlugin();
this.plugins.push(pluginWrapper);
};
Plugins.prototype.loadPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath(utils.pwd(), 'node_modules', pluginName);
var plugin = require(pluginPath);
var pluginWrapper = new Plugin({
name: pluginName,
pluginModule: plugin,
pluginConfig: pluginConfig,
logger: this.logger,
pluginPath: pluginPath,
interceptLogs: this.interceptLogs,
events: this.events,
config: this.config,
isInternal: false,
context: this.context
});
pluginWrapper.loadPlugin();
this.plugins.push(pluginWrapper);
};
@ -42,4 +96,18 @@ Plugins.prototype.getPluginsFor = function(pluginType) {
});
};
Plugins.prototype.getPluginsProperty = function(pluginType, property) {
let matchingPlugins = this.plugins.filter(function(plugin) {
return plugin.has(pluginType);
});
let matchingProperties = matchingPlugins.map((plugin) => {
return plugin[property];
});
//return flattened list
if (matchingProperties.length === 0) return [];
return matchingProperties.reduce((a,b) => { return a.concat(b); });
};
module.exports = Plugins;

View File

@ -1,3 +1,4 @@
/*eslint no-unused-vars: off*/
let Web3 = require('web3');
let web3;
let __mainContext;
@ -13,7 +14,8 @@ function doEval(code, _web3) {
}
try {
return eval(code); // jshint ignore:line
// TODO: add trace log here
return eval(code);
} catch(e) {
throw new Error(e + "\n" + code);
}

View File

@ -45,7 +45,6 @@ ServicesMonitor.prototype.initCheck = function (checkName) {
};
ServicesMonitor.prototype.addCheck = function (checkName, checkFn, time) {
let self = this;
this.logger.trace('add check: ' + checkName);
this.checkList[checkName] = {fn: checkFn, interval: time || 5000};

View File

@ -9,14 +9,14 @@ class CommandHistory {
this.pointer = this.history.length;
}
getPreviousCommand(cmd) {
getPreviousCommand() {
if (this.pointer >= 0) {
this.pointer--;
}
return this.history[this.pointer];
}
getNextCommand(cmd) {
getNextCommand() {
if (this.pointer >= this.history.length) {
this.pointer = this.history.length - 1;
return '';

View File

@ -3,13 +3,14 @@ let RunCode = require('../core/runCode.js');
class Console {
constructor(options) {
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.contractsConfig = options.contractsConfig;
}
runCode(code) {
RunCode.doEval(code); // jshint ignore:line
RunCode.doEval(code);
}
processEmbarkCmd (cmd) {
@ -22,37 +23,21 @@ class Console {
// TODO: only if the blockchain is actually active!
// will need to pass te current embark state here
'web3 - instantiated web3.js object configured to the current environment',
'quit - to immediatly exit',
'quit - to immediatly exit (alias: exit)',
'',
'The web3 object and the interfaces for the deployed contracts and their methods are also available'
];
return helpText.join('\n');
} else if (cmd === 'versions') {
//let currentSolcVersion = require('../../package.json').dependencies.solc;
let solcVersionInConfig = this.contractsConfig.versions.solc;
//let web3Version = require('../../package.json').dependencies["web3.js"].replace("^","");
let web3VersionInConfig = this.contractsConfig.versions["web3.js"];
let text = [
'versions in use:',
'solc: ' + solcVersionInConfig,
'web3.js: ' + web3VersionInConfig
];
return text.join('\n');
} else if (cmd === 'quit') {
} else if (['quit', 'exit', 'sair', 'sortir'].indexOf(cmd) >= 0) {
utils.exit();
}
return false;
}
executeCmd(cmd, callback) {
let plugin, pluginOutput;
let plugins = this.plugins.getPluginsFor('console');
for (let i = 0; i < plugins.length; i++) {
plugin = plugins[i];
pluginOutput = plugin.runCommands(cmd, {});
var pluginCmds = this.plugins.getPluginsProperty('console', 'console');
for (let pluginCmd of pluginCmds) {
let pluginOutput = pluginCmd.call(this, cmd, {});
if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput);
}

View File

@ -6,6 +6,7 @@ let Console = require('./console.js');
class Dashboard {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.version = options.version;
this.env = options.env;
@ -18,14 +19,23 @@ class Dashboard {
async.waterfall([
function startConsole(callback) {
console = new Console({plugins: self.plugins, version: self.version, contractsConfig: self.contractsConfig});
console = new Console({
events: self.events,
plugins: self.plugins,
version: self.version,
contractsConfig: self.contractsConfig
});
callback();
},
function startMonitor(callback) {
monitor = new Monitor({env: self.env, console: console});
monitor = new Monitor({env: self.env, console: console, events: self.events});
self.logger.logFunction = monitor.logEntry;
self.logger.contractsState = monitor.setContracts;
self.logger.setStatus = monitor.setStatus.bind(monitor);
self.events.on('contractsState', monitor.setContracts);
self.events.on('status', monitor.setStatus.bind(monitor));
self.events.on('servicesState', monitor.availableServices.bind(monitor));
self.events.setCommandHandler("console:command", monitor.executeCmd.bind(monitor));
self.logger.info('========================'.bold.green);
self.logger.info(('Welcome to Embark ' + self.version).yellow.bold);

View File

@ -1,21 +1,20 @@
/*jshint esversion: 6 */
let blessed = require("blessed");
let CommandHistory = require('./command_history.js');
class Dashboard {
constructor(options) {
let title = (options && options.title) || "Embark " + options.version;
constructor(_options) {
let options = _options || {};
this.env = options.env;
this.console = options.console;
this.history = new CommandHistory();
this.events = options.events;
this.color = (options && options.color) || "green";
this.minimal = (options && options.minimal) || false;
this.color = options.color || "green";
this.minimal = options.minimal || false;
this.screen = blessed.screen({
smartCSR: true,
title: title,
title: options.title || ("Embark " + options.version),
dockBorders: false,
fullUnicode: true,
autoPadding: true
@ -41,20 +40,20 @@ class Dashboard {
}
availableServices(_services) {
let services = [];
let check;
for (check in _services) {
let checkObj = _services[check];
if (checkObj.status === 'on') {
services.push(checkObj.name.green);
} else if (checkObj.status === 'off') {
services.push(checkObj.name.red);
} else if (checkObj.status === 'warn') {
services.push(checkObj.name.grey);
} else {
services.push(checkObj.name);
}
let stateColors = {
'on': 'green',
'off': 'red',
'warn': 'grey'
};
let services = Object.keys(_services).map((service) => {
let checkObj = _services[service];
if (checkObj.status in stateColors) {
let color = stateColors[checkObj.status];
return checkObj.name[color];
}
return checkObj.name;
});
this.progress.setContent(services.join('\n'));
this.screen.render();
@ -345,21 +344,31 @@ class Dashboard {
self.input.focus();
});
this.input.on('submit', function (data) {
if (data !== '') {
self.history.addCommand(data);
self.logText.log('console> '.bold.green + data);
self.console.executeCmd(data, function (result) {
self.logText.log(result);
});
}
self.input.clearValue();
self.input.focus();
});
this.input.on('submit', this.submitCmd.bind(this));
this.screen.append(this.consoleBox);
}
submitCmd(cmd) {
if (cmd !== '') {
this.history.addCommand(cmd);
this.executeCmd(cmd);
}
this.input.clearValue();
this.input.focus();
}
executeCmd(cmd, cb) {
const self = this;
self.logText.log('console> '.bold.green + cmd);
self.console.executeCmd(cmd, function (result) {
self.logText.log(result);
if (cb) {
cb(result);
}
});
}
}
module.exports = Dashboard;

View File

@ -1,14 +1,21 @@
/*jshint esversion: 6 */
let async = require('async');
const constants = require('./constants');
const _ = require('underscore');
// require("./utils/debug_util.js")(__filename, async);
let colors = require('colors');
require('colors');
// Override process.chdir so that we have a partial-implementation PWD for Windows
const realChdir = process.chdir;
process.chdir = (...args) => {
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
realChdir(...args);
};
let Engine = require('./core/engine.js');
let IPFS = require('./upload/ipfs.js');
let Swarm = require('./upload/swarm.js');
let version = require('../package.json').version;
class Embark {
@ -24,24 +31,39 @@ class Embark {
let Config = require('./core/config.js');
this.events = new Events();
this.logger = new Logger({logLevel: 'debug'});
this.logger = new Logger({logLevel: 'debug', events: this.events});
this.config = new Config({env: env, logger: this.logger, events: this.events});
this.config = new Config({env: env, logger: this.logger, events: this.events, context: this.context});
this.config.loadConfigFiles(options);
this.plugins = this.config.plugins;
}
blockchain(env, client) {
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env).run();
this.context = [constants.contexts.blockchain];
let blockchainConfig = this.config.blockchainConfig;
let storageConfig = this.config.storageConfig;
let webServerConfig = this.config.webServerConfig;
if(blockchainConfig.rpcCorsDomain === 'auto') {
if(webServerConfig) blockchainConfig.rpcCorsDomain = `http://${webServerConfig.host}:${webServerConfig.port}`;
if(storageConfig) blockchainConfig.rpcCorsDomain += `${blockchainConfig.rpcCorsDomain.length ? ',' : ''}${storageConfig.protocol}://${storageConfig.host}:${storageConfig.port}`;
}
if(blockchainConfig.wsOrigins === 'auto') {
if(webServerConfig) blockchainConfig.wsOrigins = `http://${webServerConfig.host}:${webServerConfig.port}`;
if(storageConfig) blockchainConfig.wsOrigins += `${blockchainConfig.wsOrigins.length ? ',' : ''}${storageConfig.protocol}://${storageConfig.host}:${storageConfig.port}`;
}
return require('./cmds/blockchain/blockchain.js')(blockchainConfig, client, env).run();
}
simulator(options) {
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
let Simulator = require('./cmds/simulator.js');
let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig});
let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig, logger: this.logger});
simulator.run(options);
}
generateTemplate(templateName, destinationFolder, name) {
this.context = [constants.contexts.templateGeneration];
let TemplateGenerator = require('./cmds/template_generator.js');
let templateGenerator = new TemplateGenerator(templateName);
templateGenerator.generate(destinationFolder, name);
@ -49,21 +71,24 @@ class Embark {
run(options) {
let self = this;
self.context = options.context || [constants.contexts.run, constants.contexts.build];
let Dashboard = require('./dashboard/dashboard.js');
let env = options.env;
let windowSize = require('window-size');
let engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json'
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
logLevel: options.logLevel,
context: self.context
});
engine.init();
if (!options.useDashboard) {
console.log('========================'.bold.green);
console.log(('Welcome to Embark ' + this.version).yellow.bold);
console.log('========================'.bold.green);
engine.logger.info('========================'.bold.green);
engine.logger.info(('Welcome to Embark ' + this.version).yellow.bold);
engine.logger.info('========================'.bold.green);
}
async.parallel([
@ -73,6 +98,7 @@ class Embark {
}
let dashboard = new Dashboard({
events: engine.events,
logger: engine.logger,
plugins: engine.plugins,
version: self.version,
@ -80,17 +106,7 @@ class Embark {
contractsConfig: engine.config.contractsConfig
});
dashboard.start(function () {
engine.events.on('code-generator-ready', function () {
engine.events.request('code-vanila-deployment', function (abi) {
dashboard.console.runCode(abi);
});
});
engine.logger.info('dashboard start');
engine.events.on('servicesState', function (servicesState) {
dashboard.monitor.availableServices(servicesState);
});
callback();
});
},
@ -101,11 +117,12 @@ class Embark {
}
engine.startMonitor();
engine.startService("libraryManager");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService("ipfs");
engine.startService(engine.config.storageConfig.provider, {bzz: engine.web3.bzz});
engine.events.on('check:backOnline:Ethereum', function () {
engine.logger.info('Ethereum node detected..');
@ -115,6 +132,12 @@ class Embark {
});
});
engine.events.on('outputDone', function () {
engine.logger.info("Looking for documentation? You can find it at ".cyan + "http://embark.readthedocs.io/".green.underline + ".".cyan);
engine.logger.info("Ready".underline);
engine.events.emit("status", "Ready".green);
});
engine.deployManager.deployContracts(function (err) {
engine.startService("fileWatcher");
if (options.runWebserver) {
@ -126,26 +149,36 @@ class Embark {
callback(err);
});
}
], function (err, result) {
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
engine.logger.setStatus("Ready".green);
engine.logger.info("Looking for documentation? you can find it at ".cyan + "http://embark.readthedocs.io/".green.underline);
engine.logger.info("Ready".underline);
engine.events.emit('firstDeploymentDone');
let size = windowSize.get();
if (size.height < 40 || size.width < 118) {
engine.logger.warn("tip: you can resize the terminal or disable the dashboard with " + "embark run --nodashboard".bold.underline);
}
}
});
}
build(options) {
this.context = options.context || [constants.contexts.build];
let engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins,
context: this.context
});
engine.init();
@ -156,10 +189,13 @@ class Embark {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
}
engine.startService("libraryManager");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService("ipfs");
engine.startService("swarm", {bzz: engine.web3.bzz});
callback();
},
function deploy(callback) {
@ -167,7 +203,7 @@ class Embark {
callback(err);
});
}
], function (err, result) {
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
@ -180,26 +216,172 @@ class Embark {
}
initTests(options) {
let Test = require('./core/test.js');
this.context = options.context || [constants.contexts.test];
let Test = require('./tests/test.js');
options.context = this.context;
return new Test(options);
}
// TODO: should deploy if it hasn't already
upload(platform) {
if (platform === 'ipfs') {
let ipfs = new IPFS({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig});
ipfs.deploy();
} else if (platform === 'swarm') {
let swarm = new Swarm({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig});
swarm.deploy();
} else {
console.log(("unknown platform: " + platform).red);
console.log('try "embark upload ipfs" or "embark upload swarm"'.green);
graph(options) {
this.context = options.context || [constants.contexts.graph];
options.onlyCompile = true;
let engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
context: this.context
});
engine.init();
async.parallel([
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
}
engine.startMonitor();
engine.startService("libraryManager");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment", {onlyCompile: true});
engine.deployManager.deployContracts(function (err) {
callback(err);
});
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
const GraphGenerator = require('./cmds/graph.js');
let graphGen = new GraphGenerator(engine);
graphGen.generate();
engine.logger.info("Done".underline);
process.exit();
}
});
}
reset() {
this.context = [constants.contexts.reset];
let resetCmd = require('./cmds/reset.js');
resetCmd();
}
upload(platform, options) {
this.context = options.context || [constants.contexts.upload, constants.contexts.build];
let engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
logFile: options.logFile,
logLevel: options.logLevel,
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins
});
engine.init();
let cmdPlugin;
async.waterfall([
function startServices(callback) {
engine.startService("libraryManager");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService(platform.toLowerCase(), {bzz: engine.web3.bzz});
engine.startMonitor();
callback();
},
function checkStorageService(callback){
let checkFn;
_.find(engine.servicesMonitor.checkList, (value, key) => {
if(key.toLowerCase() === platform.toLowerCase()){
checkFn = value;
return true;
}
});
if (!checkFn || typeof checkFn.fn !== 'function') {
return callback();
}
checkFn.fn(function (serviceCheckResult) {
if (!serviceCheckResult.status || serviceCheckResult.status === 'off') {
return callback({message: `Cannot upload: ${platform} node is not running on http://${engine.config.storageConfig.host}:${engine.config.storageConfig.port}.`});
}
callback();
});
},
function setupStoragePlugin(callback){
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
}
// check use has input existing storage plugin
let cmdPlugins = engine.plugins.getPluginsFor('uploadCmds');
if (cmdPlugins.length > 0) {
cmdPlugin = cmdPlugins.find((pluginCmd) => {
return pluginCmd.uploadCmds.some(uploadCmd => {
return uploadCmd.cmd === platform;
});
});
}
if (!cmdPlugin) {
engine.logger.info('try "embark upload ipfs" or "embark upload swarm"'.green);
return callback({message: 'unknown platform: ' + platform});
}
callback();
},
function deploy(callback) {
// 2. upload to storage (outputDone event triggered after webpack finished)
engine.events.on('outputDone', function () {
cmdPlugin.uploadCmds[0].cb()
.then((success) => {
callback(null, success);
})
.catch(callback);
});
// 1. build the contracts and dapp webpack
engine.deployManager.deployContracts(function (err) {
engine.logger.info("finished deploying".underline);
if(err){
callback(err);
}
});
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
} else {
engine.logger.info(`finished building DApp and deploying to ${platform}`.underline);
}
// needed due to child processes
process.exit();
});
}
runTests(file) {
let RunTests = require('./core/run_tests.js');
this.context = [constants.contexts.test];
let RunTests = require('./tests/run_tests.js');
RunTests.run(file);
}
@ -207,7 +389,8 @@ class Embark {
// temporary until next refactor
Embark.initTests = function(options) {
let Test = require('./core/test.js');
let Test = require('./tests/test.js');
options.context = [constants.contexts.test];
return new Test(options);
};

View File

@ -0,0 +1,121 @@
import IpfsApi from 'ipfs-api';
let __embarkIPFS = {};
__embarkIPFS.setProvider = function (options) {
var self = this;
var promise = new Promise(function (resolve, reject) {
try {
if (options === undefined) {
self.ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/";
} else {
var ipfsOptions = {host: options.server, protocol: 'http'};
if (options.protocol) {
ipfsOptions.protocol = options.protocol;
}
if (options.port && options.port !== 'false') {
ipfsOptions.port = options.port;
}
self.ipfsConnection = IpfsApi(ipfsOptions);
self._getUrl = options.getUrl || "http://localhost:8080/ipfs/";
}
resolve(self);
} catch (err) {
console.log(err);
self.ipfsConnection = null;
reject(new Error('Failed to connect to IPFS'));
}
});
return promise;
};
__embarkIPFS.saveText = function (text) {
const self = this;
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
self.ipfsConnection.add(self.ipfsConnection.Buffer.from(text), function (err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
});
return promise;
};
__embarkIPFS.get = function (hash) {
const self = this;
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
self.ipfsConnection.get(hash, function (err, files) {
if (err) {
return reject(err);
}
resolve(files[0].content.toString());
});
});
return promise;
};
__embarkIPFS.uploadFile = function (inputSelector) {
const self = this;
var file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
var reader = new FileReader();
reader.onloadend = function () {
var fileContent = reader.result;
var buffer = self.ipfsConnection.Buffer.from(fileContent);
self.ipfsConnection.add(buffer, function (err, result) {
if (err) {
reject(err);
} else {
resolve(result[0].path);
}
});
};
reader.readAsArrayBuffer(file);
});
return promise;
};
__embarkIPFS.isAvailable = function () {
return new Promise((resolve) => {
if (!this.ipfsConnection) {
return resolve(false);
}
this.ipfsConnection.id()
.then((id) => {
resolve(Boolean(id));
})
.catch(() => {
resolve(false);
});
});
};
__embarkIPFS.getUrl = function (hash) {
return (this._getUrl || "http://localhost:8080/ipfs/") + hash;
};

116
lib/modules/ipfs/index.js Normal file
View File

@ -0,0 +1,116 @@
let UploadIPFS = require('./upload.js');
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
class IPFS {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.buildDir = options.buildDir;
this.storageConfig = options.storageConfig;
this.host = options.host || this.storageConfig.host;
this.port = options.port || this.storageConfig.port;
this.addCheck = options.addCheck;
this.embark = embark;
this.commandlineDeploy();
this.setServiceCheck();
this.addIPFSToEmbarkJS();
this.addSetProvider();
}
commandlineDeploy() {
let upload_ipfs = new UploadIPFS({
buildDir: this.buildDir || 'dist/',
storageConfig: this.storageConfig,
configIpfsBin: this.storageConfig.ipfs_bin || "ipfs"
});
this.embark.registerUploadCommand('ipfs', upload_ipfs.deploy.bind(upload_ipfs));
}
setServiceCheck() {
let self = this;
let storageConfig = this.storageConfig;
if (!storageConfig.enabled) {
return;
}
if (storageConfig.provider !== 'ipfs' && storageConfig.available_providers.indexOf("ipfs") < 0) {
return;
}
self.events.on('check:backOnline:IPFS', function () {
self.logger.info('IPFS node detected..');
});
self.events.on('check:wentOffline:IPFS', function () {
self.logger.info('IPFS node is offline..');
});
if (!self.addCheck) {
return;
}
self.addCheck('IPFS', function (cb) {
self.logger.trace("Checking IPFS version...");
utils.httpGetJson('http://' + self.host + ':' + self.port + '/api/v0/version', function (err, body) {
if (err) {
self.logger.trace("Check IPFS version error: " + err);
return cb({name: "IPFS ", status: 'off'});
}
if (body.Version) {
return cb({name: ("IPFS " + body.Version), status: 'on'});
}
return cb({name: "IPFS ", status: 'on'});
});
});
}
addIPFSToEmbarkJS() {
const self = this;
// TODO: make this a shouldAdd condition
if (this.storageConfig === {}) {
return;
}
if ((this.storageConfig.available_providers.indexOf('ipfs') < 0) && (this.storageConfig.provider !== 'ipfs' || this.storageConfig.enabled !== true)) {
return;
}
self.events.request("version:get:ipfs-api", function(ipfsApiVersion) {
let currentIpfsApiVersion = require('../../../package.json').dependencies["ipfs-api"];
if (ipfsApiVersion !== currentIpfsApiVersion) {
self.events.request("version:getPackageLocation", "ipfs-api", ipfsApiVersion, function(err, location) {
self.embark.registerImportFile("ipfs-api", fs.dappPath(location));
});
}
});
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Storage.registerProvider('ipfs', __embarkIPFS);";
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({
server: this.storageConfig.host,
port: this.storageConfig.port,
protocol: this.storageConfig.protocol,
getUrl: this.storageConfig.getUrl
});
let code = "\nEmbarkJS.Storage.setProvider('ipfs'," + config + ");";
let shouldInit = (storageConfig) => {
return (storageConfig.provider === 'ipfs' && storageConfig.enabled === true);
};
this.embark.addProviderInit('storage', code, shouldInit);
}
}
module.exports = IPFS;

View File

@ -0,0 +1,63 @@
require('colors');
let async = require('async');
let shelljs = require('shelljs');
class IPFS {
constructor(options) {
this.options = options;
this.buildDir = options.buildDir || 'dist/';
this.storageConfig = options.storageConfig;
this.configIpfsBin = this.storageConfig.ipfs_bin || "ipfs";
}
deploy() {
return new Promise((resolve, reject) => {
console.log("deploying to ipfs!");
let self = this;
async.waterfall([
function findBinary(callback) {
let ipfs_bin = shelljs.which(self.configIpfsBin);
if (ipfs_bin === 'ipfs not found' || !ipfs_bin) {
console.log(('=== WARNING: ' + self.configIpfsBin + ' not found or not in the path. Guessing ~/go/bin/ipfs for path').yellow);
ipfs_bin = "~/go/bin/ipfs";
}
callback(null, ipfs_bin);
},
function runCommand(ipfs_bin, callback) {
let cmd = `"${ipfs_bin}" add -r ${self.buildDir}`;
console.log(("=== adding " + self.buildDir + " to ipfs").green);
console.debug(cmd);
shelljs.exec(cmd, {silent:true}, function(code, stdout, stderr){ // {silent:true}: don't echo cmd output so it can be controlled via logLevel
console.log(stdout.green);
callback(stderr, stdout);
});
},
function getHashFromOutput(result, callback) {
let rows = result.split("\n");
let dir_row = rows[rows.length - 2];
let dir_hash = dir_row.split(" ")[1];
callback(null, dir_hash);
},
function printUrls(dir_hash, callback) {
console.log(("=== DApp available at http://localhost:8080/ipfs/" + dir_hash + "/").green);
console.log(("=== DApp available at http://gateway.ipfs.io/ipfs/" + dir_hash + "/").green);
callback();
}
], function (err, _result) {
if (err) {
console.log("error uploading to ipfs".red);
console.log(err);
reject(err);
}
else resolve('successfully uploaded to ipfs');
});
});
}
}
module.exports = IPFS;

View File

@ -0,0 +1,128 @@
let async = require('../../utils/async_extend.js');
let SolcW = require('./solcW.js');
class Solidity {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.contractDirectories = options.contractDirectories;
embark.registerCompiler(".sol", this.compile_solidity.bind(this));
}
compile_solidity(contractFiles, cb) {
let self = this;
let input = {};
let solcW;
async.waterfall([
function prepareInput(callback) {
async.each(contractFiles,
function(file, fileCb) {
let filename = file.filename;
for (let directory of self.contractDirectories) {
let match = new RegExp("^" + directory);
filename = filename.replace(match, '');
}
file.content(function(fileContent) {
if (!fileContent) {
self.logger.error('Error while loading the content of ' + filename);
return fileCb();
}
input[filename] = {content: fileContent.replace(/\r\n/g, '\n')};
fileCb();
});
},
function (err) {
callback(err);
}
);
},
function loadCompiler(callback) {
// TODO: there ino need to load this twice
solcW = new SolcW({logger: self.logger, events: self.events});
if (solcW.isCompilerLoaded()) {
return callback();
}
self.logger.info("loading solc compiler..");
solcW.load_compiler(function (err) {
callback(err);
});
},
function compileContracts(callback) {
self.logger.info("compiling solidity contracts...");
let jsonObj = {
language: 'Solidity',
sources: input,
settings: {
optimizer: {
enabled: true,
runs: 200
},
outputSelection: {
'*': {
'*': ['abi', 'metadata', 'userdoc', 'devdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates']
}
}
}
};
solcW.compile(jsonObj, function (output) {
if (output.errors) {
for (let i=0; i<output.errors.length; i++) {
if (output.errors[i].type === 'Warning') {
self.logger.warn(output.errors[i].formattedMessage);
}
if (output.errors[i].type === 'Error' || output.errors[i].severity === 'error') {
return callback(new Error("Solidity errors: " + output.errors[i].formattedMessage).message);
}
}
}
callback(null, output);
});
},
function createCompiledObject(output, callback) {
let json = output.contracts;
if (!output || !output.contracts) {
return callback(new Error("error compiling for unknown reasons"));
}
if (Object.keys(output.contracts).length === 0 && output.sourceList.length > 0) {
return callback(new Error("error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code").message);
}
let compiled_object = {};
for (let contractFile in json) {
for (let contractName in json[contractFile]) {
let contract = json[contractFile][contractName];
const className = contractName;
const filename = contractFile;
compiled_object[className] = {};
compiled_object[className].code = contract.evm.bytecode.object;
compiled_object[className].runtimeBytecode = contract.evm.deployedBytecode.object;
compiled_object[className].realRuntimeBytecode = contract.evm.deployedBytecode.object.slice(0, -68);
compiled_object[className].swarmHash = contract.evm.deployedBytecode.object.slice(-68).slice(0, 64);
compiled_object[className].gasEstimates = contract.evm.gasEstimates;
compiled_object[className].functionHashes = contract.evm.methodIdentifiers;
compiled_object[className].abiDefinition = contract.abi;
compiled_object[className].filename = filename;
}
}
callback(null, compiled_object);
}
], function (err, result) {
cb(err, result);
});
}
}
module.exports = Solidity;

View File

@ -0,0 +1,41 @@
let solc;
const fs = require('fs-extra');
const path = require('path');
const constants = require('../../constants');
const Utils = require('../../utils/utils');
function findImports(filename) {
if (filename.startsWith('http') || filename.startsWith('git')) {
const fileObj = Utils.getExternalContractUrl(filename);
filename = fileObj.filePath;
}
if (fs.existsSync(filename)) {
return {contents: fs.readFileSync(filename).toString()};
}
if (fs.existsSync(path.join('./node_modules/', filename))) {
return {contents: fs.readFileSync(path.join('./node_modules/', filename)).toString()};
}
if (fs.existsSync(path.join(constants.httpContractsDirectory, filename))) {
return {contents: fs.readFileSync(path.join('./.embark/contracts', filename)).toString()};
}
return {error: 'File not found'};
}
process.on('message', function (msg) {
if (msg.action === 'loadCompiler') {
solc = require(msg.solcLocation);
process.send({result: "loadedCompiler"});
}
if (msg.action === 'compile') {
// TODO: only available in 0.4.11; need to make versions warn about this
let output = solc.compileStandardWrapper(JSON.stringify(msg.jsonObj), findImports);
process.send({result: "compilation", output: output});
}
});
process.on('exit', function () {
process.exit(0);
});

View File

@ -0,0 +1,60 @@
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
let solcProcess;
let compilerLoaded = false;
let currentSolcVersion = require('../../../package.json').dependencies.solc;
class SolcW {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
}
load_compiler(done) {
const self = this;
if (compilerLoaded) {
done();
}
solcProcess = require('child_process').fork(utils.joinPath(__dirname, '/solcP.js'));
solcProcess.once('message', function (msg) {
if (msg.result !== 'loadedCompiler') {
return;
}
compilerLoaded = true;
done();
});
this.events.request("version:get:solc", function(solcVersion) {
if (solcVersion === currentSolcVersion) {
solcProcess.send({action: 'loadCompiler', solcLocation: 'solc'});
} else {
self.events.request("version:getPackageLocation", "solc", solcVersion, function(err, location) {
if (err) {
return done(err);
}
let requirePath = fs.dappPath(location);
solcProcess.send({action: 'loadCompiler', solcLocation: requirePath});
});
}
});
}
isCompilerLoaded() {
return (compilerLoaded === true);
}
compile(jsonObj, done) {
solcProcess.once('message', function (msg) {
if (msg.result !== 'compilation') {
return;
}
done(JSON.parse(msg.output));
});
solcProcess.send({action: 'compile', jsonObj: jsonObj});
}
}
module.exports = SolcW;

View File

@ -0,0 +1,94 @@
/*global web3 */
let __embarkSwarm = {};
const bytes = require("eth-lib/lib/bytes");
__embarkSwarm.setProvider = function (options) {
this.bzz = web3.bzz;
this.protocol = options.protocol;
this.host = options.host;
this.port = options.port;
this.connectUrl = `${options.protocol}://${options.host}:${options.port}`;
this.connectError = new Error(`Cannot connect to Swarm node on ${this.connectUrl}`);
this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`;
return new Promise((resolve, reject) => {
try {
if (!this.bzz.currentProvider) {
this.bzz.setProvider(`${options.protocol}://${options.host}:${options.port}`);
}
resolve(this);
} catch (err) {
console.log(err);
reject(this.connectError);
}
});
};
__embarkSwarm.isAvailable = function () {
return new Promise((resolve, reject) => {
if (!this.bzz) {
return resolve(false);
}
this.bzz.isAvailable()
.then(resolve)
.catch(() => {
reject(this.connectError);
});
});
};
__embarkSwarm.saveText = function (text) {
return new Promise((resolve, reject) => {
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
}
this.bzz.upload(text)
.then(resolve)
.catch(reject);
}).catch(reject);
});
};
__embarkSwarm.get = function (hash) {
return new Promise((resolve, reject) => {
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
}
this.bzz.download(hash)
.then((uint8Array) => resolve(bytes.toString(bytes.fromUint8Array(uint8Array))))
.catch(reject);
}).catch(reject);
});
};
__embarkSwarm.uploadFile = function (inputSelector) {
let file = inputSelector[0].files[0];
if (file === undefined) {
throw new Error('no file found');
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = (event) => {
const fileContent = new Uint8Array(event.target.result);
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
}
this.bzz.upload(fileContent)
.then(resolve)
.catch(reject);
}).catch(reject);
};
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
};
__embarkSwarm.getUrl = function (hash) {
return this._getUrl + hash;
};

113
lib/modules/swarm/index.js Normal file
View File

@ -0,0 +1,113 @@
let UploadSwarm = require('./upload.js');
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
class Swarm {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.buildDir = options.buildDir;
this.storageConfig = options.storageConfig;
this.host = options.host || this.storageConfig.host;
this.port = options.port || this.storageConfig.port;
this.addCheck = options.addCheck;
this.embark = embark;
this.bzz = options.bzz;
this.initSwarmProvider();
this.commandlineDeploy();
this.setServiceCheck();
this.addSwarmToEmbarkJS();
this.addSetProvider();
}
initSwarmProvider(){
if(!this.bzz.currentProvider) {
this.bzz.setProvider(`http://${this.host}:${this.port}`);
}
}
commandlineDeploy() {
this.upload_swarm = new UploadSwarm({
buildDir: this.buildDir || 'dist/',
storageConfig: this.storageConfig,
bzz: this.bzz
});
this.embark.registerUploadCommand('swarm', this.upload_swarm.deploy.bind(this.upload_swarm));
}
setServiceCheck() {
let self = this;
let storageConfig = this.storageConfig;
if (!storageConfig.enabled) {
return;
}
if (storageConfig.provider !== 'swarm' && storageConfig.available_providers.indexOf("swarm") < 0) {
return;
}
this.events.on('check:backOnline:Swarm', function () {
self.logger.info('Swarm node detected...');
});
this.events.on('check:wentOffline:Swarm', function () {
self.logger.info('Swarm node is offline...');
});
if (!this.addCheck) {
return;
}
// add check for console
this.addCheck('Swarm', function(cb){
self.logger.trace("Checking Swarm availability...");
self.bzz.isAvailable().then(result => {
return cb({name: "Swarm ", status: result ? 'on':'off'});
}).catch(err => {
self.logger.trace("Check Swarm availability error: " + err);
return cb({name: "Swarm ", status: 'off'});
});
});
}
addSwarmToEmbarkJS() {
// TODO: make this a shouldAdd condition
if (this.storageConfig === {}) {
return;
}
if ((this.storageConfig.available_providers.indexOf('swarm') < 0) && (this.storageConfig.provider !== 'swarm' || this.storageConfig.enabled !== true)) {
return;
}
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Storage.registerProvider('swarm', __embarkSwarm);";
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({
host: this.storageConfig.host,
port: this.storageConfig.port,
protocol: this.storageConfig.protocol,
getUrl: this.storageConfig.getUrl
});
let code = "\nEmbarkJS.Storage.setProvider('swarm'," + config + ");";
let shouldInit = (storageConfig) => {
return (storageConfig.provider === 'swarm' && storageConfig.enabled === true);
};
this.embark.addProviderInit('storage', code, shouldInit);
}
}
module.exports = Swarm;

View File

@ -0,0 +1,50 @@
require('colors');
let async = require('async');
class Swarm {
constructor(options) {
this.options = options;
this.buildDir = options.buildDir || 'dist/';
this.bzz = options.bzz;
this.storageConfig = options.storageConfig;
}
deploy() {
return new Promise((resolve, reject) => {
console.log("deploying to swarm!");
let self = this;
let bzz = this.bzz;
async.waterfall([
function runCommand(callback) {
console.log(("=== adding " + self.buildDir + " to swarm").green);
bzz.upload({
path: self.buildDir, // path to data / file / directory
kind: "directory", // could also be "file" or "data"
defaultFile: "index.html" // optional, and only for kind === "directory"
})
.then((success) => {
callback(null, success);
})
.catch(callback);
},
function printUrls(dir_hash, callback) {
if (!dir_hash) {
return callback('No directory hash was returned');
}
console.log((`=== DApp available at ${self.storageConfig.getUrl}${dir_hash}/`).green);
callback();
}
], function (err, _result) {
if (err) {
console.log("error uploading to swarm".red);
console.log(err);
return reject(err);
}
resolve('successfully uploaded to swarm');
});
});
}
}
module.exports = Swarm;

View File

@ -0,0 +1,77 @@
let async = require('../../utils/async_extend.js');
const shelljs = require('shelljs');
const path = require('path');
class Vyper {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.contractDirectories = options.contractDirectories;
embark.registerCompiler(".py", this.compile_vyper.bind(this));
}
static compileVyperContract(filename, compileABI, callback) {
const params = compileABI ? '-f json ' : '';
shelljs.exec(`vyper ${params}${filename}`, {silent: true}, (code, stdout, stderr) => {
if (stderr) {
return callback(stderr);
}
if (code !== 0) {
return callback(`Vyper exited with error code ${code}`);
}
if (!stdout) {
return callback('Execution returned no result');
}
callback(null, stdout.replace(/\n/g, ''));
});
}
compile_vyper(contractFiles, cb) {
let self = this;
if (!contractFiles || !contractFiles.length) {
return cb();
}
self.logger.info("compiling Vyper contracts...");
const compiled_object = {};
async.each(contractFiles,
function (file, fileCb) {
const className = path.basename(file.filename).split('.')[0];
compiled_object[className] = {};
async.parallel([
function getByteCode(paraCb) {
Vyper.compileVyperContract(file.filename, false, (err, byteCode) => {
if (err) {
return paraCb(err);
}
compiled_object[className].runtimeBytecode = byteCode;
compiled_object[className].realRuntimeBytecode = byteCode;
compiled_object[className].code = byteCode;
paraCb();
});
},
function getABI(paraCb) {
Vyper.compileVyperContract(file.filename, true, (err, ABIString) => {
if (err) {
return paraCb(err);
}
let ABI = [];
try {
ABI = JSON.parse(ABIString);
} catch (e) {
return paraCb('ABI is not valid JSON');
}
compiled_object[className].abiDefinition = ABI;
paraCb();
});
}
], fileCb);
},
function (err) {
cb(err, compiled_object);
});
}
}
module.exports = Vyper;

View File

@ -0,0 +1,68 @@
var utils = require('../../utils/utils.js');
var Server = require('./server.js');
class WebServer {
constructor(embark, options) {
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;
this.addCheck = options.addCheck;
this.webServerConfig = embark.config.webServerConfig;
if (!this.webServerConfig.enabled) {
return;
}
this.host = options.host || this.webServerConfig.host;
this.port = options.port || this.webServerConfig.port;
this.events.emit("status", "Starting Server");
this.server = new Server({logger: this.logger, host: this.host, port: this.port});
this.setServiceCheck();
this.listenToCommands();
this.registerConsoleCommands();
this.server.start();
}
setServiceCheck() {
let url = 'http://' + this.host + ':' + this.port;
//embark.registerServiceCheck('WebserverService', function (cb) {
this.addCheck('Webserver', function (cb) {
utils.checkIsAvailable(url, function (available) {
let devServer = 'Webserver (' + url + ')';
let serverStatus = (available ? 'on' : 'off');
return cb({name: devServer, status: serverStatus});
});
});
this.events.on('check:wentOffline:Webserver', () => {
this.logger.info("Webserver is offline");
});
}
listenToCommands() {
this.events.setCommandHandler('start-webserver', () => { this.server.start(); });
this.events.setCommandHandler('stop-webserver', () => { this.server.stop(); });
}
registerConsoleCommands() {
const self = this;
self.embark.registerConsoleCommand((cmd, _options) => {
if (cmd === 'webserver start') {
self.events.request("start-webserver");
return " ";
}
if (cmd === 'webserver stop') {
self.events.request("stop-webserver");
return "stopping webserver...";
}
return false;
});
}
}
module.exports = WebServer;

View File

@ -0,0 +1,52 @@
let finalhandler = require('finalhandler');
let http = require('http');
let serveStatic = require('serve-static');
require('http-shutdown').extend();
class Server {
constructor(options) {
this.dist = options.dist || 'dist/';
this.port = options.port || 8000;
this.hostname = options.host || 'localhost';
this.logger = options.logger;
}
start(callback) {
if (this.server && this.server.listening) {
this.logger.warn("a webserver is already running at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
if (callback) {
callback();
}
return;
}
let serve = serveStatic(this.dist, {'index': ['index.html', 'index.htm']});
this.server = http.createServer(function onRequest(req, res) {
serve(req, res, finalhandler(req, res));
}).withShutdown();
this.logger.info("webserver available at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
this.server.listen(this.port, this.hostname);
if (callback) {
callback();
}
}
stop(callback) {
if (!this.server || !this.server.listening) {
this.logger.warn("no webserver is currently running");
if (callback) {
callback();
}
return;
}
this.server.shutdown(function() {
if (callback) {
callback();
}
});
}
}
module.exports = Server;

View File

@ -0,0 +1,77 @@
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
class Whisper {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.communicationConfig = options.communicationConfig;
this.addCheck = options.addCheck;
this.web3 = options.web3;
this.embark = embark;
this.setServiceCheck();
this.addWhisperToEmbarkJS();
this.addSetProvider();
}
setServiceCheck() {
const self = this;
self.addCheck('Whisper', function (cb) {
self.web3.shh.getVersion(function (err, version) {
if (err || version == "2") {
return cb({name: 'Whisper', status: 'off'});
} else {
return cb({name: 'Whisper (version ' + version + ')', status: 'on'});
}
});
});
}
addWhisperToEmbarkJS() {
const self = this;
// TODO: make this a shouldAdd condition
if (this.communicationConfig === {}) {
return;
}
if ((this.communicationConfig.available_providers.indexOf('whisper') < 0) && (this.communicationConfig.provider !== 'whisper' || this.communicationConfig.enabled !== true)) {
return;
}
// TODO: possible race condition could be a concern
this.events.request("version:get:web3", function(web3Version) {
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'js', 'message_events.js')).toString();
if (web3Version[0] === "0") {
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'js', 'embarkjs_old_web3.js')).toString();
code += "\nEmbarkJS.Messages.registerProvider('whisper', __embarkWhisperOld);";
} else {
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'js', 'embarkjs.js')).toString();
code += "\nEmbarkJS.Messages.registerProvider('whisper', __embarkWhisperNewWeb3);";
}
self.embark.addCodeToEmbarkJS(code);
});
}
addSetProvider() {
let connection = this.communicationConfig.connection || {};
// todo: make the add code a function as well
let config = JSON.stringify({
server: connection.host || 'localhost',
port: connection.port || '8546',
type: connection.type || 'ws'
});
let code = "\nEmbarkJS.Messages.setProvider('whisper'," + config + ");";
let shouldInit = (communicationConfig) => {
return (communicationConfig.provider === 'whisper' && communicationConfig.enabled === true);
};
this.embark.addProviderInit('communication', code, shouldInit);
}
}
module.exports = Whisper;

View File

@ -0,0 +1,123 @@
/*global EmbarkJS, Web3, __MessageEvents */
// for the whisper v5 and web3.js 1.0
let __embarkWhisperNewWeb3 = {};
__embarkWhisperNewWeb3.setProvider = function (options) {
const self = this;
let provider;
if (options === undefined) {
provider = "localhost:8546";
} else {
provider = options.server + ':' + options.port;
}
// TODO: take into account type
self.web3 = new Web3(new Web3.providers.WebsocketProvider("ws://" + provider));
self.getWhisperVersion(function (err, version) {
if (err) {
console.log("whisper not available");
} else if (version >= 5) {
self.web3.shh.newSymKey().then((id) => {
self.symKeyID = id;
});
self.web3.shh.newKeyPair().then((id) => {
self.sig = id;
});
} else {
throw new Error("version of whisper not supported");
}
self.whisperVersion = self.web3.version.whisper;
});
};
__embarkWhisperNewWeb3.sendMessage = function (options) {
var topics, data, ttl, payload;
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
var powTime = options.powTime || 3;
var powTarget = options.powTarget || 0.5;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
topics = this.web3.utils.toHex(topics).slice(0, 10);
payload = JSON.stringify(data);
let message = {
symKeyID: this.symKeyID, // encrypts using the sym key ID
sig: this.sig, // signs the message using the keyPair ID
ttl: ttl,
topic: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
powTime: powTime,
powTarget: powTarget
};
this.web3.shh.post(message, function () {
});
};
__embarkWhisperNewWeb3.listenTo = function (options, callback) {
var topics = options.topic || options.topics;
let promise = new __MessageEvents();
if (typeof topics === 'string') {
topics = [this.web3.utils.toHex(topics).slice(0, 10)];
} else {
topics = topics.map((t) => this.web3.utils.toHex(t).slice(0, 10));
}
let filter = this.web3.shh.subscribe("messages", {
symKeyID: this.symKeyID,
topics: topics
}).on('data', function (result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
data = {
topic: EmbarkJS.Utils.toAscii(result.topic),
data: payload,
//from: result.from,
time: result.timestamp
};
if (callback) {
return callback(null, data);
}
promise.cb(payload, data, result);
});
promise.filter = filter;
return promise;
};
__embarkWhisperNewWeb3.getWhisperVersion = function (cb) {
this.web3.shh.getVersion(function (err, version) {
cb(err, version);
});
};
__embarkWhisperNewWeb3.isAvailable = function () {
return new Promise((resolve, reject) => {
if (!this.web3.shh) {
return resolve(false);
}
try {
this.getWhisperVersion((err) => {
resolve(Boolean(!err));
});
}
catch (err) {
reject(err);
}
});
};

View File

@ -0,0 +1,125 @@
/*global EmbarkJS, Web3, __MessageEvents */
// for the old version of whisper and web3.js
let __embarkWhisperOld = {};
__embarkWhisperOld.setProvider = function (options) {
const self = this;
let provider;
if (options === undefined) {
provider = "localhost:8546";
} else {
provider = options.server + ':' + options.port;
}
self.web3 = new Web3(new Web3.providers.HttpProvider("http://" + provider));
self.getWhisperVersion(function (err, version) {
if (err) {
console.log("whisper not available");
} else if (version >= 5) {
throw new Error("whisper 5 not supported with this version of web3.js");
} else {
self.identity = self.web3.shh.newIdentity();
}
self.whisperVersion = self.web3.version.whisper;
});
};
__embarkWhisperOld.sendMessage = function (options) {
var topics, data, ttl, priority, payload;
topics = options.topic || options.topics;
data = options.data || options.payload;
ttl = options.ttl || 100;
priority = options.priority || 1000;
var identity = options.identity || this.identity || this.web3.shh.newIdentity();
var _topics;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
_topics = [EmbarkJS.Utils.fromAscii(topics)];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
payload = JSON.stringify(data);
var message;
message = {
from: identity,
topics: topics,
payload: EmbarkJS.Utils.fromAscii(payload),
ttl: ttl,
priority: priority
};
return this.web3.shh.post(message, function () {
});
};
__embarkWhisperOld.listenTo = function (options) {
var topics, _topics;
topics = options.topic || options.topics;
_topics = [];
if (typeof topics === 'string') {
_topics = [topics];
} else {
_topics = topics.map((t) => EmbarkJS.Utils.fromAscii(t));
}
topics = _topics;
var filterOptions = {
topics: topics
};
let promise = new __MessageEvents();
let filter = this.web3.shh.filter(filterOptions, function (err, result) {
var payload = JSON.parse(EmbarkJS.Utils.toAscii(result.payload));
var data;
if (err) {
promise.error(err);
} else {
data = {
topic: topics,
data: payload,
from: result.from,
time: (new Date(result.sent * 1000))
};
promise.cb(payload, data, result);
}
});
promise.filter = filter;
return promise;
};
__embarkWhisperOld.getWhisperVersion = function (cb) {
this.web3.version.getWhisper(function (err, _res) {
cb(err, self.web3.version.whisper);
});
};
__embarkWhisperOld.isAvailable = function () {
return new Promise((resolve, reject) => {
if (!this.web3) {
return resolve(false);
}
try {
this.getWhisperVersion((err) => {
resolve(Boolean(!err));
});
}
catch (err) {
reject(err);
}
});
};

View File

@ -0,0 +1,17 @@
let __MessageEvents = function() {
this.cb = function() {};
};
__MessageEvents.prototype.then = function(cb) {
this.cb = cb;
};
__MessageEvents.prototype.error = function(err) {
return err;
};
__MessageEvents.prototype.stop = function() {
this.filter.stopWatching();
};

View File

@ -1,122 +0,0 @@
// TODO: this is horrible and needs to be refactored ASAP
let utils = require('../utils/utils.js');
let fs = require('../core/fs.js');
let o_fs = require('fs-extra');
let http = require('follow-redirects').http;
let https = require('follow-redirects').https;
var tar = require('tar');
class Npm {
constructor(options) {
this.logger = options.logger;
}
// TODO: callback should accept an error
getPackageVersion(packageName, version, returnContent, getFromGit, callback) {
let self = this;
let npmRegistry = "https://registry.npmjs.org/" + packageName + "/" + version;
utils.httpsGet(npmRegistry, function (res) {
let body = '';
res.on('data', function (d) {
body += d;
});
res.on('end', function () {
let registryJSON = JSON.parse(body);
let tarball = registryJSON.dist.tarball;
var download = function(url, dest, cb) {
var file = o_fs.createWriteStream(dest);
var request = (url.substring(0,5) === 'https' ? https : http).get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(cb); // close() is async, call cb after close completes.
});
}).on('error', function(err) { // Handle errors
fs.unlink(dest); // Delete the file async. (But we don't check the result)
if (cb) cb(err.message);
});
};
if (getFromGit) {
let repoName = registryJSON.repository.url.replace("git+https://github.com/", "").replace(".git","");
let gitHead = registryJSON.gitHead;
if (!gitHead) {
console.error("Could not download " + packageName + " " + version);
return callback("error");
}
let fileLocation = "https://raw.githubusercontent.com/" + repoName + "/" + gitHead + "/dist/web3.min.js";
let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/';
if (fs.existsSync(packageDirectory + "/" + packageName + ".js")) {
if (returnContent) {
let distFile = packageDirectory + packageName + ".js";
callback(fs.readFileSync(distFile).toString());
} else {
callback(packageDirectory);
}
} else {
fs.mkdirpSync(packageDirectory);
self.logger.info("downloading " + packageName + " " + version + "....");
download(fileLocation, packageDirectory + "/" + packageName + ".js", function() {
o_fs.createReadStream(packageDirectory + "/" + packageName + ".js").pipe(
tar.x({
strip: 1,
C: packageDirectory
}).on('end', function() {
if (returnContent) {
let distFile = packageDirectory + packageName + ".js";
callback(fs.readFileSync(distFile).toString());
} else {
callback(packageDirectory);
}
})
);
});
}
} else {
let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/';
if (fs.existsSync(packageDirectory + "/downloaded_package.tgz")) {
if (returnContent) {
let distFile = packageDirectory + returnContent;
callback(fs.readFileSync(distFile).toString());
} else {
callback(packageDirectory);
}
} else {
fs.mkdirpSync(packageDirectory);
self.logger.info("downloading " + packageName + " " + version + "....");
download(tarball, packageDirectory + "/downloaded_package.tgz", function() {
o_fs.createReadStream(packageDirectory + '/downloaded_package.tgz').pipe(
tar.x({
strip: 1,
C: packageDirectory
}).on('end', function() {
if (returnContent) {
let distFile = packageDirectory + returnContent;
callback(fs.readFileSync(distFile).toString());
} else {
callback(packageDirectory);
}
})
);
});
}
}
});
});
}
}
module.exports = Npm;

View File

@ -1,6 +1,12 @@
/*jshint esversion: 6, loopfunc: true */
let fs = require('../core/fs.js');
let async = require('async');
var utils = require('../utils/utils.js');
const webpack = require("webpack");
require("babel-preset-react");
require("babel-preset-es2015");
require("babel-preset-es2016");
require("babel-preset-es2017");
class Pipeline {
@ -8,6 +14,7 @@ class Pipeline {
this.buildDir = options.buildDir;
this.contractsFiles = options.contractsFiles;
this.assetFiles = options.assetFiles;
this.events = options.events;
this.logger = options.logger;
this.plugins = options.plugins;
}
@ -17,63 +24,100 @@ class Pipeline {
this.buildContracts(contractsJSON);
// limit:1 due to issues when downloading required files such as web3.js
async.eachOfLimit(this.assetFiles, 1, function (files, targetFile, cb) {
self.buildWeb3JS(function() {
let importsList = {};
importsList["Embark/EmbarkJS"] = fs.dappPath(".embark", 'embark.js');
importsList["Embark/web3"] = fs.dappPath(".embark", 'web3_instance.js');
self.plugins.getPluginsProperty('imports', 'imports').forEach(function (importObject) {
let [importName, importLocation] = importObject;
importsList[importName] = importLocation;
});
for (let contractName in contractsJSON) {
let contractCode = self.buildContractJS(contractName);
let filePath = fs.dappPath(".embark", contractName + '.js');
fs.writeFileSync(filePath, contractCode);
importsList["Embark/contracts/" + contractName] = filePath;
}
// limit:1 due to issues when downloading required files such as web3.js
async.eachOfLimit(self.assetFiles, 1, function (files, targetFile, cb) {
// limit:1 due to issues when downloading required files such as web3.js
async.mapLimit(files, 1,
function(file, fileCb) {
self.logger.trace("reading " + file.filename);
let pipelinePlugins = self.plugins.getPluginsFor('pipeline');
if (file.filename.indexOf('.js') >= 0) {
if (file.filename === "$ALL_CONTRACTS") {
return fileCb(null, {content: abi, filename: file.filename, path: file.path, modified: true});
} else if (file.filename === "$EMBARK_JS") {
return file.content(function(fileContent) {
return fileCb(null, {content: fileContent, filename: "embark.js", path: file.path, modified: true});
});
} else if (file.filename[0] === '$') {
let contractName = file.filename.substr(1);
return fileCb(null, {content: self.buildContractJS(contractName), filename: contractName + ".js", path: file.path, modified: true});
} else if (file.filename === 'embark.js') {
return file.content(function(fileContent) {
return fileCb(null, {content: fileContent + "\n" + abi, filename: file.filename, path: file.path, modified: true});
});
} else if (file.filename === 'abi.js') {
return fileCb(null, {content: abi, filename: file.filename, path: file.path, modified: true});
} else if (['web3.js', 'ipfs.js', 'ipfs-api.js', 'orbit.js'].indexOf(file.filename) >= 0) {
file.content(function(fileContent) {
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, modified: true});
});
} else {
let realCwd;
if (pipelinePlugins.length > 0) {
file.content(function(fileContent) {
async.eachSeries(pipelinePlugins, function(plugin, pluginCB) {
if (file.options && file.options.skipPipeline) {
return pluginCB();
}
async.waterfall([
fileContent = plugin.runPipeline({targetFile: file.filename, source: fileContent});
file.modified = true;
pluginCB();
function findImports(next) {
self.webpackRun(file.filename, {}, false, importsList, false, next);
},
function (err) {
if (err) {
self.logger.error(err.message);
function changeCwd(next) {
realCwd = utils.pwd();
process.chdir(fs.embarkPath(''));
next();
},
//function findImportsPhase2(next) {
// console.log("====> findImports_2");
// self.webpackRun(file.filename, {
// externals: function(context, request, callback) {
// if (request === utils.joinPath(fs.dappPath(), file.filename)) {
// callback();
// } else {
// //if (request.indexOf('Embark/contracts/') === 0) {
// // let contractName = request.split('/')[2];
// // let contractCode = self.buildContractJS(contractName);
// // let filePath = utils.joinPath(fs.dappPath(), ".embark", contractName + '.js');
// // fs.writeFileSync(filePath, contractCode);
// // importsList[request] = filePath;
// //}
// callback(null, "amd " + Math.random());
// }
// }
// }, true, importsList, next);
//},
function runWebpack(next) {
self.webpackRun(file.filename, {}, true, importsList, true, next);
},
function changeCwdBack(next) {
process.chdir(realCwd);
next();
}
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, modified: true});
});
], function(err, _result) {
if (err) {
process.chdir(realCwd);
self.logger.error(err);
return fileCb(err);
}
if (!fs.existsSync('./.embark/' + file.filename)) {
self.logger.error("couldn't find file: " + file.filename);
return fileCb("couldn't find file: " + file.filename);
}
let fileContent = fs.readFileSync('./.embark/' + file.filename).toString();
self.runPlugins(file, fileContent, fileCb);
});
} else {
file.content(function(fileContent) {
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, modified: true});
self.runPlugins(file, fileContent, fileCb);
});
}
}
},
function (err, contentFiles) {
if (err) {
self.logger.warn('errors found while generating ' + targetFile);
}
let dir = targetFile.split('/').slice(0, -1).join('/');
self.logger.trace("creating dir " + self.buildDir + dir);
fs.mkdirpSync(self.buildDir + dir);
@ -87,11 +131,10 @@ class Pipeline {
}
contentFiles.map(function (file) {
let filename = file.filename.replace('app/', '');
filename = filename.replace(targetDir, '');
let filename = file.filename.replace(file.basedir + '/', '');
self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);
fs.copySync(self.buildDir + targetDir + filename, file.path, {overwrite: true});
fs.copySync(file.path, self.buildDir + targetDir + filename, {overwrite: true});
});
} else {
let content = contentFiles.map(function (file) {
@ -108,35 +151,170 @@ class Pipeline {
}
);
},
function (err, results) {
function (_err, _results) {
callback();
});
});
}
runPlugins(file, fileContent, fileCb) {
const self = this;
let pipelinePlugins = self.plugins.getPluginsFor('pipeline');
if (pipelinePlugins.length <= 0) {
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
}
async.eachSeries(pipelinePlugins,
function(plugin, pluginCB) {
if (file.options && file.options.skipPipeline) {
return pluginCB();
}
fileContent = plugin.runPipeline({targetFile: file.filename, source: fileContent});
file.modified = true;
pluginCB();
},
function (err) {
if (err) {
self.logger.error(err.message);
}
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
}
);
}
webpackRun(filename, options, includeModules, importsList, detectErrors, callback) {
let defaultOptions = {
entry: fs.dappPath(filename),
output: {
libraryTarget: 'umd',
path: fs.dappPath('.embark'),
filename: filename
},
resolve: {
alias: importsList,
modules: [
fs.embarkPath('node_modules'),
fs.dappPath('node_modules')
]
},
externals: function(context, request, callback) {
callback();
}
};
let webpackOptions = utils.recursiveMerge(defaultOptions, options);
if (includeModules) {
webpackOptions.module = {
rules: [
{
test: /\.css$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.scss$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.js$/,
loader: "babel-loader",
exclude: /(node_modules|bower_components)/,
options: {
presets: ['babel-preset-es2016', 'babel-preset-es2017', 'babel-preset-react'].map(require.resolve),
plugins: ["babel-plugin-webpack-aliases"].map(require.resolve),
compact: false
}
}
]
};
}
webpack(webpackOptions).run((_err, _stats) => {
if (!detectErrors) {
return callback();
}
if (_stats.hasErrors()) {
return callback(_stats.toJson().errors.join("\n"));
}
callback();
});
}
buildContracts(contractsJSON) {
fs.mkdirpSync(this.buildDir + 'contracts');
fs.mkdirpSync(fs.dappPath(this.buildDir, 'contracts'));
for (let className in contractsJSON) {
let contract = contractsJSON[className];
fs.writeJSONSync(this.buildDir + 'contracts/' + className + ".json", contract, {spaces: 2});
fs.writeJSONSync(fs.dappPath(this.buildDir, 'contracts', className + ".json"), contract, {spaces: 2});
}
}
buildContractJS(contractName) {
let contractJSON = fs.readFileSync('dist/contracts/' + contractName + '.json').toString();
//let EmbarkJSLib = fs.readFileSync(fs.embarkPath("js/embark.js")).toString();
let contractJSON = fs.readFileSync(fs.dappPath(this.buildDir, 'contracts', contractName + '.json')).toString();
let contractCode = "";
contractCode += "import web3 from 'Embark/web3';\n";
contractCode += "import EmbarkJS from 'Embark/EmbarkJS';\n";
contractCode += "let " + contractName + "JSONConfig = " + contractJSON + ";\n";
//contractCode += EmbarkJSLib + "\n";
contractCode += "let " + contractName + " = new EmbarkJS.Contract(" + contractName + "JSONConfig);\n";
contractCode += "if (typeof module !== 'undefined' && module.exports) {\n";
contractCode += "module.exports = " + contractName + ";\n";
contractCode += "}\n";
contractCode += "\n__embarkContext.execWhenReady(function() {\n";
contractCode += "\n" + contractName + ".setProvider(web3.currentProvider);\n";
contractCode += "\n});\n";
contractCode += "export default " + contractName + ";\n";
return contractCode;
}
buildWeb3JS(cb) {
const self = this;
let code = "";
async.waterfall([
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, utils.joinPath(fs.embarkPath("js/web3-1.0.min.js")));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
}
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += "\n if (typeof web3 !== 'undefined') {";
code += "\n } else {";
code += "\n var web3 = new Web3();\n";
code += "\n }";
self.events.request('provider-code', function(providerCode) {
code += providerCode;
code += "\nglobal.__embarkContext = __mainContext.__loadManagerInstance;\n";
code += "\nwindow.web3 = web3;\n";
code += "\nexport default web3;\n";
next();
});
},
function writeFile(next) {
fs.mkdirpSync(fs.dappPath(".embark"));
fs.writeFileSync(fs.dappPath(".embark", 'web3_instance.js'), code);
next();
}
], function(_err, _result) {
cb();
});
}
}
module.exports = Pipeline;

View File

@ -1,27 +0,0 @@
let finalhandler = require('finalhandler');
let http = require('http');
let serveStatic = require('serve-static');
class Server {
constructor(options) {
this.dist = options.dist || 'dist/';
this.port = options.port || 8000;
this.hostname = options.host || 'localhost';
this.logger = options.logger;
}
start(callback) {
let serve = serveStatic(this.dist, {'index': ['index.html', 'index.htm']});
let server = http.createServer(function onRequest(req, res) {
serve(req, res, finalhandler(req, res));
});
this.logger.info("webserver available at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
server.listen(this.port, this.hostname);
callback();
}
}
module.exports = Server;

View File

@ -1,5 +1,6 @@
/*jshint esversion: 6 */
let chokidar = require('chokidar');
let path = require('path');
const _ = require('underscore');
let fs = require('../core/fs.js');
@ -9,6 +10,7 @@ class Watch {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.fileWatchers = [];
}
start() {
@ -32,15 +34,41 @@ class Watch {
this.logger.info("ready to watch file changes");
}
restart() {
this.stop();
this.start();
}
stop() {
this.fileWatchers.forEach(fileWatcher => {
fileWatcher.close();
fileWatcher = null;
});
this.fileWatchers = [];
}
watchAssets(embarkConfig, callback) {
let self = this;
let appConfig = embarkConfig.app;
let filesToWatch = [];
for (let targetFile in appConfig) {
filesToWatch.push(appConfig[targetFile]);
let files = appConfig[targetFile];
let fileGlob = files;
// workaround for imports issue
// so embark reacts to changes made in imported js files
// chokidar glob patterns only work with front-slashes
if (!Array.isArray(files)) {
fileGlob = path.join(path.dirname(files), '**', '*.*').replace(/\\/g, '/');
} else if (files.length === 1) {
fileGlob = path.join(path.dirname(files[0]), '**', '*.*').replace(/\\/g, '/');
}
filesToWatch.push(fileGlob);
}
filesToWatch = _.uniq(filesToWatch);
this.watchFiles(
filesToWatch,
function (eventName, path) {
@ -91,6 +119,7 @@ class Watch {
let configWatcher = chokidar.watch(files, {
ignored: /[\/\\]\./, persistent: true, ignoreInitial: true, followSymlinks: true
});
this.fileWatchers.push(configWatcher);
configWatcher
.on('add', path => changeCallback('add', path))

Some files were not shown because too many files have changed in this diff Show More