diff --git a/.gitignore b/.gitignore index 75be4c5f7..162b9c4d7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ test_app/.embark/development/ test_app/config/production/password test_app/node_modules/ test_app/chains.json +.idea \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index af4c1c194..793855d31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: node_js node_js: + - "7" - "6" - - "5" - - "4" addons: code_climate: repo_token: 7454b1a666015e244c384d19f48c34e35d1ae58c3aa428ec542f10bbcb848358 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..0af795717 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/Class Hierarchy.mmd b/Class Hierarchy.mmd new file mode 100644 index 000000000..d8b2c8424 --- /dev/null +++ b/Class Hierarchy.mmd @@ -0,0 +1,29 @@ +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 diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 24f23def8..5eb7ad351 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -21,8 +21,16 @@ module.exports = (grunt) -> mochaTest: test: src: ['test/**/*.js'] + jshint: - all: ['bin/embark', 'lib/**/*.js', 'js/mine.js', 'js/embark.js'] + 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) diff --git a/README.md b/README.md index ac3cab766..c834c161b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -[![Join the chat at https://gitter.im/iurimatias/embark-framework](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iurimatias/embark-framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![npm](https://img.shields.io/npm/dm/embark.svg)]() +[![Gitter](https://img.shields.io/gitter/room/iurimatias/embark-framework.svg)]() [![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) @@ -8,9 +9,9 @@ What is Embark Embark is a framework that allows you to easily develop and deploy Decentralized Applications (DApps). -A Decentralized Application is serverless html5 application that uses one or more decentralized technologies. +A Decentralized Application is a serverless html5 application that uses one or more decentralized technologies. -Embark currently integrates with EVM blockchains (Ethereum), Decentralized Storages (IPFS), and Decentralizaed communication platforms (Whisper and Orbit). Swarm is supported for deployment. +Embark currently integrates with EVM blockchains (Ethereum), Decentralized Storages (IPFS), and Decentralized communication platforms (Whisper and Orbit). Swarm is supported for deployment. With Embark you can: @@ -18,12 +19,12 @@ With Embark you can: * Automatically deploy contracts and make them available in your JS code. Embark watches for changes, and if you update a contract, Embark will automatically redeploy the contracts (if needed) and the dapp. * Contracts are available in JS with Promises. * Do Test Driven Development with Contracts using Javascript. -* Keep track of deployed contracts, deploy only when truly needed. +* Keep track of deployed contracts; deploy only when truly needed. * Manage different chains (e.g testnet, private net, livenet) * Easily manage complex systems of interdependent contracts. **Decentralized Storage (IPFS)** -* Easily Store & Retrieve Data on the DApp through EmbarkJS. Includin uploading and retrieving files. +* Easily Store & Retrieve Data on the DApp through EmbarkJS. Including uploading and retrieving files. * Deploy the full application to IPFS or Swarm. @@ -41,20 +42,20 @@ Table of Contents * [Dashboard](#dashboard) * [Creating a new DApp](#creating-a-new-dapp) * [Libraries and APIs available](#libraries-and-languages-available) -* [Using and Configuring Contracts](#dapp-structure) +* [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) +* [Deploying to IPFS](#deploying-to-ipfs-and-swarm) * [Extending Functionality with Plugins](#plugins) * [Donations](#donations) Installation ====== -Requirements: geth (1.5.8 or higher), node (6.9.1 or higher is recommended) and npm +Requirements: geth (1.6.5 or higher), node (6.9.1 or higher is recommended) and npm Optional: 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/) @@ -68,7 +69,7 @@ $ npm -g install ethereumjs-testrpc See [Complete Installation Instructions](https://github.com/iurimatias/embark-framework/wiki/Installation). -**updating from embark 1** +**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``` @@ -96,7 +97,7 @@ Alternatively, to use an ethereum rpc simulator simply run: $ 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. +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: @@ -105,7 +106,7 @@ $ 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. +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 ===== @@ -114,20 +115,20 @@ 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 enviroment you are using, and what embark is doing at the moment. +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 one of these is down then it will be displayed in red. +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. +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. +If you want to create a blank new app: ```Bash $ embark new AppName @@ -144,13 +145,16 @@ DApp Structure |___ css/ |___ js/ config/ - |___ blockchain.json #environments configuration - |___ contracts.json #contracts configuration + |___ 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 +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 ====== @@ -223,7 +227,7 @@ If you are using multiple contracts, you can pass a reference to another contrac "SimpleStorage": { "args": [ 100, - $MyStorage + "$MyStorage" ] }, "MyStorage": { @@ -233,7 +237,7 @@ If you are using multiple contracts, you can pass a reference to another contrac }, "MyMainContract": { "args": [ - $SimpleStorage + "$SimpleStorage" ] } } @@ -274,7 +278,7 @@ You can now deploy many instances of the same contract. e.g ... ``` -Contracts addresses can be defined, If an address is defined the contract wouldn't be deployed but its defined address will be used instead. +Contracts addresses can be defined. If an address is defined, Embark uses the defined address instead of deploying the contract. ```Json @@ -322,14 +326,20 @@ events: Client side deployment will be automatically available in Embark for existing contracts: ```Javascript - SimpleStorage.deploy().then(function(anotherSimpleStorage) {}); + 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().then(function(anotherMyContractObject) {}); + 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 @@ -337,7 +347,7 @@ EmbarkJS - Storage **initialization** -The current available storage is IPFS. it can be initialized as +The current available storage is IPFS. It can be initialized as ```Javascript EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'}) @@ -422,7 +432,7 @@ Tests 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. +Embark includes a testing lib to rapidly run & test your contracts in a EVM. ```Javascript # test/simple_storage_spec.js @@ -434,6 +444,7 @@ var web3 = EmbarkSpec.web3; describe("SimpleStorage", function() { before(function(done) { + this.timeout(0); var contractsConfig = { "SimpleStorage": { args: [100] @@ -459,6 +470,7 @@ describe("SimpleStorage", function() { }); }); + ``` Embark uses [Mocha](http://mochajs.org/) by default, but you can use any testing framework you want. @@ -521,20 +533,20 @@ To deploy a dapp to SWARM, all you need to do is run a local SWARM node and then Plugins ====== -It's possible to extend Embarks functionality with plugins. For example the following is possible: +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 boostrap (``embark.addFileToPipeline``) +* 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) +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 0x8811FdF0F988f0CD1B7E9DE252ABfA5b18c1cDb1 +If you like Embark, please consider donating to 0x8811FdF0F988f0CD1B7E9DE252ABfA5b18c1cDb1 diff --git a/bin/embark b/bin/embark index eb0b4d558..c9173a48c 100755 --- a/bin/embark +++ b/bin/embark @@ -1,4 +1,4 @@ #!/usr/bin/env node - -var Embark = require('..'); -Embark.process(process.argv); +var Cmd = require('../lib/cmd'); +var cli = new Cmd(); +cli.process(process.argv); diff --git a/boilerplate/app/index.html b/boilerplate/app/index.html index 3585f0803..8068e7c7b 100644 --- a/boilerplate/app/index.html +++ b/boilerplate/app/index.html @@ -6,6 +6,6 @@

Welcome to Embark!

-

See the Wiki to see what you can do with Embark!

+

See the Embark's documentation to see what you can do with Embark!

diff --git a/boilerplate/config/development/genesis.json b/boilerplate/config/development/genesis.json index 9f2d15625..8df404021 100644 --- a/boilerplate/config/development/genesis.json +++ b/boilerplate/config/development/genesis.json @@ -1,4 +1,5 @@ { + "config": {}, "nonce": "0x0000000000000042", "difficulty": "0x0", "alloc": { diff --git a/boilerplate/package.json b/boilerplate/package.json index 4802dec8c..33d7fc4b7 100644 --- a/boilerplate/package.json +++ b/boilerplate/package.json @@ -9,7 +9,7 @@ "license": "ISC", "homepage": "", "devDependencies": { - "embark": "^2.4.2", + "embark": "^2.5.0", "mocha": "^2.2.5" } } diff --git a/boilerplate/test/contract_spec.js b/boilerplate/test/contract_spec.js index ec1b87383..51518358f 100644 --- a/boilerplate/test/contract_spec.js +++ b/boilerplate/test/contract_spec.js @@ -3,8 +3,9 @@ var Embark = require('embark'); var EmbarkSpec = Embark.initTests(); var web3 = EmbarkSpec.web3; -//describe("SimpleStorage", function() { +// describe("SimpleStorage", function() { // before(function(done) { +// this.timeout(0); // var contractsConfig = { // "SimpleStorage": { // args: [100, '0x123'] @@ -29,4 +30,4 @@ var web3 = EmbarkSpec.web3; // }); // }); // -//}); +// }); diff --git a/demo/app/index.html b/demo/app/index.html index 9dcde0444..10b27f231 100644 --- a/demo/app/index.html +++ b/demo/app/index.html @@ -77,6 +77,7 @@
+

Listen To channel

diff --git a/demo/app/js/index.js b/demo/app/js/index.js index cd5863c79..7799a2f1c 100644 --- a/demo/app/js/index.js +++ b/demo/app/js/index.js @@ -32,35 +32,55 @@ $(document).ready(function() { //EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'}); $("#storage .error").hide(); - EmbarkJS.Storage.ipfsConnection.ping() + EmbarkJS.Storage.setProvider('ipfs') .then(function(){ - $("#status-storage").addClass('status-online'); - $("#storage-controls").show(); + console.log('Provider set to IPFS'); + 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(); + } + }); }) - .catch(function(err) { - if(err){ - console.log("IPFS Connection Error => " + err.message); - $("#storage .error").show(); - $("#status-storage").addClass('status-offline'); - $("#storage-controls").hide(); - } - }); + .catch(function(err){ + console.log('Failed to set IPFS as Provider:', 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); + } }); - addToLog("#storage", "EmbarkJS.Storage.saveText('" + value + "').then(function(hash) { })"); }); $("#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); + } }); - addToLog("#storage", "EmbarkJS.Storage.get('" + value + "').then(function(content) { })"); }); $("#storage button.uploadFile").click(function() { @@ -68,8 +88,13 @@ $(document).ready(function() { 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); + } }); - addToLog("#storage", "EmbarkJS.Storage.uploadFile($('input[type=file]')).then(function(hash) { })"); }); $("#storage button.loadIpfsFile").click(function() { @@ -89,11 +114,15 @@ $(document).ready(function() { $(document).ready(function() { $("#communication .error").hide(); - web3.version.getWhisper(function(err, res) { + web3.version.getWhisper(function(err, version) { if (err) { $("#communication .error").show(); $("#communication-controls").hide(); -+ $("#status-communication").addClass('status-offline'); + $("#status-communication").addClass('status-offline'); + } else if (version >= 5) { + $("#communication .errorVersion").show(); + $("#communication-controls").hide(); + $("#status-communication").addClass('status-offline'); } else { EmbarkJS.Messages.setProvider('whisper'); $("#status-communication").addClass('status-online'); diff --git a/demo/config/development/genesis.json b/demo/config/development/genesis.json index 9f2d15625..8df404021 100644 --- a/demo/config/development/genesis.json +++ b/demo/config/development/genesis.json @@ -1,4 +1,5 @@ { + "config": {}, "nonce": "0x0000000000000042", "difficulty": "0x0", "alloc": { diff --git a/demo/package.json b/demo/package.json index e169030b6..4c7b4bd5b 100644 --- a/demo/package.json +++ b/demo/package.json @@ -10,7 +10,7 @@ "license": "ISC", "homepage": "", "devDependencies": { - "embark": "^2.4.2", + "embark": "^2.5.0", "mocha": "^2.2.5" } } diff --git a/demo/test/simple_storage_spec.js b/demo/test/simple_storage_spec.js index 83ad1610f..b2d3cb178 100644 --- a/demo/test/simple_storage_spec.js +++ b/demo/test/simple_storage_spec.js @@ -5,6 +5,7 @@ var web3 = EmbarkSpec.web3; describe("SimpleStorage", function() { before(function(done) { + this.timeout(0); var contractsConfig = { "SimpleStorage": { args: [100] diff --git a/docs/conf.py b/docs/conf.py index 6cfd371b7..1544c4505 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,9 +58,9 @@ author = u'Iuri Matias' # built documents. # # The short X.Y version. -version = u'2.4' +version = u'2.5' # The full version, including alpha/beta/rc tags. -release = u'2.4.2' +release = u'2.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/embarkjs-storage.rst b/docs/embarkjs-storage.rst index a46417923..c8b9f2a4c 100644 --- a/docs/embarkjs-storage.rst +++ b/docs/embarkjs-storage.rst @@ -13,13 +13,25 @@ The current available storage is IPFS. it can be initialized as .. code:: javascript - EmbarkJS.Storage.saveText("hello world").then(function(hash) {}); + 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) {}); + EmbarkJS.Storage.get(hash) + .then(function(content) {}) + .catch(function(err) { + if(err){ + console.log("IPFS get Error => " + err.message); + } + }); **Uploading a file** @@ -30,7 +42,13 @@ The current available storage is IPFS. it can be initialized as .. code:: javascript var input = $("input[type=file"]); - EmbarkJS.Storage.uploadFile(input).then(function(hash) {}); + EmbarkJS.Storage.uploadFile(input) + .then(function(hash) {}) + .catch(function(err) { + if(err){ + console.log("IPFS uploadFile Error => " + err.message); + } + }); **Generate URL to file** diff --git a/docs/embarkjs.rst b/docs/embarkjs.rst index 50c0e7474..dd6a39fcc 100644 --- a/docs/embarkjs.rst +++ b/docs/embarkjs.rst @@ -20,11 +20,17 @@ existing contracts: .. code:: javascript - SimpleStorage.deploy().then(function(anotherSimpleStorage) {}); + 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().then(function(anotherMyContractObject) {}); + 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) {}); diff --git a/docs/using-contracts.rst b/docs/using-contracts.rst index 7c68a287d..d3cbe86ad 100644 --- a/docs/using-contracts.rst +++ b/docs/using-contracts.rst @@ -64,7 +64,7 @@ with the correct address for the contract. "SimpleStorage": { "args": [ 100, - $MyStorage + "$MyStorage" ] }, "MyStorage": { @@ -74,7 +74,7 @@ with the correct address for the contract. }, "MyMainContract": { "args": [ - $SimpleStorage + "$SimpleStorage" ] } } diff --git a/js/build/embark.bundle.js b/js/build/embark.bundle.js index 6556a58d4..5bc9091a0 100644 --- a/js/build/embark.bundle.js +++ b/js/build/embark.bundle.js @@ -48,446 +48,530 @@ var EmbarkJS = /*jshint esversion: 6 */ //var Ipfs = require('./ipfs.js'); - var EmbarkJS = { - }; + //========================================================= + // Embark Smart Contracts + //========================================================= + + var EmbarkJS = {}; EmbarkJS.Contract = function(options) { - var self = this; - var i, abiElement; + var self = this; + var i, abiElement; - this.abi = options.abi; - this.address = options.address; - this.code = '0x' + options.code; - this.web3 = options.web3 || web3; + this.abi = options.abi; + this.address = options.address; + this.code = '0x' + options.code; + this.web3 = options.web3 || web3; - var ContractClass = this.web3.eth.contract(this.abi); + var ContractClass = this.web3.eth.contract(this.abi); - this.eventList = []; + 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); + 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 getConfirmation = function() { - self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) { - if (err) { - return reject(err); - } + var messageEvents = function() { + this.cb = function() {}; + }; - if (receipt !== null) { - return resolve(receipt); - } + messageEvents.prototype.then = function(cb) { + this.cb = cb; + }; - setTimeout(getConfirmation, 1000); - }); + 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); - if (typeof(transaction) !== "string" || props.constant) { - resolve(transaction); - } else { - getConfirmation(); - } - }); + var promise = new Promise(function(resolve, reject) { + args.push(function(err, transaction) { + promise.tx = transaction; + if (err) { + return reject(err); + } - fn.apply(fn, args); - }); + var getConfirmation = function() { + self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) { + if (err) { + return reject(err); + } - return promise; - }; - return true; - } - return false; - }); + 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 || {}; + var self = this; + var contractParams; + var options = _options || {}; - contractParams = args || []; + 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})); - } + contractParams.push({ + from: this.web3.eth.accounts[0], + data: this.code, + gas: options.gas || 800000 }); - // returns promise - // deploys contract - // wraps it around EmbarkJS.Contract - contractObject["new"].apply(contractObject, contractParams); - }); + var contractObject = this.web3.eth.contract(this.abi); - - return promise; + 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.IPFS = 'ipfs'; + EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy; - EmbarkJS.Storage = { + EmbarkJS.Contract.prototype.at = function(address) { + return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address }); }; - EmbarkJS.Storage.setProvider = function(provider, options) { - if (provider === 'ipfs') { - this.currentStorage = EmbarkJS.Storage.IPFS; - if (options === undefined) { - this.ipfsConnection = IpfsApi('localhost', '5001'); - } else { - this.ipfsConnection = IpfsApi(options.server, options.port); - } + EmbarkJS.Contract.prototype.send = function(value, unit, _options) { + var options, wei; + if (typeof unit === 'object') { + options = unit; + wei = value; } else { - throw Error('unknown provider'); + 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) { - var self = this; - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } - var promise = new Promise(function(resolve, reject) { - self.ipfsConnection.add((new self.ipfsConnection.Buffer(text)), function(err, result) { - if (err) { - reject(err); - } else { - resolve(result[0].path); - } - }); - }); - - return promise; - }; - - EmbarkJS.Storage.uploadFile = function(inputSelector) { - var self = this; - var file = inputSelector[0].files[0]; - - if (file === undefined) { - throw new Error('no file found'); - } - - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } - - var promise = new Promise(function(resolve, reject) { - 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; + return this.currentStorage.saveText(text); }; EmbarkJS.Storage.get = function(hash) { - var self = this; - // TODO: detect type, then convert if needed - //var ipfsHash = web3.toAscii(hash); - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } + return this.currentStorage.get(hash); + }; - var promise = new Promise(function(resolve, reject) { - self.ipfsConnection.object.get([hash]).then(function(node) { - resolve(node.data); - }); - }); - - return promise; + EmbarkJS.Storage.uploadFile = function(inputSelector) { + return this.currentStorage.uploadFile(inputSelector); }; EmbarkJS.Storage.getUrl = function(hash) { - //var ipfsHash = web3.toAscii(hash); - - return 'http://localhost:8080/ipfs/' + hash; + return this.currentStorage.getUrl(hash); }; - EmbarkJS.Messages = { + 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'); + } else { + self.ipfsConnection = IpfsApi(options.server, options.port); + } + 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) { + //var ipfsHash = web3.toAscii(hash); + + return 'http://localhost:8080/ipfs/' + hash; + }; + + //========================================================= + // Embark Messaging + //========================================================= + + EmbarkJS.Messages = {}; + EmbarkJS.Messages.setProvider = function(provider, options) { - var self = this; - var ipfs; - if (provider === 'whisper') { - this.currentMessages = EmbarkJS.Messages.Whisper; - if (typeof variable === 'undefined' && typeof(web3) === 'undefined') { - if (options === undefined) { - web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); - } else { - web3 = new Web3(new Web3.providers.HttpProvider("http://" + options.server + ':' + options.port)); - } - } - web3.version.getWhisper(function(err, res) { - if (err) { - console.log("whisper not available"); - } else { - self.currentMessages.identity = web3.shh.newIdentity(); - } - }); - } else if (provider === 'orbit') { - this.currentMessages = EmbarkJS.Messages.Orbit; - if (options === undefined) { - ipfs = HaadIpfsApi('localhost', '5001'); + var self = this; + var ipfs; + if (provider === 'whisper') { + this.currentMessages = EmbarkJS.Messages.Whisper; + if (typeof variable === 'undefined' && typeof(web3) === 'undefined') { + if (options === undefined) { + web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + } else { + web3 = new Web3(new Web3.providers.HttpProvider("http://" + options.server + ':' + options.port)); + } + } + web3.version.getWhisper(function(err, res) { + if (err) { + console.log("whisper not available"); + } else if (web3.version.whisper >= 5) { + console.log("this version of whisper is not supported yet; try a version of geth bellow 1.6.1"); + } else { + self.currentMessages.identity = web3.shh.newIdentity(); + } + }); + } else if (provider === '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 { - ipfs = HaadIpfsApi(options.server, options.port); + throw Error('Unknown message provider'); } - this.currentMessages.orbit = new Orbit(ipfs); - this.currentMessages.orbit.connect(web3.eth.accounts[0]); - } else { - throw Error('unknown provider'); - } }; EmbarkJS.Messages.sendMessage = function(options) { - return this.currentMessages.sendMessage(options); + return this.currentMessages.sendMessage(options); }; EmbarkJS.Messages.listenTo = function(options) { - return this.currentMessages.listenTo(options); + return this.currentMessages.listenTo(options); }; - EmbarkJS.Messages.Whisper = { - }; + EmbarkJS.Messages.Whisper = {}; EmbarkJS.Messages.Whisper.sendMessage = function(options) { - var topics = options.topic || options.topics; - var data = options.data || options.payload; - var identity = options.identity || this.identity || web3.shh.newIdentity(); - var ttl = options.ttl || 100; - var priority = options.priority || 1000; - var _topics; + var topics = options.topic || options.topics; + var data = options.data || options.payload; + var identity = options.identity || this.identity || web3.shh.newIdentity(); + var ttl = options.ttl || 100; + var priority = options.priority || 1000; + var _topics; - if (topics === undefined) { - throw new Error("missing option: topic"); - } - - if (data === undefined) { - throw new Error("missing option: data"); - } - - // do fromAscii to each topics unless it's already a string - if (typeof topics === 'string') { - _topics = [web3.fromAscii(topics)]; - } else { - // TODO: replace with es6 + babel; - for (var i = 0; i < topics.length; i++) { - _topics.push(web3.fromAscii(topics[i])); + if (topics === undefined) { + throw new Error("missing option: topic"); } - } - topics = _topics; - var payload = JSON.stringify(data); + if (data === undefined) { + throw new Error("missing option: data"); + } - var message = { - from: identity, - topics: topics, - payload: web3.fromAscii(payload), - ttl: ttl, - priority: priority - }; + // do fromAscii to each topics unless it's already a string + if (typeof topics === 'string') { + _topics = [web3.fromAscii(topics)]; + } else { + // TODO: replace with es6 + babel; + for (var i = 0; i < topics.length; i++) { + _topics.push(web3.fromAscii(topics[i])); + } + } + topics = _topics; - return web3.shh.post(message, function() {}); + var payload = JSON.stringify(data); + + var message = { + from: identity, + topics: topics, + payload: web3.fromAscii(payload), + ttl: ttl, + priority: priority + }; + + return web3.shh.post(message, function() {}); }; EmbarkJS.Messages.Whisper.listenTo = function(options) { - var topics = options.topic || options.topics; - var _topics = []; + var topics = options.topic || options.topics; + var _topics = []; - if (typeof topics === 'string') { - _topics = [topics]; - } else { - // TODO: replace with es6 + babel; - for (var i = 0; i < topics.length; i++) { - _topics.push(topics[i]); - } - } - topics = _topics; - - var filterOptions = { - topics: topics - }; - - var 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(); - }; - - var promise = new messageEvents(); - - var filter = web3.shh.filter(filterOptions, function(err, result) { - var payload = JSON.parse(web3.toAscii(result.payload)); - var data; - if (err) { - promise.error(err); + if (typeof topics === 'string') { + _topics = [topics]; } else { - data = { - topic: topics, - data: payload, - from: result.from, - time: (new Date(result.sent * 1000)) - }; - promise.cb(payload, data, result); + // TODO: replace with es6 + babel; + for (var i = 0; i < topics.length; i++) { + _topics.push(topics[i]); + } } - }); + topics = _topics; - promise.filter = filter; + var filterOptions = { + topics: topics + }; - return promise; + var 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(); + }; + + var promise = new messageEvents(); + + var filter = web3.shh.filter(filterOptions, function(err, result) { + var payload = JSON.parse(web3.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 = {}; EmbarkJS.Messages.Orbit.sendMessage = function(options) { - var topics = options.topic || options.topics; - var data = options.data || options.payload; + var topics = options.topic || options.topics; + var data = options.data || options.payload; - if (topics === undefined) { - throw new Error("missing option: topic"); - } + if (topics === undefined) { + throw new Error("missing option: topic"); + } - if (data === undefined) { - throw new Error("missing option: data"); - } + 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(','); - } + if (typeof topics === 'string') { + topics = topics; + } else { + // TODO: better to just send to different channels instead + topics = topics.join(','); + } - this.orbit.join(topics); + this.orbit.join(topics); - var payload = JSON.stringify(data); + var payload = JSON.stringify(data); - this.orbit.send(topics, data); + this.orbit.send(topics, data); }; EmbarkJS.Messages.Orbit.listenTo = function(options) { - var self = this; - var topics = options.topic || options.topics; + var self = this; + var topics = options.topic || options.topics; - if (typeof topics === 'string') { - topics = topics; - } else { - topics = topics.join(','); - } + if (typeof topics === 'string') { + topics = topics; + } else { + topics = topics.join(','); + } - this.orbit.join(topics); + this.orbit.join(topics); - var messageEvents = function() { - this.cb = function() {}; - }; + var messageEvents = function() { + this.cb = function() {}; + }; - messageEvents.prototype.then = function(cb) { - this.cb = cb; - }; + messageEvents.prototype.then = function(cb) { + this.cb = cb; + }; - messageEvents.prototype.error = function(err) { - return err; - }; + messageEvents.prototype.error = function(err) { + return err; + }; - var promise = new messageEvents(); + 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); + 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 promise; }; module.exports = EmbarkJS; diff --git a/js/embark.js b/js/embark.js index d9aba4ac1..6b378cd30 100644 --- a/js/embark.js +++ b/js/embark.js @@ -1,446 +1,530 @@ /*jshint esversion: 6 */ //var Ipfs = require('./ipfs.js'); -var EmbarkJS = { -}; +//========================================================= +// Embark Smart Contracts +//========================================================= + +var EmbarkJS = {}; EmbarkJS.Contract = function(options) { - var self = this; - var i, abiElement; + var self = this; + var i, abiElement; - this.abi = options.abi; - this.address = options.address; - this.code = '0x' + options.code; - this.web3 = options.web3 || web3; + this.abi = options.abi; + this.address = options.address; + this.code = '0x' + options.code; + this.web3 = options.web3 || web3; - var ContractClass = this.web3.eth.contract(this.abi); + var ContractClass = this.web3.eth.contract(this.abi); - this.eventList = []; + 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); + 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 getConfirmation = function() { - self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) { - if (err) { - return reject(err); - } + var messageEvents = function() { + this.cb = function() {}; + }; - if (receipt !== null) { - return resolve(receipt); - } + messageEvents.prototype.then = function(cb) { + this.cb = cb; + }; - setTimeout(getConfirmation, 1000); - }); + 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); - if (typeof(transaction) !== "string" || props.constant) { - resolve(transaction); - } else { - getConfirmation(); - } - }); + var promise = new Promise(function(resolve, reject) { + args.push(function(err, transaction) { + promise.tx = transaction; + if (err) { + return reject(err); + } - fn.apply(fn, args); - }); + var getConfirmation = function() { + self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) { + if (err) { + return reject(err); + } - return promise; - }; - return true; - } - return false; - }); + 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 || {}; + var self = this; + var contractParams; + var options = _options || {}; - contractParams = args || []; + 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})); - } + contractParams.push({ + from: this.web3.eth.accounts[0], + data: this.code, + gas: options.gas || 800000 }); - // returns promise - // deploys contract - // wraps it around EmbarkJS.Contract - contractObject["new"].apply(contractObject, contractParams); - }); + var contractObject = this.web3.eth.contract(this.abi); - - return promise; + 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.IPFS = 'ipfs'; +EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy; -EmbarkJS.Storage = { +EmbarkJS.Contract.prototype.at = function(address) { + return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address }); }; -EmbarkJS.Storage.setProvider = function(provider, options) { - if (provider === 'ipfs') { - this.currentStorage = EmbarkJS.Storage.IPFS; - if (options === undefined) { - this.ipfsConnection = IpfsApi('localhost', '5001'); - } else { - this.ipfsConnection = IpfsApi(options.server, options.port); - } +EmbarkJS.Contract.prototype.send = function(value, unit, _options) { + var options, wei; + if (typeof unit === 'object') { + options = unit; + wei = value; } else { - throw Error('unknown provider'); + 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) { - var self = this; - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } - var promise = new Promise(function(resolve, reject) { - self.ipfsConnection.add((new self.ipfsConnection.Buffer(text)), function(err, result) { - if (err) { - reject(err); - } else { - resolve(result[0].path); - } - }); - }); - - return promise; -}; - -EmbarkJS.Storage.uploadFile = function(inputSelector) { - var self = this; - var file = inputSelector[0].files[0]; - - if (file === undefined) { - throw new Error('no file found'); - } - - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } - - var promise = new Promise(function(resolve, reject) { - 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; + return this.currentStorage.saveText(text); }; EmbarkJS.Storage.get = function(hash) { - var self = this; - // TODO: detect type, then convert if needed - //var ipfsHash = web3.toAscii(hash); - if (!this.ipfsConnection) { - this.setProvider('ipfs'); - } + return this.currentStorage.get(hash); +}; - var promise = new Promise(function(resolve, reject) { - self.ipfsConnection.object.get([hash]).then(function(node) { - resolve(node.data); - }); - }); - - return promise; +EmbarkJS.Storage.uploadFile = function(inputSelector) { + return this.currentStorage.uploadFile(inputSelector); }; EmbarkJS.Storage.getUrl = function(hash) { - //var ipfsHash = web3.toAscii(hash); - - return 'http://localhost:8080/ipfs/' + hash; + return this.currentStorage.getUrl(hash); }; -EmbarkJS.Messages = { +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'); + } else { + self.ipfsConnection = IpfsApi(options.server, options.port); + } + 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) { + //var ipfsHash = web3.toAscii(hash); + + return 'http://localhost:8080/ipfs/' + hash; +}; + +//========================================================= +// Embark Messaging +//========================================================= + +EmbarkJS.Messages = {}; + EmbarkJS.Messages.setProvider = function(provider, options) { - var self = this; - var ipfs; - if (provider === 'whisper') { - this.currentMessages = EmbarkJS.Messages.Whisper; - if (typeof variable === 'undefined' && typeof(web3) === 'undefined') { - if (options === undefined) { - web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); - } else { - web3 = new Web3(new Web3.providers.HttpProvider("http://" + options.server + ':' + options.port)); - } - } - web3.version.getWhisper(function(err, res) { - if (err) { - console.log("whisper not available"); - } else { - self.currentMessages.identity = web3.shh.newIdentity(); - } - }); - } else if (provider === 'orbit') { - this.currentMessages = EmbarkJS.Messages.Orbit; - if (options === undefined) { - ipfs = HaadIpfsApi('localhost', '5001'); + var self = this; + var ipfs; + if (provider === 'whisper') { + this.currentMessages = EmbarkJS.Messages.Whisper; + if (typeof variable === 'undefined' && typeof(web3) === 'undefined') { + if (options === undefined) { + web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + } else { + web3 = new Web3(new Web3.providers.HttpProvider("http://" + options.server + ':' + options.port)); + } + } + web3.version.getWhisper(function(err, res) { + if (err) { + console.log("whisper not available"); + } else if (web3.version.whisper >= 5) { + console.log("this version of whisper is not supported yet; try a version of geth bellow 1.6.1"); + } else { + self.currentMessages.identity = web3.shh.newIdentity(); + } + }); + } else if (provider === '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 { - ipfs = HaadIpfsApi(options.server, options.port); + throw Error('Unknown message provider'); } - this.currentMessages.orbit = new Orbit(ipfs); - this.currentMessages.orbit.connect(web3.eth.accounts[0]); - } else { - throw Error('unknown provider'); - } }; EmbarkJS.Messages.sendMessage = function(options) { - return this.currentMessages.sendMessage(options); + return this.currentMessages.sendMessage(options); }; EmbarkJS.Messages.listenTo = function(options) { - return this.currentMessages.listenTo(options); + return this.currentMessages.listenTo(options); }; -EmbarkJS.Messages.Whisper = { -}; +EmbarkJS.Messages.Whisper = {}; EmbarkJS.Messages.Whisper.sendMessage = function(options) { - var topics = options.topic || options.topics; - var data = options.data || options.payload; - var identity = options.identity || this.identity || web3.shh.newIdentity(); - var ttl = options.ttl || 100; - var priority = options.priority || 1000; - var _topics; + var topics = options.topic || options.topics; + var data = options.data || options.payload; + var identity = options.identity || this.identity || web3.shh.newIdentity(); + var ttl = options.ttl || 100; + var priority = options.priority || 1000; + var _topics; - if (topics === undefined) { - throw new Error("missing option: topic"); - } - - if (data === undefined) { - throw new Error("missing option: data"); - } - - // do fromAscii to each topics unless it's already a string - if (typeof topics === 'string') { - _topics = [web3.fromAscii(topics)]; - } else { - // TODO: replace with es6 + babel; - for (var i = 0; i < topics.length; i++) { - _topics.push(web3.fromAscii(topics[i])); + if (topics === undefined) { + throw new Error("missing option: topic"); } - } - topics = _topics; - var payload = JSON.stringify(data); + if (data === undefined) { + throw new Error("missing option: data"); + } - var message = { - from: identity, - topics: topics, - payload: web3.fromAscii(payload), - ttl: ttl, - priority: priority - }; + // do fromAscii to each topics unless it's already a string + if (typeof topics === 'string') { + _topics = [web3.fromAscii(topics)]; + } else { + // TODO: replace with es6 + babel; + for (var i = 0; i < topics.length; i++) { + _topics.push(web3.fromAscii(topics[i])); + } + } + topics = _topics; - return web3.shh.post(message, function() {}); + var payload = JSON.stringify(data); + + var message = { + from: identity, + topics: topics, + payload: web3.fromAscii(payload), + ttl: ttl, + priority: priority + }; + + return web3.shh.post(message, function() {}); }; EmbarkJS.Messages.Whisper.listenTo = function(options) { - var topics = options.topic || options.topics; - var _topics = []; + var topics = options.topic || options.topics; + var _topics = []; - if (typeof topics === 'string') { - _topics = [topics]; - } else { - // TODO: replace with es6 + babel; - for (var i = 0; i < topics.length; i++) { - _topics.push(topics[i]); - } - } - topics = _topics; - - var filterOptions = { - topics: topics - }; - - var 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(); - }; - - var promise = new messageEvents(); - - var filter = web3.shh.filter(filterOptions, function(err, result) { - var payload = JSON.parse(web3.toAscii(result.payload)); - var data; - if (err) { - promise.error(err); + if (typeof topics === 'string') { + _topics = [topics]; } else { - data = { - topic: topics, - data: payload, - from: result.from, - time: (new Date(result.sent * 1000)) - }; - promise.cb(payload, data, result); + // TODO: replace with es6 + babel; + for (var i = 0; i < topics.length; i++) { + _topics.push(topics[i]); + } } - }); + topics = _topics; - promise.filter = filter; + var filterOptions = { + topics: topics + }; - return promise; + var 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(); + }; + + var promise = new messageEvents(); + + var filter = web3.shh.filter(filterOptions, function(err, result) { + var payload = JSON.parse(web3.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 = {}; EmbarkJS.Messages.Orbit.sendMessage = function(options) { - var topics = options.topic || options.topics; - var data = options.data || options.payload; + var topics = options.topic || options.topics; + var data = options.data || options.payload; - if (topics === undefined) { - throw new Error("missing option: topic"); - } + if (topics === undefined) { + throw new Error("missing option: topic"); + } - if (data === undefined) { - throw new Error("missing option: data"); - } + 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(','); - } + if (typeof topics === 'string') { + topics = topics; + } else { + // TODO: better to just send to different channels instead + topics = topics.join(','); + } - this.orbit.join(topics); + this.orbit.join(topics); - var payload = JSON.stringify(data); + var payload = JSON.stringify(data); - this.orbit.send(topics, data); + this.orbit.send(topics, data); }; EmbarkJS.Messages.Orbit.listenTo = function(options) { - var self = this; - var topics = options.topic || options.topics; + var self = this; + var topics = options.topic || options.topics; - if (typeof topics === 'string') { - topics = topics; - } else { - topics = topics.join(','); - } + if (typeof topics === 'string') { + topics = topics; + } else { + topics = topics.join(','); + } - this.orbit.join(topics); + this.orbit.join(topics); - var messageEvents = function() { - this.cb = function() {}; - }; + var messageEvents = function() { + this.cb = function() {}; + }; - messageEvents.prototype.then = function(cb) { - this.cb = cb; - }; + messageEvents.prototype.then = function(cb) { + this.cb = cb; + }; - messageEvents.prototype.error = function(err) { - return err; - }; + messageEvents.prototype.error = function(err) { + return err; + }; - var promise = new messageEvents(); + 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); + 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 promise; }; module.exports = EmbarkJS; diff --git a/js/web3.js b/js/web3.js index b576349a4..416e79715 100644 --- a/js/web3.js +++ b/js/web3.js @@ -1842,7 +1842,7 @@ module.exports = function (value, options) { }; -},{"crypto-js":59,"crypto-js/sha3":80}],20:[function(require,module,exports){ +},{"crypto-js":58,"crypto-js/sha3":79}],20:[function(require,module,exports){ /* This file is part of web3.js. @@ -1884,7 +1884,7 @@ var sha3 = require('./sha3.js'); var utf8 = require('utf8'); var unitMap = { - 'noether': '0', + 'noether': '0', 'wei': '1', 'kwei': '1000', 'Kwei': '1000', @@ -2104,7 +2104,7 @@ var toHex = function (val) { if (isBigNumber(val)) return fromDecimal(val); - if (isObject(val)) + if (typeof val === 'object') return fromUtf8(JSON.stringify(val)); // if its a negative number, pass it through fromDecimal @@ -2258,8 +2258,6 @@ var isAddress = function (address) { } }; - - /** * Checks if the given string is a checksummed address * @@ -2267,18 +2265,18 @@ var isAddress = function (address) { * @param {String} address the given HEX adress * @return {Boolean} */ -var isChecksumAddress = function (address) { +var isChecksumAddress = function (address) { // Check each case address = address.replace('0x',''); var addressHash = sha3(address.toLowerCase()); - for (var i = 0; i < 40; i++ ) { + for (var i = 0; i < 40; i++ ) { // the nth letter should be uppercase if the nth digit of casemap is 1 if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) { return false; } } - return true; + return true; }; @@ -2290,15 +2288,15 @@ var isChecksumAddress = function (address) { * @param {String} address the given HEX adress * @return {String} */ -var toChecksumAddress = function (address) { +var toChecksumAddress = function (address) { if (typeof address === 'undefined') return ''; address = address.toLowerCase().replace('0x',''); var addressHash = sha3(address); var checksumAddress = '0x'; - for (var i = 0; i < address.length; i++ ) { - // If ith character is 9 to f then make it uppercase + for (var i = 0; i < address.length; i++ ) { + // If ith character is 9 to f then make it uppercase if (parseInt(addressHash[i], 16) > 7) { checksumAddress += address[i].toUpperCase(); } else { @@ -2370,7 +2368,7 @@ var isFunction = function (object) { * @return {Boolean} */ var isObject = function (object) { - return typeof object === 'object'; + return object !== null && !(object instanceof Array) && typeof object === 'object'; }; /** @@ -2410,6 +2408,38 @@ var isJson = function (str) { } }; +/** + * Returns true if given string is a valid Ethereum block header bloom. + * + * @method isBloom + * @param {String} hex encoded bloom filter + * @return {Boolean} + */ +var isBloom = function (bloom) { + if (!/^(0x)?[0-9a-f]{512}$/i.test(bloom)) { + return false; + } else if (/^(0x)?[0-9a-f]{512}$/.test(bloom) || /^(0x)?[0-9A-F]{512}$/.test(bloom)) { + return true; + } + return false; +}; + +/** + * Returns true if given string is a valid log topic. + * + * @method isTopic + * @param {String} hex encoded topic + * @return {Boolean} + */ +var isTopic = function (topic) { + if (!/^(0x)?[0-9a-f]{64}$/i.test(topic)) { + return false; + } else if (/^(0x)?[0-9a-f]{64}$/.test(topic) || /^(0x)?[0-9A-F]{64}$/.test(topic)) { + return true; + } + return false; +}; + module.exports = { padLeft: padLeft, padRight: padRight, @@ -2438,12 +2468,14 @@ module.exports = { isObject: isObject, isBoolean: isBoolean, isArray: isArray, - isJson: isJson + isJson: isJson, + isBloom: isBloom, + isTopic: isTopic, }; -},{"./sha3.js":19,"bignumber.js":"bignumber.js","utf8":85}],21:[function(require,module,exports){ +},{"./sha3.js":19,"bignumber.js":"bignumber.js","utf8":84}],21:[function(require,module,exports){ module.exports={ - "version": "0.18.2" + "version": "0.19.0" } },{}],22:[function(require,module,exports){ @@ -2549,6 +2581,8 @@ Web3.prototype.isAddress = utils.isAddress; Web3.prototype.isChecksumAddress = utils.isChecksumAddress; Web3.prototype.toChecksumAddress = utils.toChecksumAddress; Web3.prototype.isIBAN = utils.isIBAN; +Web3.prototype.padLeft = utils.padLeft; +Web3.prototype.padRight = utils.padRight; Web3.prototype.sha3 = function(string, options) { @@ -2957,7 +2991,7 @@ var ContractFactory = function (eth, abi) { if (options.value > 0) { var constructorAbi = abi.filter(function (json) { return json.type === 'constructor' && json.inputs.length === args.length; - })[0] || {}; + })[0] || {}; if (!constructorAbi.payable) { throw new Error('Cannot send value to non-payable constructor'); @@ -3092,8 +3126,11 @@ module.exports = ContractFactory; */ module.exports = { - InvalidNumberOfParams: function () { - return new Error('Invalid number of input parameters'); + InvalidNumberOfSolidityArgs: function () { + return new Error('Invalid number of arguments to Solidity function'); + }, + InvalidNumberOfRPCParams: function () { + return new Error('Invalid number of input parameters to RPC method'); }, InvalidConnection: function (host){ return new Error('CONNECTION ERROR: Couldn\'t connect to node '+ host +'.'); @@ -3523,7 +3560,9 @@ var Filter = function (requestManager, options, methods, formatter, callback, fi self.callbacks.forEach(function(cb){ cb(error); }); - filterCreationErrorCallback(error); + if (typeof filterCreationErrorCallback === 'function') { + filterCreationErrorCallback(error); + } } else { self.filterId = id; @@ -3937,6 +3976,7 @@ module.exports = { var coder = require('../solidity/coder'); var utils = require('../utils/utils'); +var errors = require('./errors'); var formatters = require('./formatters'); var sha3 = require('../utils/sha3'); @@ -3969,6 +4009,23 @@ SolidityFunction.prototype.extractDefaultBlock = function (args) { } }; +/** + * Should be called to check if the number of arguments is correct + * + * @method validateArgs + * @param {Array} arguments + * @throws {Error} if it is not + */ +SolidityFunction.prototype.validateArgs = function (args) { + var inputArgs = args.filter(function (a) { + // filter the options object but not arguments that are arrays + return !(utils.isObject(a) === true && utils.isArray(a) === false); + }); + if (inputArgs.length !== this._inputTypes.length) { + throw errors.InvalidNumberOfSolidityArgs(); + } +}; + /** * Should be used to create payload from arguments * @@ -3981,6 +4038,7 @@ SolidityFunction.prototype.toPayload = function (args) { if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { options = args[args.length - 1]; } + this.validateArgs(args); options.to = this._address; options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); return options; @@ -4175,8 +4233,7 @@ SolidityFunction.prototype.attachToContract = function (contract) { module.exports = SolidityFunction; - -},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./formatters":30}],32:[function(require,module,exports){ +},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./errors":26,"./formatters":30}],32:[function(require,module,exports){ /* This file is part of web3.js. @@ -4331,7 +4388,7 @@ HttpProvider.prototype.isConnected = function() { module.exports = HttpProvider; -},{"./errors":26,"xhr2":86,"xmlhttprequest":17}],33:[function(require,module,exports){ +},{"./errors":26,"xhr2":85,"xmlhttprequest":17}],33:[function(require,module,exports){ /* This file is part of web3.js. @@ -4928,7 +4985,7 @@ Method.prototype.extractCallback = function (args) { */ Method.prototype.validateArgs = function (args) { if (args.length !== this.params) { - throw errors.InvalidNumberOfParams(); + throw errors.InvalidNumberOfRPCParams(); } }; @@ -5022,7 +5079,6 @@ Method.prototype.request = function () { module.exports = Method; - },{"../utils/utils":20,"./errors":26}],37:[function(require,module,exports){ /* This file is part of web3.js. @@ -5155,12 +5211,12 @@ function Eth(web3) { var self = this; - methods().forEach(function(method) { + methods().forEach(function(method) { method.attachToObject(self); method.setRequestManager(self._requestManager); }); - properties().forEach(function(p) { + properties().forEach(function(p) { p.attachToObject(self); p.setRequestManager(self._requestManager); }); @@ -5296,6 +5352,13 @@ var methods = function () { inputFormatter: [formatters.inputTransactionFormatter] }); + var signTransaction = new Method({ + name: 'signTransaction', + call: 'eth_signTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] + }); + var sign = new Method({ name: 'sign', call: 'eth_sign', @@ -5364,6 +5427,7 @@ var methods = function () { call, estimateGas, sendRawTransaction, + signTransaction, sendTransaction, sign, compileSolidity, @@ -5421,8 +5485,8 @@ Eth.prototype.contract = function (abi) { return factory; }; -Eth.prototype.filter = function (fil, callback) { - return new Filter(this._requestManager, fil, watches.eth(), formatters.outputLogFormatter, callback); +Eth.prototype.filter = function (fil, callback, filterCreationErrorCallback) { + return new Filter(this._requestManager, fil, watches.eth(), formatters.outputLogFormatter, callback, filterCreationErrorCallback); }; Eth.prototype.namereg = function () { @@ -5439,7 +5503,6 @@ Eth.prototype.isSyncing = function (callback) { module.exports = Eth; - },{"../../utils/config":18,"../../utils/utils":20,"../contract":25,"../filter":29,"../formatters":30,"../iban":33,"../method":36,"../namereg":44,"../property":45,"../syncing":48,"../transfer":49,"./watches":43}],39:[function(require,module,exports){ /* This file is part of web3.js. @@ -5548,6 +5611,25 @@ var methods = function () { inputFormatter: [null] }); + var importRawKey = new Method({ + name: 'importRawKey', + call: 'personal_importRawKey', + params: 2 + }); + + var sign = new Method({ + name: 'sign', + call: 'personal_sign', + params: 3, + inputFormatter: [null, formatters.inputAddressFormatter, null] + }); + + var ecRecover = new Method({ + name: 'ecRecover', + call: 'personal_ecRecover', + params: 2 + }); + var unlockAccount = new Method({ name: 'unlockAccount', call: 'personal_unlockAccount', @@ -5571,7 +5653,10 @@ var methods = function () { return [ newAccount, + importRawKey, unlockAccount, + ecRecover, + sign, sendTransaction, lockAccount ]; @@ -6595,8 +6680,6 @@ module.exports = transfer; },{"../contracts/SmartExchange.json":3,"./iban":33}],50:[function(require,module,exports){ - -},{}],51:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -6829,7 +6912,7 @@ module.exports = transfer; return CryptoJS.AES; })); -},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],52:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],51:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -7705,7 +7788,7 @@ module.exports = transfer; })); -},{"./core":53}],53:[function(require,module,exports){ +},{"./core":52}],52:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -8466,7 +8549,7 @@ module.exports = transfer; return CryptoJS; })); -},{}],54:[function(require,module,exports){ +},{}],53:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -8602,7 +8685,7 @@ module.exports = transfer; return CryptoJS.enc.Base64; })); -},{"./core":53}],55:[function(require,module,exports){ +},{"./core":52}],54:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -8752,7 +8835,7 @@ module.exports = transfer; return CryptoJS.enc.Utf16; })); -},{"./core":53}],56:[function(require,module,exports){ +},{"./core":52}],55:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -8885,7 +8968,7 @@ module.exports = transfer; return CryptoJS.EvpKDF; })); -},{"./core":53,"./hmac":58,"./sha1":77}],57:[function(require,module,exports){ +},{"./core":52,"./hmac":57,"./sha1":76}],56:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -8952,7 +9035,7 @@ module.exports = transfer; return CryptoJS.format.Hex; })); -},{"./cipher-core":52,"./core":53}],58:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],57:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -9096,7 +9179,7 @@ module.exports = transfer; })); -},{"./core":53}],59:[function(require,module,exports){ +},{"./core":52}],58:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9115,7 +9198,7 @@ module.exports = transfer; return CryptoJS; })); -},{"./aes":51,"./cipher-core":52,"./core":53,"./enc-base64":54,"./enc-utf16":55,"./evpkdf":56,"./format-hex":57,"./hmac":58,"./lib-typedarrays":60,"./md5":61,"./mode-cfb":62,"./mode-ctr":64,"./mode-ctr-gladman":63,"./mode-ecb":65,"./mode-ofb":66,"./pad-ansix923":67,"./pad-iso10126":68,"./pad-iso97971":69,"./pad-nopadding":70,"./pad-zeropadding":71,"./pbkdf2":72,"./rabbit":74,"./rabbit-legacy":73,"./rc4":75,"./ripemd160":76,"./sha1":77,"./sha224":78,"./sha256":79,"./sha3":80,"./sha384":81,"./sha512":82,"./tripledes":83,"./x64-core":84}],60:[function(require,module,exports){ +},{"./aes":50,"./cipher-core":51,"./core":52,"./enc-base64":53,"./enc-utf16":54,"./evpkdf":55,"./format-hex":56,"./hmac":57,"./lib-typedarrays":59,"./md5":60,"./mode-cfb":61,"./mode-ctr":63,"./mode-ctr-gladman":62,"./mode-ecb":64,"./mode-ofb":65,"./pad-ansix923":66,"./pad-iso10126":67,"./pad-iso97971":68,"./pad-nopadding":69,"./pad-zeropadding":70,"./pbkdf2":71,"./rabbit":73,"./rabbit-legacy":72,"./rc4":74,"./ripemd160":75,"./sha1":76,"./sha224":77,"./sha256":78,"./sha3":79,"./sha384":80,"./sha512":81,"./tripledes":82,"./x64-core":83}],59:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -9192,7 +9275,7 @@ module.exports = transfer; return CryptoJS.lib.WordArray; })); -},{"./core":53}],61:[function(require,module,exports){ +},{"./core":52}],60:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -9461,7 +9544,7 @@ module.exports = transfer; return CryptoJS.MD5; })); -},{"./core":53}],62:[function(require,module,exports){ +},{"./core":52}],61:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9540,7 +9623,7 @@ module.exports = transfer; return CryptoJS.mode.CFB; })); -},{"./cipher-core":52,"./core":53}],63:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],62:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9657,7 +9740,7 @@ module.exports = transfer; return CryptoJS.mode.CTRGladman; })); -},{"./cipher-core":52,"./core":53}],64:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],63:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9716,7 +9799,7 @@ module.exports = transfer; return CryptoJS.mode.CTR; })); -},{"./cipher-core":52,"./core":53}],65:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],64:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9757,7 +9840,7 @@ module.exports = transfer; return CryptoJS.mode.ECB; })); -},{"./cipher-core":52,"./core":53}],66:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],65:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9812,7 +9895,7 @@ module.exports = transfer; return CryptoJS.mode.OFB; })); -},{"./cipher-core":52,"./core":53}],67:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],66:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9862,7 +9945,7 @@ module.exports = transfer; return CryptoJS.pad.Ansix923; })); -},{"./cipher-core":52,"./core":53}],68:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],67:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9907,7 +9990,7 @@ module.exports = transfer; return CryptoJS.pad.Iso10126; })); -},{"./cipher-core":52,"./core":53}],69:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],68:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9948,7 +10031,7 @@ module.exports = transfer; return CryptoJS.pad.Iso97971; })); -},{"./cipher-core":52,"./core":53}],70:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],69:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -9979,7 +10062,7 @@ module.exports = transfer; return CryptoJS.pad.NoPadding; })); -},{"./cipher-core":52,"./core":53}],71:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],70:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -10025,7 +10108,7 @@ module.exports = transfer; return CryptoJS.pad.ZeroPadding; })); -},{"./cipher-core":52,"./core":53}],72:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52}],71:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -10171,7 +10254,7 @@ module.exports = transfer; return CryptoJS.PBKDF2; })); -},{"./core":53,"./hmac":58,"./sha1":77}],73:[function(require,module,exports){ +},{"./core":52,"./hmac":57,"./sha1":76}],72:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -10362,7 +10445,7 @@ module.exports = transfer; return CryptoJS.RabbitLegacy; })); -},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],74:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],73:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -10555,7 +10638,7 @@ module.exports = transfer; return CryptoJS.Rabbit; })); -},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],75:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],74:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -10695,7 +10778,7 @@ module.exports = transfer; return CryptoJS.RC4; })); -},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],76:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],75:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -10963,7 +11046,7 @@ module.exports = transfer; return CryptoJS.RIPEMD160; })); -},{"./core":53}],77:[function(require,module,exports){ +},{"./core":52}],76:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -11114,7 +11197,7 @@ module.exports = transfer; return CryptoJS.SHA1; })); -},{"./core":53}],78:[function(require,module,exports){ +},{"./core":52}],77:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -11195,7 +11278,7 @@ module.exports = transfer; return CryptoJS.SHA224; })); -},{"./core":53,"./sha256":79}],79:[function(require,module,exports){ +},{"./core":52,"./sha256":78}],78:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -11395,7 +11478,7 @@ module.exports = transfer; return CryptoJS.SHA256; })); -},{"./core":53}],80:[function(require,module,exports){ +},{"./core":52}],79:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -11719,7 +11802,7 @@ module.exports = transfer; return CryptoJS.SHA3; })); -},{"./core":53,"./x64-core":84}],81:[function(require,module,exports){ +},{"./core":52,"./x64-core":83}],80:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -11803,7 +11886,7 @@ module.exports = transfer; return CryptoJS.SHA384; })); -},{"./core":53,"./sha512":82,"./x64-core":84}],82:[function(require,module,exports){ +},{"./core":52,"./sha512":81,"./x64-core":83}],81:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -12127,7 +12210,7 @@ module.exports = transfer; return CryptoJS.SHA512; })); -},{"./core":53,"./x64-core":84}],83:[function(require,module,exports){ +},{"./core":52,"./x64-core":83}],82:[function(require,module,exports){ ;(function (root, factory, undef) { if (typeof exports === "object") { // CommonJS @@ -12898,7 +12981,7 @@ module.exports = transfer; return CryptoJS.TripleDES; })); -},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],84:[function(require,module,exports){ +},{"./cipher-core":51,"./core":52,"./enc-base64":53,"./evpkdf":55,"./md5":60}],83:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS @@ -13203,7 +13286,7 @@ module.exports = transfer; return CryptoJS; })); -},{"./core":53}],85:[function(require,module,exports){ +},{"./core":52}],84:[function(require,module,exports){ /*! https://mths.be/utf8js v2.1.2 by @mathias */ ;(function(root) { @@ -13449,2695 +13532,2746 @@ module.exports = transfer; }(this)); -},{}],86:[function(require,module,exports){ +},{}],85:[function(require,module,exports){ module.exports = XMLHttpRequest; },{}],"bignumber.js":[function(require,module,exports){ -/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */ - -;(function (global) { - 'use strict'; - - /* - bignumber.js v2.0.7 - A JavaScript library for arbitrary-precision arithmetic. - https://github.com/MikeMcl/bignumber.js - Copyright (c) 2015 Michael Mclaughlin - MIT Expat Licence - */ - - - var BigNumber, crypto, parseNumeric, - isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, - mathceil = Math.ceil, - mathfloor = Math.floor, - notBool = ' not a boolean or binary digit', - roundingMode = 'rounding mode', - tooManyDigits = 'number type has more than 15 significant digits', - ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', - BASE = 1e14, - LOG_BASE = 14, - MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 - // MAX_INT32 = 0x7fffffff, // 2^31 - 1 - POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], - SQRT_BASE = 1e7, - - /* - * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and - * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an - * exception is thrown (if ERRORS is true). - */ - MAX = 1E9; // 0 to MAX_INT32 - - - /* - * Create and return a BigNumber constructor. - */ - function another(configObj) { - var div, - - // id tracks the caller function, so its name can be included in error messages. - id = 0, - P = BigNumber.prototype, - ONE = new BigNumber(1), - - - /********************************* EDITABLE DEFAULTS **********************************/ - - - /* - * The default values below must be integers within the inclusive ranges stated. - * The values can also be changed at run-time using BigNumber.config. - */ - - // The maximum number of decimal places for operations involving division. - DECIMAL_PLACES = 20, // 0 to MAX - - /* - * The rounding mode used when rounding to the above decimal places, and when using - * toExponential, toFixed, toFormat and toPrecision, and round (default value). - * UP 0 Away from zero. - * DOWN 1 Towards zero. - * CEIL 2 Towards +Infinity. - * FLOOR 3 Towards -Infinity. - * HALF_UP 4 Towards nearest neighbour. If equidistant, up. - * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. - * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. - * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. - * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. - */ - ROUNDING_MODE = 4, // 0 to 8 - - // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] - - // The exponent value at and beneath which toString returns exponential notation. - // Number type: -7 - TO_EXP_NEG = -7, // 0 to -MAX - - // The exponent value at and above which toString returns exponential notation. - // Number type: 21 - TO_EXP_POS = 21, // 0 to MAX - - // RANGE : [MIN_EXP, MAX_EXP] - - // The minimum exponent value, beneath which underflow to zero occurs. - // Number type: -324 (5e-324) - MIN_EXP = -1e7, // -1 to -MAX - - // The maximum exponent value, above which overflow to Infinity occurs. - // Number type: 308 (1.7976931348623157e+308) - // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. - MAX_EXP = 1e7, // 1 to MAX - - // Whether BigNumber Errors are ever thrown. - ERRORS = true, // true or false - - // Change to intValidatorNoErrors if ERRORS is false. - isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors - - // Whether to use cryptographically-secure random number generation, if available. - CRYPTO = false, // true or false - - /* - * The modulo mode used when calculating the modulus: a mod n. - * The quotient (q = a / n) is calculated according to the corresponding rounding mode. - * The remainder (r) is calculated as: r = a - n * q. - * - * UP 0 The remainder is positive if the dividend is negative, else is negative. - * DOWN 1 The remainder has the same sign as the dividend. - * This modulo mode is commonly known as 'truncated division' and is - * equivalent to (a % n) in JavaScript. - * FLOOR 3 The remainder has the same sign as the divisor (Python %). - * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. - * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). - * The remainder is always positive. - * - * The truncated division, floored division, Euclidian division and IEEE 754 remainder - * modes are commonly used for the modulus operation. - * Although the other rounding modes can also be used, they may not give useful results. - */ - MODULO_MODE = 1, // 0 to 9 - - // The maximum number of significant digits of the result of the toPower operation. - // If POW_PRECISION is 0, there will be unlimited significant digits. - POW_PRECISION = 100, // 0 to MAX - - // The format specification used by the BigNumber.prototype.toFormat method. - FORMAT = { - decimalSeparator: '.', - groupSeparator: ',', - groupSize: 3, - secondaryGroupSize: 0, - fractionGroupSeparator: '\xA0', // non-breaking space - fractionGroupSize: 0 - }; - - - /******************************************************************************************/ - - - // CONSTRUCTOR - - - /* - * The BigNumber constructor and exported function. - * Create and return a new instance of a BigNumber object. - * - * n {number|string|BigNumber} A numeric value. - * [b] {number} The base of n. Integer, 2 to 64 inclusive. - */ - function BigNumber( n, b ) { - var c, e, i, num, len, str, - x = this; - - // Enable constructor usage without new. - if ( !( x instanceof BigNumber ) ) { - - // 'BigNumber() constructor call without new: {n}' - if (ERRORS) raise( 26, 'constructor call without new', n ); - return new BigNumber( n, b ); - } - - // 'new BigNumber() base not an integer: {b}' - // 'new BigNumber() base out of range: {b}' - if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { - - // Duplicate. - if ( n instanceof BigNumber ) { - x.s = n.s; - x.e = n.e; - x.c = ( n = n.c ) ? n.slice() : n; - id = 0; - return; - } - - if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { - x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; - - // Fast path for integers. - if ( n === ~~n ) { - for ( e = 0, i = n; i >= 10; i /= 10, e++ ); - x.e = e; - x.c = [n]; - id = 0; - return; - } - - str = n + ''; - } else { - if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - } else { - b = b | 0; - str = n + ''; - - // Ensure return value is rounded to DECIMAL_PLACES as with other bases. - // Allow exponential notation to be used with base 10 argument. - if ( b == 10 ) { - x = new BigNumber( n instanceof BigNumber ? n : str ); - return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); - } - - // Avoid potential interpretation of Infinity and NaN as base 44+ values. - // Any number in exponential form will fail due to the [Ee][+-]. - if ( ( num = typeof n == 'number' ) && n * 0 != 0 || - !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + - '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { - return parseNumeric( x, str, num, b ); - } - - if (num) { - x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; - - if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { - - // 'new BigNumber() number type has more than 15 significant digits: {n}' - raise( id, tooManyDigits, n ); - } - - // Prevent later check for length on converted number. - num = false; - } else { - x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; - } - - str = convertBase( str, 10, b, x.s ); - } - - // Decimal point? - if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); - - // Exponential form? - if ( ( i = str.search( /e/i ) ) > 0 ) { - - // Determine exponent. - if ( e < 0 ) e = i; - e += +str.slice( i + 1 ); - str = str.substring( 0, i ); - } else if ( e < 0 ) { - - // Integer. - e = str.length; - } - - // Determine leading zeros. - for ( i = 0; str.charCodeAt(i) === 48; i++ ); - - // Determine trailing zeros. - for ( len = str.length; str.charCodeAt(--len) === 48; ); - str = str.slice( i, len + 1 ); - - if (str) { - len = str.length; - - // Disallow numbers with over 15 significant digits if number type. - // 'new BigNumber() number type has more than 15 significant digits: {n}' - if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); - - e = e - i - 1; - - // Overflow? - if ( e > MAX_EXP ) { - - // Infinity. - x.c = x.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - x.c = [ x.e = 0 ]; - } else { - x.e = e; - x.c = []; - - // Transform base - - // e is the base 10 exponent. - // i is where to slice str to get the first element of the coefficient array. - i = ( e + 1 ) % LOG_BASE; - if ( e < 0 ) i += LOG_BASE; - - if ( i < len ) { - if (i) x.c.push( +str.slice( 0, i ) ); - - for ( len -= LOG_BASE; i < len; ) { - x.c.push( +str.slice( i, i += LOG_BASE ) ); - } - - str = str.slice(i); - i = LOG_BASE - str.length; - } else { - i -= len; - } - - for ( ; i--; str += '0' ); - x.c.push( +str ); - } - } else { - - // Zero. - x.c = [ x.e = 0 ]; - } - - id = 0; - } - - - // CONSTRUCTOR PROPERTIES - - - BigNumber.another = another; - - BigNumber.ROUND_UP = 0; - BigNumber.ROUND_DOWN = 1; - BigNumber.ROUND_CEIL = 2; - BigNumber.ROUND_FLOOR = 3; - BigNumber.ROUND_HALF_UP = 4; - BigNumber.ROUND_HALF_DOWN = 5; - BigNumber.ROUND_HALF_EVEN = 6; - BigNumber.ROUND_HALF_CEIL = 7; - BigNumber.ROUND_HALF_FLOOR = 8; - BigNumber.EUCLID = 9; - - - /* - * Configure infrequently-changing library-wide settings. - * - * Accept an object or an argument list, with one or many of the following properties or - * parameters respectively: - * - * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive - * ROUNDING_MODE {number} Integer, 0 to 8 inclusive - * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or - * [integer -MAX to 0 incl., 0 to MAX incl.] - * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - * [integer -MAX to -1 incl., integer 1 to MAX incl.] - * ERRORS {boolean|number} true, false, 1 or 0 - * CRYPTO {boolean|number} true, false, 1 or 0 - * MODULO_MODE {number} 0 to 9 inclusive - * POW_PRECISION {number} 0 to MAX inclusive - * FORMAT {object} See BigNumber.prototype.toFormat - * decimalSeparator {string} - * groupSeparator {string} - * groupSize {number} - * secondaryGroupSize {number} - * fractionGroupSeparator {string} - * fractionGroupSize {number} - * - * (The values assigned to the above FORMAT object properties are not checked for validity.) - * - * E.g. - * BigNumber.config(20, 4) is equivalent to - * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) - * - * Ignore properties/parameters set to null or undefined. - * Return an object with the properties current values. - */ - BigNumber.config = function () { - var v, p, - i = 0, - r = {}, - a = arguments, - o = a[0], - has = o && typeof o == 'object' - ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } - : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; - - // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. - // 'config() DECIMAL_PLACES not an integer: {v}' - // 'config() DECIMAL_PLACES out of range: {v}' - if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { - DECIMAL_PLACES = v | 0; - } - r[p] = DECIMAL_PLACES; - - // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. - // 'config() ROUNDING_MODE not an integer: {v}' - // 'config() ROUNDING_MODE out of range: {v}' - if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { - ROUNDING_MODE = v | 0; - } - r[p] = ROUNDING_MODE; - - // EXPONENTIAL_AT {number|number[]} - // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. - // 'config() EXPONENTIAL_AT not an integer: {v}' - // 'config() EXPONENTIAL_AT out of range: {v}' - if ( has( p = 'EXPONENTIAL_AT' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { - TO_EXP_NEG = v[0] | 0; - TO_EXP_POS = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); - } - } - r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; - - // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or - // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. - // 'config() RANGE not an integer: {v}' - // 'config() RANGE cannot be zero: {v}' - // 'config() RANGE out of range: {v}' - if ( has( p = 'RANGE' ) ) { - - if ( isArray(v) ) { - if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { - MIN_EXP = v[0] | 0; - MAX_EXP = v[1] | 0; - } - } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { - if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); - else if (ERRORS) raise( 2, p + ' cannot be zero', v ); - } - } - r[p] = [ MIN_EXP, MAX_EXP ]; - - // ERRORS {boolean|number} true, false, 1 or 0. - // 'config() ERRORS not a boolean or binary digit: {v}' - if ( has( p = 'ERRORS' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - id = 0; - isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = ERRORS; - - // CRYPTO {boolean|number} true, false, 1 or 0. - // 'config() CRYPTO not a boolean or binary digit: {v}' - // 'config() crypto unavailable: {crypto}' - if ( has( p = 'CRYPTO' ) ) { - - if ( v === !!v || v === 1 || v === 0 ) { - CRYPTO = !!( v && crypto && typeof crypto == 'object' ); - if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); - } else if (ERRORS) { - raise( 2, p + notBool, v ); - } - } - r[p] = CRYPTO; - - // MODULO_MODE {number} Integer, 0 to 9 inclusive. - // 'config() MODULO_MODE not an integer: {v}' - // 'config() MODULO_MODE out of range: {v}' - if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { - MODULO_MODE = v | 0; - } - r[p] = MODULO_MODE; - - // POW_PRECISION {number} Integer, 0 to MAX inclusive. - // 'config() POW_PRECISION not an integer: {v}' - // 'config() POW_PRECISION out of range: {v}' - if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { - POW_PRECISION = v | 0; - } - r[p] = POW_PRECISION; - - // FORMAT {object} - // 'config() FORMAT not an object: {v}' - if ( has( p = 'FORMAT' ) ) { - - if ( typeof v == 'object' ) { - FORMAT = v; - } else if (ERRORS) { - raise( 2, p + ' not an object', v ); - } - } - r[p] = FORMAT; - - return r; - }; - - - /* - * Return a new BigNumber whose value is the maximum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; - - - /* - * Return a new BigNumber whose value is the minimum of the arguments. - * - * arguments {number|string|BigNumber} - */ - BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; - - - /* - * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, - * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing - * zeros are produced). - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * - * 'random() decimal places not an integer: {dp}' - * 'random() decimal places out of range: {dp}' - * 'random() crypto unavailable: {crypto}' - */ - BigNumber.random = (function () { - var pow2_53 = 0x20000000000000; - - // Return a 53 bit integer n, where 0 <= n < 9007199254740992. - // Check if Math.random() produces more than 32 bits of randomness. - // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. - // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. - var random53bitInt = (Math.random() * pow2_53) & 0x1fffff - ? function () { return mathfloor( Math.random() * pow2_53 ); } - : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + - (Math.random() * 0x800000 | 0); }; - - return function (dp) { - var a, b, e, k, v, - i = 0, - c = [], - rand = new BigNumber(ONE); - - dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; - k = mathceil( dp / LOG_BASE ); - - if (CRYPTO) { - - // Browsers supporting crypto.getRandomValues. - if ( crypto && crypto.getRandomValues ) { - - a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); - - for ( ; i < k; ) { - - // 53 bits: - // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) - // 11111 11111111 11111111 11111111 11100000 00000000 00000000 - // ((Math.pow(2, 32) - 1) >>> 11).toString(2) - // 11111 11111111 11111111 - // 0x20000 is 2^21. - v = a[i] * 0x20000 + (a[i + 1] >>> 11); - - // Rejection sampling: - // 0 <= v < 9007199254740992 - // Probability that v >= 9e15, is - // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 - if ( v >= 9e15 ) { - b = crypto.getRandomValues( new Uint32Array(2) ); - a[i] = b[0]; - a[i + 1] = b[1]; - } else { - - // 0 <= v <= 8999999999999999 - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 2; - } - } - i = k / 2; - - // Node.js supporting crypto.randomBytes. - } else if ( crypto && crypto.randomBytes ) { - - // buffer - a = crypto.randomBytes( k *= 7 ); - - for ( ; i < k; ) { - - // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 - // 0x100000000 is 2^32, 0x1000000 is 2^24 - // 11111 11111111 11111111 11111111 11111111 11111111 11111111 - // 0 <= v < 9007199254740992 - v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + - ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + - ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; - - if ( v >= 9e15 ) { - crypto.randomBytes(7).copy( a, i ); - } else { - - // 0 <= (v % 1e14) <= 99999999999999 - c.push( v % 1e14 ); - i += 7; - } - } - i = k / 7; - } else if (ERRORS) { - raise( 14, 'crypto unavailable', crypto ); - } - } - - // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. - if (!i) { - - for ( ; i < k; ) { - v = random53bitInt(); - if ( v < 9e15 ) c[i++] = v % 1e14; - } - } - - k = c[--i]; - dp %= LOG_BASE; - - // Convert trailing digits to zeros according to dp. - if ( k && dp ) { - v = POWS_TEN[LOG_BASE - dp]; - c[i] = mathfloor( k / v ) * v; - } - - // Remove trailing elements which are zero. - for ( ; c[i] === 0; c.pop(), i-- ); - - // Zero? - if ( i < 0 ) { - c = [ e = 0 ]; - } else { - - // Remove leading elements which are zero and adjust exponent accordingly. - for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); - - // Count the digits of the first element of c to determine leading zeros, and... - for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); - - // adjust the exponent accordingly. - if ( i < LOG_BASE ) e -= LOG_BASE - i; - } - - rand.e = e; - rand.c = c; - return rand; - }; - })(); - - - // PRIVATE FUNCTIONS - - - // Convert a numeric string of baseIn to a numeric string of baseOut. - function convertBase( str, baseOut, baseIn, sign ) { - var d, e, k, r, x, xc, y, - i = str.indexOf( '.' ), - dp = DECIMAL_PLACES, - rm = ROUNDING_MODE; - - if ( baseIn < 37 ) str = str.toLowerCase(); - - // Non-integer. - if ( i >= 0 ) { - k = POW_PRECISION; - - // Unlimited precision. - POW_PRECISION = 0; - str = str.replace( '.', '' ); - y = new BigNumber(baseIn); - x = y.pow( str.length - i ); - POW_PRECISION = k; - - // Convert str as if an integer, then restore the fraction part by dividing the - // result by its base raised to a power. - y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); - y.e = y.c.length; - } - - // Convert the number as integer. - xc = toBaseOut( str, baseIn, baseOut ); - e = k = xc.length; - - // Remove trailing zeros. - for ( ; xc[--k] == 0; xc.pop() ); - if ( !xc[0] ) return '0'; - - if ( i < 0 ) { - --e; - } else { - x.c = xc; - x.e = e; - - // sign is needed for correct rounding. - x.s = sign; - x = div( x, y, dp, rm, baseOut ); - xc = x.c; - r = x.r; - e = x.e; - } - - d = e + dp + 1; - - // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. - i = xc[d]; - k = baseOut / 2; - r = r || d < 0 || xc[d + 1] != null; - - r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( d < 1 || !xc[0] ) { - - // 1^-dp or 0. - str = r ? toFixedPoint( '1', -dp ) : '0'; - } else { - xc.length = d; - - if (r) { - - // Rounding up may mean the previous digit has to be rounded up and so on. - for ( --baseOut; ++xc[--d] > baseOut; ) { - xc[d] = 0; - - if ( !d ) { - ++e; - xc.unshift(1); - } - } - } - - // Determine trailing zeros. - for ( k = xc.length; !xc[--k]; ); - - // E.g. [4, 11, 15] becomes 4bf. - for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); - str = toFixedPoint( str, e ); - } - - // The caller will add the sign. - return str; - } - - - // Perform division in the specified base. Called by div and convertBase. - div = (function () { - - // Assume non-zero x and k. - function multiply( x, k, base ) { - var m, temp, xlo, xhi, - carry = 0, - i = x.length, - klo = k % SQRT_BASE, - khi = k / SQRT_BASE | 0; - - for ( x = x.slice(); i--; ) { - xlo = x[i] % SQRT_BASE; - xhi = x[i] / SQRT_BASE | 0; - m = khi * xlo + xhi * klo; - temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; - carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; - x[i] = temp % base; - } - - if (carry) x.unshift(carry); - - return x; - } - - function compare( a, b, aL, bL ) { - var i, cmp; - - if ( aL != bL ) { - cmp = aL > bL ? 1 : -1; - } else { - - for ( i = cmp = 0; i < aL; i++ ) { - - if ( a[i] != b[i] ) { - cmp = a[i] > b[i] ? 1 : -1; - break; - } - } - } - return cmp; - } - - function subtract( a, b, aL, base ) { - var i = 0; - - // Subtract b from a. - for ( ; aL--; ) { - a[aL] -= i; - i = a[aL] < b[aL] ? 1 : 0; - a[aL] = i * base + a[aL] - b[aL]; - } - - // Remove leading zeros. - for ( ; !a[0] && a.length > 1; a.shift() ); - } - - // x: dividend, y: divisor. - return function ( x, y, dp, rm, base ) { - var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, - yL, yz, - s = x.s == y.s ? 1 : -1, - xc = x.c, - yc = y.c; - - // Either NaN, Infinity or 0? - if ( !xc || !xc[0] || !yc || !yc[0] ) { - - return new BigNumber( - - // Return NaN if either NaN, or both Infinity or 0. - !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : - - // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. - xc && xc[0] == 0 || !yc ? s * 0 : s / 0 - ); - } - - q = new BigNumber(s); - qc = q.c = []; - e = x.e - y.e; - s = dp + e + 1; - - if ( !base ) { - base = BASE; - e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); - s = s / LOG_BASE | 0; - } - - // Result exponent may be one less then the current value of e. - // The coefficients of the BigNumbers from convertBase may have trailing zeros. - for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); - if ( yc[i] > ( xc[i] || 0 ) ) e--; - - if ( s < 0 ) { - qc.push(1); - more = true; - } else { - xL = xc.length; - yL = yc.length; - i = 0; - s += 2; - - // Normalise xc and yc so highest order digit of yc is >= base / 2. - - n = mathfloor( base / ( yc[0] + 1 ) ); - - // Not necessary, but to handle odd bases where yc[0] == ( base / 2 ) - 1. - // if ( n > 1 || n++ == 1 && yc[0] < base / 2 ) { - if ( n > 1 ) { - yc = multiply( yc, n, base ); - xc = multiply( xc, n, base ); - yL = yc.length; - xL = xc.length; - } - - xi = yL; - rem = xc.slice( 0, yL ); - remL = rem.length; - - // Add zeros to make remainder as long as divisor. - for ( ; remL < yL; rem[remL++] = 0 ); - yz = yc.slice(); - yz.unshift(0); - yc0 = yc[0]; - if ( yc[1] >= base / 2 ) yc0++; - // Not necessary, but to prevent trial digit n > base, when using base 3. - // else if ( base == 3 && yc0 == 1 ) yc0 = 1 + 1e-15; - - do { - n = 0; - - // Compare divisor and remainder. - cmp = compare( yc, rem, yL, remL ); - - // If divisor < remainder. - if ( cmp < 0 ) { - - // Calculate trial digit, n. - - rem0 = rem[0]; - if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); - - // n is how many times the divisor goes into the current remainder. - n = mathfloor( rem0 / yc0 ); - - // Algorithm: - // 1. product = divisor * trial digit (n) - // 2. if product > remainder: product -= divisor, n-- - // 3. remainder -= product - // 4. if product was < remainder at 2: - // 5. compare new remainder and divisor - // 6. If remainder > divisor: remainder -= divisor, n++ - - if ( n > 1 ) { - - // n may be > base only when base is 3. - if (n >= base) n = base - 1; - - // product = divisor * trial digit. - prod = multiply( yc, n, base ); - prodL = prod.length; - remL = rem.length; - - // Compare product and remainder. - // If product > remainder. - // Trial digit n too high. - // n is 1 too high about 5% of the time, and is not known to have - // ever been more than 1 too high. - while ( compare( prod, rem, prodL, remL ) == 1 ) { - n--; - - // Subtract divisor from product. - subtract( prod, yL < prodL ? yz : yc, prodL, base ); - prodL = prod.length; - cmp = 1; - } - } else { - - // n is 0 or 1, cmp is -1. - // If n is 0, there is no need to compare yc and rem again below, - // so change cmp to 1 to avoid it. - // If n is 1, leave cmp as -1, so yc and rem are compared again. - if ( n == 0 ) { - - // divisor < remainder, so n must be at least 1. - cmp = n = 1; - } - - // product = divisor - prod = yc.slice(); - prodL = prod.length; - } - - if ( prodL < remL ) prod.unshift(0); - - // Subtract product from remainder. - subtract( rem, prod, remL, base ); - remL = rem.length; - - // If product was < remainder. - if ( cmp == -1 ) { - - // Compare divisor and new remainder. - // If divisor < new remainder, subtract divisor from remainder. - // Trial digit n too low. - // n is 1 too low about 5% of the time, and very rarely 2 too low. - while ( compare( yc, rem, yL, remL ) < 1 ) { - n++; - - // Subtract divisor from remainder. - subtract( rem, yL < remL ? yz : yc, remL, base ); - remL = rem.length; - } - } - } else if ( cmp === 0 ) { - n++; - rem = [0]; - } // else cmp === 1 and n will be 0 - - // Add the next digit, n, to the result array. - qc[i++] = n; - - // Update the remainder. - if ( rem[0] ) { - rem[remL++] = xc[xi] || 0; - } else { - rem = [ xc[xi] ]; - remL = 1; - } - } while ( ( xi++ < xL || rem[0] != null ) && s-- ); - - more = rem[0] != null; - - // Leading zero? - if ( !qc[0] ) qc.shift(); - } - - if ( base == BASE ) { - - // To calculate q.e, first get the number of digits of qc[0]. - for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); - round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); - - // Caller is convertBase. - } else { - q.e = e; - q.r = +more; - } - - return q; - }; - })(); - - - /* - * Return a string representing the value of BigNumber n in fixed-point or exponential - * notation rounded to the specified decimal places or significant digits. - * - * n is a BigNumber. - * i is the index of the last digit required (i.e. the digit that may be rounded up). - * rm is the rounding mode. - * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. - */ - function format( n, i, rm, caller ) { - var c0, e, ne, len, str; - - rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) - ? rm | 0 : ROUNDING_MODE; - - if ( !n.c ) return n.toString(); - c0 = n.c[0]; - ne = n.e; - - if ( i == null ) { - str = coeffToString( n.c ); - str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG - ? toExponential( str, ne ) - : toFixedPoint( str, ne ); - } else { - n = round( new BigNumber(n), i, rm ); - - // n.e may have changed if the value was rounded up. - e = n.e; - - str = coeffToString( n.c ); - len = str.length; - - // toPrecision returns exponential notation if the number of significant digits - // specified is less than the number of digits necessary to represent the integer - // part of the value in fixed-point notation. - - // Exponential notation. - if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { - - // Append zeros? - for ( ; len < i; str += '0', len++ ); - str = toExponential( str, e ); - - // Fixed-point notation. - } else { - i -= ne; - str = toFixedPoint( str, e ); - - // Append zeros? - if ( e + 1 > len ) { - if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); - } else { - i += e - len; - if ( i > 0 ) { - if ( e + 1 == len ) str += '.'; - for ( ; i--; str += '0' ); - } - } - } - } - - return n.s < 0 && c0 ? '-' + str : str; - } - - - // Handle BigNumber.max and BigNumber.min. - function maxOrMin( args, method ) { - var m, n, - i = 0; - - if ( isArray( args[0] ) ) args = args[0]; - m = new BigNumber( args[0] ); - - for ( ; ++i < args.length; ) { - n = new BigNumber( args[i] ); - - // If any number is NaN, return NaN. - if ( !n.s ) { - m = n; - break; - } else if ( method.call( m, n ) ) { - m = n; - } - } - - return m; - } - - - /* - * Return true if n is an integer in range, otherwise throw. - * Use for argument validation when ERRORS is true. - */ - function intValidatorWithErrors( n, min, max, caller, name ) { - if ( n < min || n > max || n != truncate(n) ) { - raise( caller, ( name || 'decimal places' ) + - ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); - } - - return true; - } - - - /* - * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. - * Called by minus, plus and times. - */ - function normalise( n, c, e ) { - var i = 1, - j = c.length; - - // Remove trailing zeros. - for ( ; !c[--j]; c.pop() ); - - // Calculate the base 10 exponent. First get the number of digits of c[0]. - for ( j = c[0]; j >= 10; j /= 10, i++ ); - - // Overflow? - if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { - - // Infinity. - n.c = n.e = null; - - // Underflow? - } else if ( e < MIN_EXP ) { - - // Zero. - n.c = [ n.e = 0 ]; - } else { - n.e = e; - n.c = c; - } - - return n; - } - - - // Handle values that fail the validity test in BigNumber. - parseNumeric = (function () { - var basePrefix = /^(-?)0([xbo])/i, - dotAfter = /^([^.]+)\.$/, - dotBefore = /^\.([^.]+)$/, - isInfinityOrNaN = /^-?(Infinity|NaN)$/, - whitespaceOrPlus = /^\s*\+|^\s+|\s+$/g; - - return function ( x, str, num, b ) { - var base, - s = num ? str : str.replace( whitespaceOrPlus, '' ); - - // No exception on ±Infinity or NaN. - if ( isInfinityOrNaN.test(s) ) { - x.s = isNaN(s) ? null : s < 0 ? -1 : 1; - } else { - if ( !num ) { - - // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i - s = s.replace( basePrefix, function ( m, p1, p2 ) { - base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; - return !b || b == base ? p1 : m; - }); - - if (b) { - base = b; - - // E.g. '1.' to '1', '.1' to '0.1' - s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); - } - - if ( str != s ) return new BigNumber( s, base ); - } - - // 'new BigNumber() not a number: {n}' - // 'new BigNumber() not a base {b} number: {n}' - if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); - x.s = null; - } - - x.c = x.e = null; - id = 0; - } - })(); - - - // Throw a BigNumber Error. - function raise( caller, msg, val ) { - var error = new Error( [ - 'new BigNumber', // 0 - 'cmp', // 1 - 'config', // 2 - 'div', // 3 - 'divToInt', // 4 - 'eq', // 5 - 'gt', // 6 - 'gte', // 7 - 'lt', // 8 - 'lte', // 9 - 'minus', // 10 - 'mod', // 11 - 'plus', // 12 - 'precision', // 13 - 'random', // 14 - 'round', // 15 - 'shift', // 16 - 'times', // 17 - 'toDigits', // 18 - 'toExponential', // 19 - 'toFixed', // 20 - 'toFormat', // 21 - 'toFraction', // 22 - 'pow', // 23 - 'toPrecision', // 24 - 'toString', // 25 - 'BigNumber' // 26 - ][caller] + '() ' + msg + ': ' + val ); - - error.name = 'BigNumber Error'; - id = 0; - throw error; - } - - - /* - * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. - * If r is truthy, it is known that there are more digits after the rounding digit. - */ - function round( x, sd, rm, r ) { - var d, i, j, k, n, ni, rd, - xc = x.c, - pows10 = POWS_TEN; - - // if x is not Infinity or NaN... - if (xc) { - - // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. - // n is a base 1e14 number, the value of the element of array x.c containing rd. - // ni is the index of n within x.c. - // d is the number of digits of n. - // i is the index of rd within n including leading zeros. - // j is the actual index of rd within n (if < 0, rd is a leading zero). - out: { - - // Get the number of digits of the first element of xc. - for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); - i = sd - d; - - // If the rounding digit is in the first element of xc... - if ( i < 0 ) { - i += LOG_BASE; - j = sd; - n = xc[ ni = 0 ]; - - // Get the rounding digit at index j of n. - rd = n / pows10[ d - j - 1 ] % 10 | 0; - } else { - ni = mathceil( ( i + 1 ) / LOG_BASE ); - - if ( ni >= xc.length ) { - - if (r) { - - // Needed by sqrt. - for ( ; xc.length <= ni; xc.push(0) ); - n = rd = 0; - d = 1; - i %= LOG_BASE; - j = i - LOG_BASE + 1; - } else { - break out; - } - } else { - n = k = xc[ni]; - - // Get the number of digits of n. - for ( d = 1; k >= 10; k /= 10, d++ ); - - // Get the index of rd within n. - i %= LOG_BASE; - - // Get the index of rd within n, adjusted for leading zeros. - // The number of leading zeros of n is given by LOG_BASE - d. - j = i - LOG_BASE + d; - - // Get the rounding digit at index j of n. - rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; - } - } - - r = r || sd < 0 || - - // Are there any non-zero digits after the rounding digit? - // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right - // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. - xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); - - r = rm < 4 - ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) - : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && - - // Check whether the digit to the left of the rounding digit is odd. - ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || - rm == ( x.s < 0 ? 8 : 7 ) ); - - if ( sd < 1 || !xc[0] ) { - xc.length = 0; - - if (r) { - - // Convert sd to decimal places. - sd -= x.e + 1; - - // 1, 0.1, 0.01, 0.001, 0.0001 etc. - xc[0] = pows10[ sd % LOG_BASE ]; - x.e = -sd || 0; - } else { - - // Zero. - xc[0] = x.e = 0; - } - - return x; - } - - // Remove excess digits. - if ( i == 0 ) { - xc.length = ni; - k = 1; - ni--; - } else { - xc.length = ni + 1; - k = pows10[ LOG_BASE - i ]; - - // E.g. 56700 becomes 56000 if 7 is the rounding digit. - // j > 0 means i > number of leading zeros of n. - xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; - } - - // Round up? - if (r) { - - for ( ; ; ) { - - // If the digit to be rounded up is in the first element of xc... - if ( ni == 0 ) { - - // i will be the length of xc[0] before k is added. - for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); - j = xc[0] += k; - for ( k = 1; j >= 10; j /= 10, k++ ); - - // if i != k the length has increased. - if ( i != k ) { - x.e++; - if ( xc[0] == BASE ) xc[0] = 1; - } - - break; - } else { - xc[ni] += k; - if ( xc[ni] != BASE ) break; - xc[ni--] = 0; - k = 1; - } - } - } - - // Remove trailing zeros. - for ( i = xc.length; xc[--i] === 0; xc.pop() ); - } - - // Overflow? Infinity. - if ( x.e > MAX_EXP ) { - x.c = x.e = null; - - // Underflow? Zero. - } else if ( x.e < MIN_EXP ) { - x.c = [ x.e = 0 ]; - } - } - - return x; - } - - - // PROTOTYPE/INSTANCE METHODS - - - /* - * Return a new BigNumber whose value is the absolute value of this BigNumber. - */ - P.absoluteValue = P.abs = function () { - var x = new BigNumber(this); - if ( x.s < 0 ) x.s = 1; - return x; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of Infinity. - */ - P.ceil = function () { - return round( new BigNumber(this), this.e + 1, 2 ); - }; - - - /* - * Return - * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), - * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), - * 0 if they have the same value, - * or null if the value of either is NaN. - */ - P.comparedTo = P.cmp = function ( y, b ) { - id = 1; - return compare( this, new BigNumber( y, b ) ); - }; - - - /* - * Return the number of decimal places of the value of this BigNumber, or null if the value - * of this BigNumber is ±Infinity or NaN. - */ - P.decimalPlaces = P.dp = function () { - var n, v, - c = this.c; - - if ( !c ) return null; - n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; - - // Subtract the number of trailing zeros of the last number. - if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); - if ( n < 0 ) n = 0; - - return n; - }; - - - /* - * n / 0 = I - * n / N = N - * n / I = 0 - * 0 / n = 0 - * 0 / 0 = N - * 0 / N = N - * 0 / I = 0 - * N / n = N - * N / 0 = N - * N / N = N - * N / I = N - * I / n = I - * I / 0 = I - * I / N = N - * I / I = N - * - * Return a new BigNumber whose value is the value of this BigNumber divided by the value of - * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.dividedBy = P.div = function ( y, b ) { - id = 3; - return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); - }; - - - /* - * Return a new BigNumber whose value is the integer part of dividing the value of this - * BigNumber by the value of BigNumber(y, b). - */ - P.dividedToIntegerBy = P.divToInt = function ( y, b ) { - id = 4; - return div( this, new BigNumber( y, b ), 0, 1 ); - }; - - - /* - * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), - * otherwise returns false. - */ - P.equals = P.eq = function ( y, b ) { - id = 5; - return compare( this, new BigNumber( y, b ) ) === 0; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole - * number in the direction of -Infinity. - */ - P.floor = function () { - return round( new BigNumber(this), this.e + 1, 3 ); - }; - - - /* - * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.greaterThan = P.gt = function ( y, b ) { - id = 6; - return compare( this, new BigNumber( y, b ) ) > 0; - }; - - - /* - * Return true if the value of this BigNumber is greater than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.greaterThanOrEqualTo = P.gte = function ( y, b ) { - id = 7; - return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; - - }; - - - /* - * Return true if the value of this BigNumber is a finite number, otherwise returns false. - */ - P.isFinite = function () { - return !!this.c; - }; - - - /* - * Return true if the value of this BigNumber is an integer, otherwise return false. - */ - P.isInteger = P.isInt = function () { - return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; - }; - - - /* - * Return true if the value of this BigNumber is NaN, otherwise returns false. - */ - P.isNaN = function () { - return !this.s; - }; - - - /* - * Return true if the value of this BigNumber is negative, otherwise returns false. - */ - P.isNegative = P.isNeg = function () { - return this.s < 0; - }; - - - /* - * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. - */ - P.isZero = function () { - return !!this.c && this.c[0] == 0; - }; - - - /* - * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), - * otherwise returns false. - */ - P.lessThan = P.lt = function ( y, b ) { - id = 8; - return compare( this, new BigNumber( y, b ) ) < 0; - }; - - - /* - * Return true if the value of this BigNumber is less than or equal to the value of - * BigNumber(y, b), otherwise returns false. - */ - P.lessThanOrEqualTo = P.lte = function ( y, b ) { - id = 9; - return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; - }; - - - /* - * n - 0 = n - * n - N = N - * n - I = -I - * 0 - n = -n - * 0 - 0 = 0 - * 0 - N = N - * 0 - I = -I - * N - n = N - * N - 0 = N - * N - N = N - * N - I = N - * I - n = I - * I - 0 = I - * I - N = N - * I - I = N - * - * Return a new BigNumber whose value is the value of this BigNumber minus the value of - * BigNumber(y, b). - */ - P.minus = P.sub = function ( y, b ) { - var i, j, t, xLTy, - x = this, - a = x.s; - - id = 10; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.plus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Either Infinity? - if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); - - // Either zero? - if ( !xc[0] || !yc[0] ) { - - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : - - // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity - ROUNDING_MODE == 3 ? -0 : 0 ); - } - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Determine which is the bigger number. - if ( a = xe - ye ) { - - if ( xLTy = a < 0 ) { - a = -a; - t = xc; - } else { - ye = xe; - t = yc; - } - - t.reverse(); - - // Prepend zeros to equalise exponents. - for ( b = a; b--; t.push(0) ); - t.reverse(); - } else { - - // Exponents equal. Check digit by digit. - j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; - - for ( a = b = 0; b < j; b++ ) { - - if ( xc[b] != yc[b] ) { - xLTy = xc[b] < yc[b]; - break; - } - } - } - - // x < y? Point xc to the array of the bigger number. - if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; - - b = ( j = yc.length ) - ( i = xc.length ); - - // Append zeros to xc if shorter. - // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. - if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); - b = BASE - 1; - - // Subtract yc from xc. - for ( ; j > a; ) { - - if ( xc[--j] < yc[j] ) { - for ( i = j; i && !xc[--i]; xc[i] = b ); - --xc[i]; - xc[j] += BASE; - } - - xc[j] -= yc[j]; - } - - // Remove leading zeros and adjust exponent accordingly. - for ( ; xc[0] == 0; xc.shift(), --ye ); - - // Zero? - if ( !xc[0] ) { - - // Following IEEE 754 (2008) 6.3, - // n - n = +0 but n - n = -0 when rounding towards -Infinity. - y.s = ROUNDING_MODE == 3 ? -1 : 1; - y.c = [ y.e = 0 ]; - return y; - } - - // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity - // for finite x and y. - return normalise( y, xc, ye ); - }; - - - /* - * n % 0 = N - * n % N = N - * n % I = n - * 0 % n = 0 - * -0 % n = -0 - * 0 % 0 = N - * 0 % N = N - * 0 % I = 0 - * N % n = N - * N % 0 = N - * N % N = N - * N % I = N - * I % n = N - * I % 0 = N - * I % N = N - * I % I = N - * - * Return a new BigNumber whose value is the value of this BigNumber modulo the value of - * BigNumber(y, b). The result depends on the value of MODULO_MODE. - */ - P.modulo = P.mod = function ( y, b ) { - var q, s, - x = this; - - id = 11; - y = new BigNumber( y, b ); - - // Return NaN if x is Infinity or NaN, or y is NaN or zero. - if ( !x.c || !y.s || y.c && !y.c[0] ) { - return new BigNumber(NaN); - - // Return x if y is Infinity or x is zero. - } else if ( !y.c || x.c && !x.c[0] ) { - return new BigNumber(x); - } - - if ( MODULO_MODE == 9 ) { - - // Euclidian division: q = sign(y) * floor(x / abs(y)) - // r = x - qy where 0 <= r < abs(y) - s = y.s; - y.s = 1; - q = div( x, y, 0, 3 ); - y.s = s; - q.s *= s; - } else { - q = div( x, y, 0, MODULO_MODE ); - } - - return x.minus( q.times(y) ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber negated, - * i.e. multiplied by -1. - */ - P.negated = P.neg = function () { - var x = new BigNumber(this); - x.s = -x.s || null; - return x; - }; - - - /* - * n + 0 = n - * n + N = N - * n + I = I - * 0 + n = n - * 0 + 0 = 0 - * 0 + N = N - * 0 + I = I - * N + n = N - * N + 0 = N - * N + N = N - * N + I = N - * I + n = I - * I + 0 = I - * I + N = N - * I + I = I - * - * Return a new BigNumber whose value is the value of this BigNumber plus the value of - * BigNumber(y, b). - */ - P.plus = P.add = function ( y, b ) { - var t, - x = this, - a = x.s; - - id = 12; - y = new BigNumber( y, b ); - b = y.s; - - // Either NaN? - if ( !a || !b ) return new BigNumber(NaN); - - // Signs differ? - if ( a != b ) { - y.s = -b; - return x.minus(y); - } - - var xe = x.e / LOG_BASE, - ye = y.e / LOG_BASE, - xc = x.c, - yc = y.c; - - if ( !xe || !ye ) { - - // Return ±Infinity if either ±Infinity. - if ( !xc || !yc ) return new BigNumber( a / 0 ); - - // Either zero? - // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. - if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); - } - - xe = bitFloor(xe); - ye = bitFloor(ye); - xc = xc.slice(); - - // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. - if ( a = xe - ye ) { - if ( a > 0 ) { - ye = xe; - t = yc; - } else { - a = -a; - t = xc; - } - - t.reverse(); - for ( ; a--; t.push(0) ); - t.reverse(); - } - - a = xc.length; - b = yc.length; - - // Point xc to the longer array, and b to the shorter length. - if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; - - // Only start adding at yc.length - 1 as the further digits of xc can be ignored. - for ( a = 0; b; ) { - a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; - xc[b] %= BASE; - } - - if (a) { - xc.unshift(a); - ++ye; - } - - // No need to check for zero, as +x + +y != 0 && -x + -y != 0 - // ye = MAX_EXP + 1 possible - return normalise( y, xc, ye ); - }; - - - /* - * Return the number of significant digits of the value of this BigNumber. - * - * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. - */ - P.precision = P.sd = function (z) { - var n, v, - x = this, - c = x.c; - - // 'precision() argument not a boolean or binary digit: {z}' - if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { - if (ERRORS) raise( 13, 'argument' + notBool, z ); - if ( z != !!z ) z = null; - } - - if ( !c ) return null; - v = c.length - 1; - n = v * LOG_BASE + 1; - - if ( v = c[v] ) { - - // Subtract the number of trailing zeros of the last element. - for ( ; v % 10 == 0; v /= 10, n-- ); - - // Add the number of digits of the first element. - for ( v = c[0]; v >= 10; v /= 10, n++ ); - } - - if ( z && x.e + 1 > n ) n = x.e + 1; - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if - * omitted. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'round() decimal places out of range: {dp}' - * 'round() decimal places not an integer: {dp}' - * 'round() rounding mode not an integer: {rm}' - * 'round() rounding mode out of range: {rm}' - */ - P.round = function ( dp, rm ) { - var n = new BigNumber(this); - - if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { - round( n, ~~dp + this.e + 1, rm == null || - !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); - } - - return n; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber shifted by k places - * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. - * - * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. - * - * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity - * otherwise. - * - * 'shift() argument not an integer: {k}' - * 'shift() argument out of range: {k}' - */ - P.shift = function (k) { - var n = this; - return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) - - // k < 1e+21, or truncate(k) will produce exponential notation. - ? n.times( '1e' + truncate(k) ) - : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) - ? n.s * ( k < 0 ? 0 : 1 / 0 ) - : n ); - }; - - - /* - * sqrt(-n) = N - * sqrt( N) = N - * sqrt(-I) = N - * sqrt( I) = I - * sqrt( 0) = 0 - * sqrt(-0) = -0 - * - * Return a new BigNumber whose value is the square root of the value of this BigNumber, - * rounded according to DECIMAL_PLACES and ROUNDING_MODE. - */ - P.squareRoot = P.sqrt = function () { - var m, n, r, rep, t, - x = this, - c = x.c, - s = x.s, - e = x.e, - dp = DECIMAL_PLACES + 4, - half = new BigNumber('0.5'); - - // Negative/NaN/Infinity/zero? - if ( s !== 1 || !c || !c[0] ) { - return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); - } - - // Initial estimate. - s = Math.sqrt( +x ); - - // Math.sqrt underflow/overflow? - // Pass x to Math.sqrt as integer, then adjust the exponent of the result. - if ( s == 0 || s == 1 / 0 ) { - n = coeffToString(c); - if ( ( n.length + e ) % 2 == 0 ) n += '0'; - s = Math.sqrt(n); - e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); - - if ( s == 1 / 0 ) { - n = '1e' + e; - } else { - n = s.toExponential(); - n = n.slice( 0, n.indexOf('e') + 1 ) + e; - } - - r = new BigNumber(n); - } else { - r = new BigNumber( s + '' ); - } - - // Check for zero. - // r could be zero if MIN_EXP is changed after the this value was created. - // This would cause a division by zero (x/t) and hence Infinity below, which would cause - // coeffToString to throw. - if ( r.c[0] ) { - e = r.e; - s = e + dp; - if ( s < 3 ) s = 0; - - // Newton-Raphson iteration. - for ( ; ; ) { - t = r; - r = half.times( t.plus( div( x, t, dp, 1 ) ) ); - - if ( coeffToString( t.c ).slice( 0, s ) === ( n = - coeffToString( r.c ) ).slice( 0, s ) ) { - - // The exponent of r may here be one less than the final result exponent, - // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits - // are indexed correctly. - if ( r.e < e ) --s; - n = n.slice( s - 3, s + 1 ); - - // The 4th rounding digit may be in error by -1 so if the 4 rounding digits - // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the - // iteration. - if ( n == '9999' || !rep && n == '4999' ) { - - // On the first iteration only, check to see if rounding up gives the - // exact result as the nines may infinitely repeat. - if ( !rep ) { - round( t, t.e + DECIMAL_PLACES + 2, 0 ); - - if ( t.times(t).eq(x) ) { - r = t; - break; - } - } - - dp += 4; - s += 4; - rep = 1; - } else { - - // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact - // result. If not, then there are further digits and m will be truthy. - if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { - - // Truncate to the first rounding digit. - round( r, r.e + DECIMAL_PLACES + 2, 1 ); - m = !r.times(r).eq(x); - } - - break; - } - } - } - } - - return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); - }; - - - /* - * n * 0 = 0 - * n * N = N - * n * I = I - * 0 * n = 0 - * 0 * 0 = 0 - * 0 * N = N - * 0 * I = N - * N * n = N - * N * 0 = N - * N * N = N - * N * I = N - * I * n = I - * I * 0 = N - * I * N = N - * I * I = I - * - * Return a new BigNumber whose value is the value of this BigNumber times the value of - * BigNumber(y, b). - */ - P.times = P.mul = function ( y, b ) { - var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, - base, sqrtBase, - x = this, - xc = x.c, - yc = ( id = 17, y = new BigNumber( y, b ) ).c; - - // Either NaN, ±Infinity or ±0? - if ( !xc || !yc || !xc[0] || !yc[0] ) { - - // Return NaN if either is NaN, or one is 0 and the other is Infinity. - if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { - y.c = y.e = y.s = null; - } else { - y.s *= x.s; - - // Return ±Infinity if either is ±Infinity. - if ( !xc || !yc ) { - y.c = y.e = null; - - // Return ±0 if either is ±0. - } else { - y.c = [0]; - y.e = 0; - } - } - - return y; - } - - e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); - y.s *= x.s; - xcL = xc.length; - ycL = yc.length; - - // Ensure xc points to longer array and xcL to its length. - if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; - - // Initialise the result array with zeros. - for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); - - base = BASE; - sqrtBase = SQRT_BASE; - - for ( i = ycL; --i >= 0; ) { - c = 0; - ylo = yc[i] % sqrtBase; - yhi = yc[i] / sqrtBase | 0; - - for ( k = xcL, j = i + k; j > i; ) { - xlo = xc[--k] % sqrtBase; - xhi = xc[k] / sqrtBase | 0; - m = yhi * xlo + xhi * ylo; - xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; - c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; - zc[j--] = xlo % base; - } - - zc[j] = c; - } - - if (c) { - ++e; - } else { - zc.shift(); - } - - return normalise( y, zc, e ); - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of - * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toDigits() precision out of range: {sd}' - * 'toDigits() precision not an integer: {sd}' - * 'toDigits() rounding mode not an integer: {rm}' - * 'toDigits() rounding mode out of range: {rm}' - */ - P.toDigits = function ( sd, rm ) { - var n = new BigNumber(this); - sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; - rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; - return sd ? round( n, sd, rm ) : n; - }; - - - /* - * Return a string representing the value of this BigNumber in exponential notation and - * rounded using ROUNDING_MODE to dp fixed decimal places. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toExponential() decimal places not an integer: {dp}' - * 'toExponential() decimal places out of range: {dp}' - * 'toExponential() rounding mode not an integer: {rm}' - * 'toExponential() rounding mode out of range: {rm}' - */ - P.toExponential = function ( dp, rm ) { - return format( this, - dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounding - * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. - * - * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', - * but e.g. (-0.00001).toFixed(0) is '-0'. - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFixed() decimal places not an integer: {dp}' - * 'toFixed() decimal places out of range: {dp}' - * 'toFixed() rounding mode not an integer: {rm}' - * 'toFixed() rounding mode out of range: {rm}' - */ - P.toFixed = function ( dp, rm ) { - return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) - ? ~~dp + this.e + 1 : null, rm, 20 ); - }; - - - /* - * Return a string representing the value of this BigNumber in fixed-point notation rounded - * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties - * of the FORMAT object (see BigNumber.config). - * - * FORMAT = { - * decimalSeparator : '.', - * groupSeparator : ',', - * groupSize : 3, - * secondaryGroupSize : 0, - * fractionGroupSeparator : '\xA0', // non-breaking space - * fractionGroupSize : 0 - * }; - * - * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toFormat() decimal places not an integer: {dp}' - * 'toFormat() decimal places out of range: {dp}' - * 'toFormat() rounding mode not an integer: {rm}' - * 'toFormat() rounding mode out of range: {rm}' - */ - P.toFormat = function ( dp, rm ) { - var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) - ? ~~dp + this.e + 1 : null, rm, 21 ); - - if ( this.c ) { - var i, - arr = str.split('.'), - g1 = +FORMAT.groupSize, - g2 = +FORMAT.secondaryGroupSize, - groupSeparator = FORMAT.groupSeparator, - intPart = arr[0], - fractionPart = arr[1], - isNeg = this.s < 0, - intDigits = isNeg ? intPart.slice(1) : intPart, - len = intDigits.length; - - if (g2) i = g1, g1 = g2, g2 = i, len -= i; - - if ( g1 > 0 && len > 0 ) { - i = len % g1 || g1; - intPart = intDigits.substr( 0, i ); - - for ( ; i < len; i += g1 ) { - intPart += groupSeparator + intDigits.substr( i, g1 ); - } - - if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); - if (isNeg) intPart = '-' + intPart; - } - - str = fractionPart - ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) - ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), - '$&' + FORMAT.fractionGroupSeparator ) - : fractionPart ) - : intPart; - } - - return str; - }; - - - /* - * Return a string array representing the value of this BigNumber as a simple fraction with - * an integer numerator and an integer denominator. The denominator will be a positive - * non-zero value less than or equal to the specified maximum denominator. If a maximum - * denominator is not specified, the denominator will be the lowest value necessary to - * represent the number exactly. - * - * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. - * - * 'toFraction() max denominator not an integer: {md}' - * 'toFraction() max denominator out of range: {md}' - */ - P.toFraction = function (md) { - var arr, d0, d2, e, exp, n, n0, q, s, - k = ERRORS, - x = this, - xc = x.c, - d = new BigNumber(ONE), - n1 = d0 = new BigNumber(ONE), - d1 = n0 = new BigNumber(ONE); - - if ( md != null ) { - ERRORS = false; - n = new BigNumber(md); - ERRORS = k; - - if ( !( k = n.isInt() ) || n.lt(ONE) ) { - - if (ERRORS) { - raise( 22, - 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); - } - - // ERRORS is false: - // If md is a finite non-integer >= 1, round it to an integer and use it. - md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; - } - } - - if ( !xc ) return x.toString(); - s = coeffToString(xc); - - // Determine initial denominator. - // d is a power of 10 and the minimum max denominator that specifies the value exactly. - e = d.e = s.length - x.e - 1; - d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; - md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; - - exp = MAX_EXP; - MAX_EXP = 1 / 0; - n = new BigNumber(s); - - // n0 = d1 = 0 - n0.c[0] = 0; - - for ( ; ; ) { - q = div( n, d, 0, 1 ); - d2 = d0.plus( q.times(d1) ); - if ( d2.cmp(md) == 1 ) break; - d0 = d1; - d1 = d2; - n1 = n0.plus( q.times( d2 = n1 ) ); - n0 = d2; - d = n.minus( q.times( d2 = d ) ); - n = d2; - } - - d2 = div( md.minus(d0), d1, 0, 1 ); - n0 = n0.plus( d2.times(n1) ); - d0 = d0.plus( d2.times(d1) ); - n0.s = n1.s = x.s; - e *= 2; - - // Determine which fraction is closer to x, n0/d0 or n1/d1 - arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( - div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 - ? [ n1.toString(), d1.toString() ] - : [ n0.toString(), d0.toString() ]; - - MAX_EXP = exp; - return arr; - }; - - - /* - * Return the value of this BigNumber converted to a number primitive. - */ - P.toNumber = function () { - var x = this; - - // Ensure zero has correct sign. - return +x || ( x.s ? x.s * 0 : NaN ); - }; - - - /* - * Return a BigNumber whose value is the value of this BigNumber raised to the power n. - * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. - * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. - * - * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. - * (Performs 54 loop iterations for n of 9007199254740992.) - * - * 'pow() exponent not an integer: {n}' - * 'pow() exponent out of range: {n}' - */ - P.toPower = P.pow = function (n) { - var k, y, - i = mathfloor( n < 0 ? -n : +n ), - x = this; - - // Pass ±Infinity to Math.pow if exponent is out of range. - if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && - ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || - parseFloat(n) != n && !( n = NaN ) ) ) { - return new BigNumber( Math.pow( +x, n ) ); - } - - // Truncating each coefficient array to a length of k after each multiplication equates - // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a - // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) - k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; - y = new BigNumber(ONE); - - for ( ; ; ) { - - if ( i % 2 ) { - y = y.times(x); - if ( !y.c ) break; - if ( k && y.c.length > k ) y.c.length = k; - } - - i = mathfloor( i / 2 ); - if ( !i ) break; - - x = x.times(x); - if ( k && x.c && x.c.length > k ) x.c.length = k; - } - - if ( n < 0 ) y = ONE.div(y); - return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; - }; - - - /* - * Return a string representing the value of this BigNumber rounded to sd significant digits - * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits - * necessary to represent the integer part of the value in fixed-point notation, then use - * exponential notation. - * - * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. - * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. - * - * 'toPrecision() precision not an integer: {sd}' - * 'toPrecision() precision out of range: {sd}' - * 'toPrecision() rounding mode not an integer: {rm}' - * 'toPrecision() rounding mode out of range: {rm}' - */ - P.toPrecision = function ( sd, rm ) { - return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) - ? sd | 0 : null, rm, 24 ); - }; - - - /* - * Return a string representing the value of this BigNumber in base b, or base 10 if b is - * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and - * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent - * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than - * TO_EXP_NEG, return exponential notation. - * - * [b] {number} Integer, 2 to 64 inclusive. - * - * 'toString() base not an integer: {b}' - * 'toString() base out of range: {b}' - */ - P.toString = function (b) { - var str, - n = this, - s = n.s, - e = n.e; - - // Infinity or NaN? - if ( e === null ) { - - if (s) { - str = 'Infinity'; - if ( s < 0 ) str = '-' + str; - } else { - str = 'NaN'; - } - } else { - str = coeffToString( n.c ); - - if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { - str = e <= TO_EXP_NEG || e >= TO_EXP_POS - ? toExponential( str, e ) - : toFixedPoint( str, e ); - } else { - str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); - } - - if ( s < 0 && n.c[0] ) str = '-' + str; - } - - return str; - }; - - - /* - * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole - * number. - */ - P.truncated = P.trunc = function () { - return round( new BigNumber(this), this.e + 1, 1 ); - }; - - - - /* - * Return as toString, but do not accept a base argument. - */ - P.valueOf = P.toJSON = function () { - return this.toString(); - }; - - - // Aliases for BigDecimal methods. - //P.add = P.plus; // P.add included above - //P.subtract = P.minus; // P.sub included above - //P.multiply = P.times; // P.mul included above - //P.divide = P.div; - //P.remainder = P.mod; - //P.compareTo = P.cmp; - //P.negate = P.neg; - - - if ( configObj != null ) BigNumber.config(configObj); - - return BigNumber; - } - - - // PRIVATE HELPER FUNCTIONS - - - function bitFloor(n) { - var i = n | 0; - return n > 0 || n === i ? i : i - 1; - } - - - // Return a coefficient array as a string of base 10 digits. - function coeffToString(a) { - var s, z, - i = 1, - j = a.length, - r = a[0] + ''; - - for ( ; i < j; ) { - s = a[i++] + ''; - z = LOG_BASE - s.length; - for ( ; z--; s = '0' + s ); - r += s; - } - - // Determine trailing zeros. - for ( j = r.length; r.charCodeAt(--j) === 48; ); - return r.slice( 0, j + 1 || 1 ); - } - - - // Compare the value of BigNumbers x and y. - function compare( x, y ) { - var a, b, - xc = x.c, - yc = y.c, - i = x.s, - j = y.s, - k = x.e, - l = y.e; - - // Either NaN? - if ( !i || !j ) return null; - - a = xc && !xc[0]; - b = yc && !yc[0]; - - // Either zero? - if ( a || b ) return a ? b ? 0 : -j : i; - - // Signs differ? - if ( i != j ) return i; - - a = i < 0; - b = k == l; - - // Either Infinity? - if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; - - // Compare exponents. - if ( !b ) return k > l ^ a ? 1 : -1; - - j = ( k = xc.length ) < ( l = yc.length ) ? k : l; - - // Compare digit by digit. - for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; - - // Compare lengths. - return k == l ? 0 : k > l ^ a ? 1 : -1; - } - - - /* - * Return true if n is a valid number in range, otherwise false. - * Use for argument validation when ERRORS is false. - * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. - */ - function intValidatorNoErrors( n, min, max ) { - return ( n = truncate(n) ) >= min && n <= max; - } - - - function isArray(obj) { - return Object.prototype.toString.call(obj) == '[object Array]'; - } - - - /* - * Convert string of baseIn to an array of numbers of baseOut. - * Eg. convertBase('255', 10, 16) returns [15, 15]. - * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. - */ - function toBaseOut( str, baseIn, baseOut ) { - var j, - arr = [0], - arrL, - i = 0, - len = str.length; - - for ( ; i < len; ) { - for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); - arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); - - for ( ; j < arr.length; j++ ) { - - if ( arr[j] > baseOut - 1 ) { - if ( arr[j + 1] == null ) arr[j + 1] = 0; - arr[j + 1] += arr[j] / baseOut | 0; - arr[j] %= baseOut; - } - } - } - - return arr.reverse(); - } - - - function toExponential( str, e ) { - return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + - ( e < 0 ? 'e' : 'e+' ) + e; - } - - - function toFixedPoint( str, e ) { - var len, z; - - // Negative exponent? - if ( e < 0 ) { - - // Prepend zeros. - for ( z = '0.'; ++e; z += '0' ); - str = z + str; - - // Positive exponent - } else { - len = str.length; - - // Append zeros. - if ( ++e > len ) { - for ( z = '0', e -= len; --e; z += '0' ); - str += z; - } else if ( e < len ) { - str = str.slice( 0, e ) + '.' + str.slice(e); - } - } - - return str; - } - - - function truncate(n) { - n = parseFloat(n); - return n < 0 ? mathceil(n) : mathfloor(n); - } - - - // EXPORT - - - BigNumber = another(); - - // AMD. - if ( typeof define == 'function' && define.amd ) { - define( function () { return BigNumber; } ); - - // Node and other environments that support module.exports. - } else if ( typeof module != 'undefined' && module.exports ) { - module.exports = BigNumber; - if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} - - // Browser. - } else { - global.BigNumber = BigNumber; - } -})(this); - -},{"crypto":50}],"web3":[function(require,module,exports){ +/*! bignumber.js v4.0.2 https://github.com/MikeMcl/bignumber.js/LICENCE */ + +;(function (globalObj) { + 'use strict'; + + /* + bignumber.js v4.0.2 + A JavaScript library for arbitrary-precision arithmetic. + https://github.com/MikeMcl/bignumber.js + Copyright (c) 2017 Michael Mclaughlin + MIT Expat Licence + */ + + + var BigNumber, + isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, + mathceil = Math.ceil, + mathfloor = Math.floor, + notBool = ' not a boolean or binary digit', + roundingMode = 'rounding mode', + tooManyDigits = 'number type has more than 15 significant digits', + ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', + BASE = 1e14, + LOG_BASE = 14, + MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 + // MAX_INT32 = 0x7fffffff, // 2^31 - 1 + POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], + SQRT_BASE = 1e7, + + /* + * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and + * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an + * exception is thrown (if ERRORS is true). + */ + MAX = 1E9; // 0 to MAX_INT32 + + + /* + * Create and return a BigNumber constructor. + */ + function constructorFactory(config) { + var div, parseNumeric, + + // id tracks the caller function, so its name can be included in error messages. + id = 0, + P = BigNumber.prototype, + ONE = new BigNumber(1), + + + /********************************* EDITABLE DEFAULTS **********************************/ + + + /* + * The default values below must be integers within the inclusive ranges stated. + * The values can also be changed at run-time using BigNumber.config. + */ + + // The maximum number of decimal places for operations involving division. + DECIMAL_PLACES = 20, // 0 to MAX + + /* + * The rounding mode used when rounding to the above decimal places, and when using + * toExponential, toFixed, toFormat and toPrecision, and round (default value). + * UP 0 Away from zero. + * DOWN 1 Towards zero. + * CEIL 2 Towards +Infinity. + * FLOOR 3 Towards -Infinity. + * HALF_UP 4 Towards nearest neighbour. If equidistant, up. + * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. + * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. + * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. + * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. + */ + ROUNDING_MODE = 4, // 0 to 8 + + // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] + + // The exponent value at and beneath which toString returns exponential notation. + // Number type: -7 + TO_EXP_NEG = -7, // 0 to -MAX + + // The exponent value at and above which toString returns exponential notation. + // Number type: 21 + TO_EXP_POS = 21, // 0 to MAX + + // RANGE : [MIN_EXP, MAX_EXP] + + // The minimum exponent value, beneath which underflow to zero occurs. + // Number type: -324 (5e-324) + MIN_EXP = -1e7, // -1 to -MAX + + // The maximum exponent value, above which overflow to Infinity occurs. + // Number type: 308 (1.7976931348623157e+308) + // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. + MAX_EXP = 1e7, // 1 to MAX + + // Whether BigNumber Errors are ever thrown. + ERRORS = true, // true or false + + // Change to intValidatorNoErrors if ERRORS is false. + isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors + + // Whether to use cryptographically-secure random number generation, if available. + CRYPTO = false, // true or false + + /* + * The modulo mode used when calculating the modulus: a mod n. + * The quotient (q = a / n) is calculated according to the corresponding rounding mode. + * The remainder (r) is calculated as: r = a - n * q. + * + * UP 0 The remainder is positive if the dividend is negative, else is negative. + * DOWN 1 The remainder has the same sign as the dividend. + * This modulo mode is commonly known as 'truncated division' and is + * equivalent to (a % n) in JavaScript. + * FLOOR 3 The remainder has the same sign as the divisor (Python %). + * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. + * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). + * The remainder is always positive. + * + * The truncated division, floored division, Euclidian division and IEEE 754 remainder + * modes are commonly used for the modulus operation. + * Although the other rounding modes can also be used, they may not give useful results. + */ + MODULO_MODE = 1, // 0 to 9 + + // The maximum number of significant digits of the result of the toPower operation. + // If POW_PRECISION is 0, there will be unlimited significant digits. + POW_PRECISION = 0, // 0 to MAX + + // The format specification used by the BigNumber.prototype.toFormat method. + FORMAT = { + decimalSeparator: '.', + groupSeparator: ',', + groupSize: 3, + secondaryGroupSize: 0, + fractionGroupSeparator: '\xA0', // non-breaking space + fractionGroupSize: 0 + }; + + + /******************************************************************************************/ + + + // CONSTRUCTOR + + + /* + * The BigNumber constructor and exported function. + * Create and return a new instance of a BigNumber object. + * + * n {number|string|BigNumber} A numeric value. + * [b] {number} The base of n. Integer, 2 to 64 inclusive. + */ + function BigNumber( n, b ) { + var c, e, i, num, len, str, + x = this; + + // Enable constructor usage without new. + if ( !( x instanceof BigNumber ) ) { + + // 'BigNumber() constructor call without new: {n}' + if (ERRORS) raise( 26, 'constructor call without new', n ); + return new BigNumber( n, b ); + } + + // 'new BigNumber() base not an integer: {b}' + // 'new BigNumber() base out of range: {b}' + if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { + + // Duplicate. + if ( n instanceof BigNumber ) { + x.s = n.s; + x.e = n.e; + x.c = ( n = n.c ) ? n.slice() : n; + id = 0; + return; + } + + if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { + x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; + + // Fast path for integers. + if ( n === ~~n ) { + for ( e = 0, i = n; i >= 10; i /= 10, e++ ); + x.e = e; + x.c = [n]; + id = 0; + return; + } + + str = n + ''; + } else { + if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + } else { + b = b | 0; + str = n + ''; + + // Ensure return value is rounded to DECIMAL_PLACES as with other bases. + // Allow exponential notation to be used with base 10 argument. + if ( b == 10 ) { + x = new BigNumber( n instanceof BigNumber ? n : str ); + return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); + } + + // Avoid potential interpretation of Infinity and NaN as base 44+ values. + // Any number in exponential form will fail due to the [Ee][+-]. + if ( ( num = typeof n == 'number' ) && n * 0 != 0 || + !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + + '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { + return parseNumeric( x, str, num, b ); + } + + if (num) { + x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; + + if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { + + // 'new BigNumber() number type has more than 15 significant digits: {n}' + raise( id, tooManyDigits, n ); + } + + // Prevent later check for length on converted number. + num = false; + } else { + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + + str = convertBase( str, 10, b, x.s ); + } + + // Decimal point? + if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); + + // Exponential form? + if ( ( i = str.search( /e/i ) ) > 0 ) { + + // Determine exponent. + if ( e < 0 ) e = i; + e += +str.slice( i + 1 ); + str = str.substring( 0, i ); + } else if ( e < 0 ) { + + // Integer. + e = str.length; + } + + // Determine leading zeros. + for ( i = 0; str.charCodeAt(i) === 48; i++ ); + + // Determine trailing zeros. + for ( len = str.length; str.charCodeAt(--len) === 48; ); + str = str.slice( i, len + 1 ); + + if (str) { + len = str.length; + + // Disallow numbers with over 15 significant digits if number type. + // 'new BigNumber() number type has more than 15 significant digits: {n}' + if ( num && ERRORS && len > 15 && ( n > MAX_SAFE_INTEGER || n !== mathfloor(n) ) ) { + raise( id, tooManyDigits, x.s * n ); + } + + e = e - i - 1; + + // Overflow? + if ( e > MAX_EXP ) { + + // Infinity. + x.c = x.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + x.c = [ x.e = 0 ]; + } else { + x.e = e; + x.c = []; + + // Transform base + + // e is the base 10 exponent. + // i is where to slice str to get the first element of the coefficient array. + i = ( e + 1 ) % LOG_BASE; + if ( e < 0 ) i += LOG_BASE; + + if ( i < len ) { + if (i) x.c.push( +str.slice( 0, i ) ); + + for ( len -= LOG_BASE; i < len; ) { + x.c.push( +str.slice( i, i += LOG_BASE ) ); + } + + str = str.slice(i); + i = LOG_BASE - str.length; + } else { + i -= len; + } + + for ( ; i--; str += '0' ); + x.c.push( +str ); + } + } else { + + // Zero. + x.c = [ x.e = 0 ]; + } + + id = 0; + } + + + // CONSTRUCTOR PROPERTIES + + + BigNumber.another = constructorFactory; + + BigNumber.ROUND_UP = 0; + BigNumber.ROUND_DOWN = 1; + BigNumber.ROUND_CEIL = 2; + BigNumber.ROUND_FLOOR = 3; + BigNumber.ROUND_HALF_UP = 4; + BigNumber.ROUND_HALF_DOWN = 5; + BigNumber.ROUND_HALF_EVEN = 6; + BigNumber.ROUND_HALF_CEIL = 7; + BigNumber.ROUND_HALF_FLOOR = 8; + BigNumber.EUCLID = 9; + + + /* + * Configure infrequently-changing library-wide settings. + * + * Accept an object or an argument list, with one or many of the following properties or + * parameters respectively: + * + * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive + * ROUNDING_MODE {number} Integer, 0 to 8 inclusive + * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or + * [integer -MAX to 0 incl., 0 to MAX incl.] + * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + * [integer -MAX to -1 incl., integer 1 to MAX incl.] + * ERRORS {boolean|number} true, false, 1 or 0 + * CRYPTO {boolean|number} true, false, 1 or 0 + * MODULO_MODE {number} 0 to 9 inclusive + * POW_PRECISION {number} 0 to MAX inclusive + * FORMAT {object} See BigNumber.prototype.toFormat + * decimalSeparator {string} + * groupSeparator {string} + * groupSize {number} + * secondaryGroupSize {number} + * fractionGroupSeparator {string} + * fractionGroupSize {number} + * + * (The values assigned to the above FORMAT object properties are not checked for validity.) + * + * E.g. + * BigNumber.config(20, 4) is equivalent to + * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) + * + * Ignore properties/parameters set to null or undefined. + * Return an object with the properties current values. + */ + BigNumber.config = BigNumber.set = function () { + var v, p, + i = 0, + r = {}, + a = arguments, + o = a[0], + has = o && typeof o == 'object' + ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } + : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; + + // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. + // 'config() DECIMAL_PLACES not an integer: {v}' + // 'config() DECIMAL_PLACES out of range: {v}' + if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { + DECIMAL_PLACES = v | 0; + } + r[p] = DECIMAL_PLACES; + + // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. + // 'config() ROUNDING_MODE not an integer: {v}' + // 'config() ROUNDING_MODE out of range: {v}' + if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { + ROUNDING_MODE = v | 0; + } + r[p] = ROUNDING_MODE; + + // EXPONENTIAL_AT {number|number[]} + // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. + // 'config() EXPONENTIAL_AT not an integer: {v}' + // 'config() EXPONENTIAL_AT out of range: {v}' + if ( has( p = 'EXPONENTIAL_AT' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { + TO_EXP_NEG = v[0] | 0; + TO_EXP_POS = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); + } + } + r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; + + // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. + // 'config() RANGE not an integer: {v}' + // 'config() RANGE cannot be zero: {v}' + // 'config() RANGE out of range: {v}' + if ( has( p = 'RANGE' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { + MIN_EXP = v[0] | 0; + MAX_EXP = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); + else if (ERRORS) raise( 2, p + ' cannot be zero', v ); + } + } + r[p] = [ MIN_EXP, MAX_EXP ]; + + // ERRORS {boolean|number} true, false, 1 or 0. + // 'config() ERRORS not a boolean or binary digit: {v}' + if ( has( p = 'ERRORS' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + id = 0; + isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = ERRORS; + + // CRYPTO {boolean|number} true, false, 1 or 0. + // 'config() CRYPTO not a boolean or binary digit: {v}' + // 'config() crypto unavailable: {crypto}' + if ( has( p = 'CRYPTO' ) ) { + + if ( v === true || v === false || v === 1 || v === 0 ) { + if (v) { + v = typeof crypto == 'undefined'; + if ( !v && crypto && (crypto.getRandomValues || crypto.randomBytes)) { + CRYPTO = true; + } else if (ERRORS) { + raise( 2, 'crypto unavailable', v ? void 0 : crypto ); + } else { + CRYPTO = false; + } + } else { + CRYPTO = false; + } + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = CRYPTO; + + // MODULO_MODE {number} Integer, 0 to 9 inclusive. + // 'config() MODULO_MODE not an integer: {v}' + // 'config() MODULO_MODE out of range: {v}' + if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { + MODULO_MODE = v | 0; + } + r[p] = MODULO_MODE; + + // POW_PRECISION {number} Integer, 0 to MAX inclusive. + // 'config() POW_PRECISION not an integer: {v}' + // 'config() POW_PRECISION out of range: {v}' + if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { + POW_PRECISION = v | 0; + } + r[p] = POW_PRECISION; + + // FORMAT {object} + // 'config() FORMAT not an object: {v}' + if ( has( p = 'FORMAT' ) ) { + + if ( typeof v == 'object' ) { + FORMAT = v; + } else if (ERRORS) { + raise( 2, p + ' not an object', v ); + } + } + r[p] = FORMAT; + + return r; + }; + + + /* + * Return a new BigNumber whose value is the maximum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; + + + /* + * Return a new BigNumber whose value is the minimum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; + + + /* + * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, + * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing + * zeros are produced). + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * + * 'random() decimal places not an integer: {dp}' + * 'random() decimal places out of range: {dp}' + * 'random() crypto unavailable: {crypto}' + */ + BigNumber.random = (function () { + var pow2_53 = 0x20000000000000; + + // Return a 53 bit integer n, where 0 <= n < 9007199254740992. + // Check if Math.random() produces more than 32 bits of randomness. + // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. + // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. + var random53bitInt = (Math.random() * pow2_53) & 0x1fffff + ? function () { return mathfloor( Math.random() * pow2_53 ); } + : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + + (Math.random() * 0x800000 | 0); }; + + return function (dp) { + var a, b, e, k, v, + i = 0, + c = [], + rand = new BigNumber(ONE); + + dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; + k = mathceil( dp / LOG_BASE ); + + if (CRYPTO) { + + // Browsers supporting crypto.getRandomValues. + if (crypto.getRandomValues) { + + a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); + + for ( ; i < k; ) { + + // 53 bits: + // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) + // 11111 11111111 11111111 11111111 11100000 00000000 00000000 + // ((Math.pow(2, 32) - 1) >>> 11).toString(2) + // 11111 11111111 11111111 + // 0x20000 is 2^21. + v = a[i] * 0x20000 + (a[i + 1] >>> 11); + + // Rejection sampling: + // 0 <= v < 9007199254740992 + // Probability that v >= 9e15, is + // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 + if ( v >= 9e15 ) { + b = crypto.getRandomValues( new Uint32Array(2) ); + a[i] = b[0]; + a[i + 1] = b[1]; + } else { + + // 0 <= v <= 8999999999999999 + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 2; + } + } + i = k / 2; + + // Node.js supporting crypto.randomBytes. + } else if (crypto.randomBytes) { + + // buffer + a = crypto.randomBytes( k *= 7 ); + + for ( ; i < k; ) { + + // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 + // 0x100000000 is 2^32, 0x1000000 is 2^24 + // 11111 11111111 11111111 11111111 11111111 11111111 11111111 + // 0 <= v < 9007199254740992 + v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + + ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + + ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; + + if ( v >= 9e15 ) { + crypto.randomBytes(7).copy( a, i ); + } else { + + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 7; + } + } + i = k / 7; + } else { + CRYPTO = false; + if (ERRORS) raise( 14, 'crypto unavailable', crypto ); + } + } + + // Use Math.random. + if (!CRYPTO) { + + for ( ; i < k; ) { + v = random53bitInt(); + if ( v < 9e15 ) c[i++] = v % 1e14; + } + } + + k = c[--i]; + dp %= LOG_BASE; + + // Convert trailing digits to zeros according to dp. + if ( k && dp ) { + v = POWS_TEN[LOG_BASE - dp]; + c[i] = mathfloor( k / v ) * v; + } + + // Remove trailing elements which are zero. + for ( ; c[i] === 0; c.pop(), i-- ); + + // Zero? + if ( i < 0 ) { + c = [ e = 0 ]; + } else { + + // Remove leading elements which are zero and adjust exponent accordingly. + for ( e = -1 ; c[0] === 0; c.splice(0, 1), e -= LOG_BASE); + + // Count the digits of the first element of c to determine leading zeros, and... + for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); + + // adjust the exponent accordingly. + if ( i < LOG_BASE ) e -= LOG_BASE - i; + } + + rand.e = e; + rand.c = c; + return rand; + }; + })(); + + + // PRIVATE FUNCTIONS + + + // Convert a numeric string of baseIn to a numeric string of baseOut. + function convertBase( str, baseOut, baseIn, sign ) { + var d, e, k, r, x, xc, y, + i = str.indexOf( '.' ), + dp = DECIMAL_PLACES, + rm = ROUNDING_MODE; + + if ( baseIn < 37 ) str = str.toLowerCase(); + + // Non-integer. + if ( i >= 0 ) { + k = POW_PRECISION; + + // Unlimited precision. + POW_PRECISION = 0; + str = str.replace( '.', '' ); + y = new BigNumber(baseIn); + x = y.pow( str.length - i ); + POW_PRECISION = k; + + // Convert str as if an integer, then restore the fraction part by dividing the + // result by its base raised to a power. + y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); + y.e = y.c.length; + } + + // Convert the number as integer. + xc = toBaseOut( str, baseIn, baseOut ); + e = k = xc.length; + + // Remove trailing zeros. + for ( ; xc[--k] == 0; xc.pop() ); + if ( !xc[0] ) return '0'; + + if ( i < 0 ) { + --e; + } else { + x.c = xc; + x.e = e; + + // sign is needed for correct rounding. + x.s = sign; + x = div( x, y, dp, rm, baseOut ); + xc = x.c; + r = x.r; + e = x.e; + } + + d = e + dp + 1; + + // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. + i = xc[d]; + k = baseOut / 2; + r = r || d < 0 || xc[d + 1] != null; + + r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( d < 1 || !xc[0] ) { + + // 1^-dp or 0. + str = r ? toFixedPoint( '1', -dp ) : '0'; + } else { + xc.length = d; + + if (r) { + + // Rounding up may mean the previous digit has to be rounded up and so on. + for ( --baseOut; ++xc[--d] > baseOut; ) { + xc[d] = 0; + + if ( !d ) { + ++e; + xc = [1].concat(xc); + } + } + } + + // Determine trailing zeros. + for ( k = xc.length; !xc[--k]; ); + + // E.g. [4, 11, 15] becomes 4bf. + for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); + str = toFixedPoint( str, e ); + } + + // The caller will add the sign. + return str; + } + + + // Perform division in the specified base. Called by div and convertBase. + div = (function () { + + // Assume non-zero x and k. + function multiply( x, k, base ) { + var m, temp, xlo, xhi, + carry = 0, + i = x.length, + klo = k % SQRT_BASE, + khi = k / SQRT_BASE | 0; + + for ( x = x.slice(); i--; ) { + xlo = x[i] % SQRT_BASE; + xhi = x[i] / SQRT_BASE | 0; + m = khi * xlo + xhi * klo; + temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; + carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; + x[i] = temp % base; + } + + if (carry) x = [carry].concat(x); + + return x; + } + + function compare( a, b, aL, bL ) { + var i, cmp; + + if ( aL != bL ) { + cmp = aL > bL ? 1 : -1; + } else { + + for ( i = cmp = 0; i < aL; i++ ) { + + if ( a[i] != b[i] ) { + cmp = a[i] > b[i] ? 1 : -1; + break; + } + } + } + return cmp; + } + + function subtract( a, b, aL, base ) { + var i = 0; + + // Subtract b from a. + for ( ; aL--; ) { + a[aL] -= i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + + // Remove leading zeros. + for ( ; !a[0] && a.length > 1; a.splice(0, 1) ); + } + + // x: dividend, y: divisor. + return function ( x, y, dp, rm, base ) { + var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, + yL, yz, + s = x.s == y.s ? 1 : -1, + xc = x.c, + yc = y.c; + + // Either NaN, Infinity or 0? + if ( !xc || !xc[0] || !yc || !yc[0] ) { + + return new BigNumber( + + // Return NaN if either NaN, or both Infinity or 0. + !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : + + // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. + xc && xc[0] == 0 || !yc ? s * 0 : s / 0 + ); + } + + q = new BigNumber(s); + qc = q.c = []; + e = x.e - y.e; + s = dp + e + 1; + + if ( !base ) { + base = BASE; + e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); + s = s / LOG_BASE | 0; + } + + // Result exponent may be one less then the current value of e. + // The coefficients of the BigNumbers from convertBase may have trailing zeros. + for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); + if ( yc[i] > ( xc[i] || 0 ) ) e--; + + if ( s < 0 ) { + qc.push(1); + more = true; + } else { + xL = xc.length; + yL = yc.length; + i = 0; + s += 2; + + // Normalise xc and yc so highest order digit of yc is >= base / 2. + + n = mathfloor( base / ( yc[0] + 1 ) ); + + // Not necessary, but to handle odd bases where yc[0] == ( base / 2 ) - 1. + // if ( n > 1 || n++ == 1 && yc[0] < base / 2 ) { + if ( n > 1 ) { + yc = multiply( yc, n, base ); + xc = multiply( xc, n, base ); + yL = yc.length; + xL = xc.length; + } + + xi = yL; + rem = xc.slice( 0, yL ); + remL = rem.length; + + // Add zeros to make remainder as long as divisor. + for ( ; remL < yL; rem[remL++] = 0 ); + yz = yc.slice(); + yz = [0].concat(yz); + yc0 = yc[0]; + if ( yc[1] >= base / 2 ) yc0++; + // Not necessary, but to prevent trial digit n > base, when using base 3. + // else if ( base == 3 && yc0 == 1 ) yc0 = 1 + 1e-15; + + do { + n = 0; + + // Compare divisor and remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < remainder. + if ( cmp < 0 ) { + + // Calculate trial digit, n. + + rem0 = rem[0]; + if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); + + // n is how many times the divisor goes into the current remainder. + n = mathfloor( rem0 / yc0 ); + + // Algorithm: + // 1. product = divisor * trial digit (n) + // 2. if product > remainder: product -= divisor, n-- + // 3. remainder -= product + // 4. if product was < remainder at 2: + // 5. compare new remainder and divisor + // 6. If remainder > divisor: remainder -= divisor, n++ + + if ( n > 1 ) { + + // n may be > base only when base is 3. + if (n >= base) n = base - 1; + + // product = divisor * trial digit. + prod = multiply( yc, n, base ); + prodL = prod.length; + remL = rem.length; + + // Compare product and remainder. + // If product > remainder. + // Trial digit n too high. + // n is 1 too high about 5% of the time, and is not known to have + // ever been more than 1 too high. + while ( compare( prod, rem, prodL, remL ) == 1 ) { + n--; + + // Subtract divisor from product. + subtract( prod, yL < prodL ? yz : yc, prodL, base ); + prodL = prod.length; + cmp = 1; + } + } else { + + // n is 0 or 1, cmp is -1. + // If n is 0, there is no need to compare yc and rem again below, + // so change cmp to 1 to avoid it. + // If n is 1, leave cmp as -1, so yc and rem are compared again. + if ( n == 0 ) { + + // divisor < remainder, so n must be at least 1. + cmp = n = 1; + } + + // product = divisor + prod = yc.slice(); + prodL = prod.length; + } + + if ( prodL < remL ) prod = [0].concat(prod); + + // Subtract product from remainder. + subtract( rem, prod, remL, base ); + remL = rem.length; + + // If product was < remainder. + if ( cmp == -1 ) { + + // Compare divisor and new remainder. + // If divisor < new remainder, subtract divisor from remainder. + // Trial digit n too low. + // n is 1 too low about 5% of the time, and very rarely 2 too low. + while ( compare( yc, rem, yL, remL ) < 1 ) { + n++; + + // Subtract divisor from remainder. + subtract( rem, yL < remL ? yz : yc, remL, base ); + remL = rem.length; + } + } + } else if ( cmp === 0 ) { + n++; + rem = [0]; + } // else cmp === 1 and n will be 0 + + // Add the next digit, n, to the result array. + qc[i++] = n; + + // Update the remainder. + if ( rem[0] ) { + rem[remL++] = xc[xi] || 0; + } else { + rem = [ xc[xi] ]; + remL = 1; + } + } while ( ( xi++ < xL || rem[0] != null ) && s-- ); + + more = rem[0] != null; + + // Leading zero? + if ( !qc[0] ) qc.splice(0, 1); + } + + if ( base == BASE ) { + + // To calculate q.e, first get the number of digits of qc[0]. + for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); + round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); + + // Caller is convertBase. + } else { + q.e = e; + q.r = +more; + } + + return q; + }; + })(); + + + /* + * Return a string representing the value of BigNumber n in fixed-point or exponential + * notation rounded to the specified decimal places or significant digits. + * + * n is a BigNumber. + * i is the index of the last digit required (i.e. the digit that may be rounded up). + * rm is the rounding mode. + * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. + */ + function format( n, i, rm, caller ) { + var c0, e, ne, len, str; + + rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) + ? rm | 0 : ROUNDING_MODE; + + if ( !n.c ) return n.toString(); + c0 = n.c[0]; + ne = n.e; + + if ( i == null ) { + str = coeffToString( n.c ); + str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG + ? toExponential( str, ne ) + : toFixedPoint( str, ne ); + } else { + n = round( new BigNumber(n), i, rm ); + + // n.e may have changed if the value was rounded up. + e = n.e; + + str = coeffToString( n.c ); + len = str.length; + + // toPrecision returns exponential notation if the number of significant digits + // specified is less than the number of digits necessary to represent the integer + // part of the value in fixed-point notation. + + // Exponential notation. + if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { + + // Append zeros? + for ( ; len < i; str += '0', len++ ); + str = toExponential( str, e ); + + // Fixed-point notation. + } else { + i -= ne; + str = toFixedPoint( str, e ); + + // Append zeros? + if ( e + 1 > len ) { + if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); + } else { + i += e - len; + if ( i > 0 ) { + if ( e + 1 == len ) str += '.'; + for ( ; i--; str += '0' ); + } + } + } + } + + return n.s < 0 && c0 ? '-' + str : str; + } + + + // Handle BigNumber.max and BigNumber.min. + function maxOrMin( args, method ) { + var m, n, + i = 0; + + if ( isArray( args[0] ) ) args = args[0]; + m = new BigNumber( args[0] ); + + for ( ; ++i < args.length; ) { + n = new BigNumber( args[i] ); + + // If any number is NaN, return NaN. + if ( !n.s ) { + m = n; + break; + } else if ( method.call( m, n ) ) { + m = n; + } + } + + return m; + } + + + /* + * Return true if n is an integer in range, otherwise throw. + * Use for argument validation when ERRORS is true. + */ + function intValidatorWithErrors( n, min, max, caller, name ) { + if ( n < min || n > max || n != truncate(n) ) { + raise( caller, ( name || 'decimal places' ) + + ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); + } + + return true; + } + + + /* + * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. + * Called by minus, plus and times. + */ + function normalise( n, c, e ) { + var i = 1, + j = c.length; + + // Remove trailing zeros. + for ( ; !c[--j]; c.pop() ); + + // Calculate the base 10 exponent. First get the number of digits of c[0]. + for ( j = c[0]; j >= 10; j /= 10, i++ ); + + // Overflow? + if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { + + // Infinity. + n.c = n.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + n.c = [ n.e = 0 ]; + } else { + n.e = e; + n.c = c; + } + + return n; + } + + + // Handle values that fail the validity test in BigNumber. + parseNumeric = (function () { + var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i, + dotAfter = /^([^.]+)\.$/, + dotBefore = /^\.([^.]+)$/, + isInfinityOrNaN = /^-?(Infinity|NaN)$/, + whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g; + + return function ( x, str, num, b ) { + var base, + s = num ? str : str.replace( whitespaceOrPlus, '' ); + + // No exception on ±Infinity or NaN. + if ( isInfinityOrNaN.test(s) ) { + x.s = isNaN(s) ? null : s < 0 ? -1 : 1; + } else { + if ( !num ) { + + // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i + s = s.replace( basePrefix, function ( m, p1, p2 ) { + base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; + return !b || b == base ? p1 : m; + }); + + if (b) { + base = b; + + // E.g. '1.' to '1', '.1' to '0.1' + s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); + } + + if ( str != s ) return new BigNumber( s, base ); + } + + // 'new BigNumber() not a number: {n}' + // 'new BigNumber() not a base {b} number: {n}' + if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); + x.s = null; + } + + x.c = x.e = null; + id = 0; + } + })(); + + + // Throw a BigNumber Error. + function raise( caller, msg, val ) { + var error = new Error( [ + 'new BigNumber', // 0 + 'cmp', // 1 + 'config', // 2 + 'div', // 3 + 'divToInt', // 4 + 'eq', // 5 + 'gt', // 6 + 'gte', // 7 + 'lt', // 8 + 'lte', // 9 + 'minus', // 10 + 'mod', // 11 + 'plus', // 12 + 'precision', // 13 + 'random', // 14 + 'round', // 15 + 'shift', // 16 + 'times', // 17 + 'toDigits', // 18 + 'toExponential', // 19 + 'toFixed', // 20 + 'toFormat', // 21 + 'toFraction', // 22 + 'pow', // 23 + 'toPrecision', // 24 + 'toString', // 25 + 'BigNumber' // 26 + ][caller] + '() ' + msg + ': ' + val ); + + error.name = 'BigNumber Error'; + id = 0; + throw error; + } + + + /* + * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. + * If r is truthy, it is known that there are more digits after the rounding digit. + */ + function round( x, sd, rm, r ) { + var d, i, j, k, n, ni, rd, + xc = x.c, + pows10 = POWS_TEN; + + // if x is not Infinity or NaN... + if (xc) { + + // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. + // n is a base 1e14 number, the value of the element of array x.c containing rd. + // ni is the index of n within x.c. + // d is the number of digits of n. + // i is the index of rd within n including leading zeros. + // j is the actual index of rd within n (if < 0, rd is a leading zero). + out: { + + // Get the number of digits of the first element of xc. + for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); + i = sd - d; + + // If the rounding digit is in the first element of xc... + if ( i < 0 ) { + i += LOG_BASE; + j = sd; + n = xc[ ni = 0 ]; + + // Get the rounding digit at index j of n. + rd = n / pows10[ d - j - 1 ] % 10 | 0; + } else { + ni = mathceil( ( i + 1 ) / LOG_BASE ); + + if ( ni >= xc.length ) { + + if (r) { + + // Needed by sqrt. + for ( ; xc.length <= ni; xc.push(0) ); + n = rd = 0; + d = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + break out; + } + } else { + n = k = xc[ni]; + + // Get the number of digits of n. + for ( d = 1; k >= 10; k /= 10, d++ ); + + // Get the index of rd within n. + i %= LOG_BASE; + + // Get the index of rd within n, adjusted for leading zeros. + // The number of leading zeros of n is given by LOG_BASE - d. + j = i - LOG_BASE + d; + + // Get the rounding digit at index j of n. + rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; + } + } + + r = r || sd < 0 || + + // Are there any non-zero digits after the rounding digit? + // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right + // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. + xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); + + r = rm < 4 + ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && + + // Check whether the digit to the left of the rounding digit is odd. + ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( sd < 1 || !xc[0] ) { + xc.length = 0; + + if (r) { + + // Convert sd to decimal places. + sd -= x.e + 1; + + // 1, 0.1, 0.01, 0.001, 0.0001 etc. + xc[0] = pows10[ ( LOG_BASE - sd % LOG_BASE ) % LOG_BASE ]; + x.e = -sd || 0; + } else { + + // Zero. + xc[0] = x.e = 0; + } + + return x; + } + + // Remove excess digits. + if ( i == 0 ) { + xc.length = ni; + k = 1; + ni--; + } else { + xc.length = ni + 1; + k = pows10[ LOG_BASE - i ]; + + // E.g. 56700 becomes 56000 if 7 is the rounding digit. + // j > 0 means i > number of leading zeros of n. + xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; + } + + // Round up? + if (r) { + + for ( ; ; ) { + + // If the digit to be rounded up is in the first element of xc... + if ( ni == 0 ) { + + // i will be the length of xc[0] before k is added. + for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); + j = xc[0] += k; + for ( k = 1; j >= 10; j /= 10, k++ ); + + // if i != k the length has increased. + if ( i != k ) { + x.e++; + if ( xc[0] == BASE ) xc[0] = 1; + } + + break; + } else { + xc[ni] += k; + if ( xc[ni] != BASE ) break; + xc[ni--] = 0; + k = 1; + } + } + } + + // Remove trailing zeros. + for ( i = xc.length; xc[--i] === 0; xc.pop() ); + } + + // Overflow? Infinity. + if ( x.e > MAX_EXP ) { + x.c = x.e = null; + + // Underflow? Zero. + } else if ( x.e < MIN_EXP ) { + x.c = [ x.e = 0 ]; + } + } + + return x; + } + + + // PROTOTYPE/INSTANCE METHODS + + + /* + * Return a new BigNumber whose value is the absolute value of this BigNumber. + */ + P.absoluteValue = P.abs = function () { + var x = new BigNumber(this); + if ( x.s < 0 ) x.s = 1; + return x; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of Infinity. + */ + P.ceil = function () { + return round( new BigNumber(this), this.e + 1, 2 ); + }; + + + /* + * Return + * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), + * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), + * 0 if they have the same value, + * or null if the value of either is NaN. + */ + P.comparedTo = P.cmp = function ( y, b ) { + id = 1; + return compare( this, new BigNumber( y, b ) ); + }; + + + /* + * Return the number of decimal places of the value of this BigNumber, or null if the value + * of this BigNumber is ±Infinity or NaN. + */ + P.decimalPlaces = P.dp = function () { + var n, v, + c = this.c; + + if ( !c ) return null; + n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; + + // Subtract the number of trailing zeros of the last number. + if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); + if ( n < 0 ) n = 0; + + return n; + }; + + + /* + * n / 0 = I + * n / N = N + * n / I = 0 + * 0 / n = 0 + * 0 / 0 = N + * 0 / N = N + * 0 / I = 0 + * N / n = N + * N / 0 = N + * N / N = N + * N / I = N + * I / n = I + * I / 0 = I + * I / N = N + * I / I = N + * + * Return a new BigNumber whose value is the value of this BigNumber divided by the value of + * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.dividedBy = P.div = function ( y, b ) { + id = 3; + return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); + }; + + + /* + * Return a new BigNumber whose value is the integer part of dividing the value of this + * BigNumber by the value of BigNumber(y, b). + */ + P.dividedToIntegerBy = P.divToInt = function ( y, b ) { + id = 4; + return div( this, new BigNumber( y, b ), 0, 1 ); + }; + + + /* + * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), + * otherwise returns false. + */ + P.equals = P.eq = function ( y, b ) { + id = 5; + return compare( this, new BigNumber( y, b ) ) === 0; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of -Infinity. + */ + P.floor = function () { + return round( new BigNumber(this), this.e + 1, 3 ); + }; + + + /* + * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.greaterThan = P.gt = function ( y, b ) { + id = 6; + return compare( this, new BigNumber( y, b ) ) > 0; + }; + + + /* + * Return true if the value of this BigNumber is greater than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.greaterThanOrEqualTo = P.gte = function ( y, b ) { + id = 7; + return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; + + }; + + + /* + * Return true if the value of this BigNumber is a finite number, otherwise returns false. + */ + P.isFinite = function () { + return !!this.c; + }; + + + /* + * Return true if the value of this BigNumber is an integer, otherwise return false. + */ + P.isInteger = P.isInt = function () { + return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; + }; + + + /* + * Return true if the value of this BigNumber is NaN, otherwise returns false. + */ + P.isNaN = function () { + return !this.s; + }; + + + /* + * Return true if the value of this BigNumber is negative, otherwise returns false. + */ + P.isNegative = P.isNeg = function () { + return this.s < 0; + }; + + + /* + * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. + */ + P.isZero = function () { + return !!this.c && this.c[0] == 0; + }; + + + /* + * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.lessThan = P.lt = function ( y, b ) { + id = 8; + return compare( this, new BigNumber( y, b ) ) < 0; + }; + + + /* + * Return true if the value of this BigNumber is less than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.lessThanOrEqualTo = P.lte = function ( y, b ) { + id = 9; + return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; + }; + + + /* + * n - 0 = n + * n - N = N + * n - I = -I + * 0 - n = -n + * 0 - 0 = 0 + * 0 - N = N + * 0 - I = -I + * N - n = N + * N - 0 = N + * N - N = N + * N - I = N + * I - n = I + * I - 0 = I + * I - N = N + * I - I = N + * + * Return a new BigNumber whose value is the value of this BigNumber minus the value of + * BigNumber(y, b). + */ + P.minus = P.sub = function ( y, b ) { + var i, j, t, xLTy, + x = this, + a = x.s; + + id = 10; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.plus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Either Infinity? + if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); + + // Either zero? + if ( !xc[0] || !yc[0] ) { + + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : + + // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity + ROUNDING_MODE == 3 ? -0 : 0 ); + } + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Determine which is the bigger number. + if ( a = xe - ye ) { + + if ( xLTy = a < 0 ) { + a = -a; + t = xc; + } else { + ye = xe; + t = yc; + } + + t.reverse(); + + // Prepend zeros to equalise exponents. + for ( b = a; b--; t.push(0) ); + t.reverse(); + } else { + + // Exponents equal. Check digit by digit. + j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; + + for ( a = b = 0; b < j; b++ ) { + + if ( xc[b] != yc[b] ) { + xLTy = xc[b] < yc[b]; + break; + } + } + } + + // x < y? Point xc to the array of the bigger number. + if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; + + b = ( j = yc.length ) - ( i = xc.length ); + + // Append zeros to xc if shorter. + // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. + if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); + b = BASE - 1; + + // Subtract yc from xc. + for ( ; j > a; ) { + + if ( xc[--j] < yc[j] ) { + for ( i = j; i && !xc[--i]; xc[i] = b ); + --xc[i]; + xc[j] += BASE; + } + + xc[j] -= yc[j]; + } + + // Remove leading zeros and adjust exponent accordingly. + for ( ; xc[0] == 0; xc.splice(0, 1), --ye ); + + // Zero? + if ( !xc[0] ) { + + // Following IEEE 754 (2008) 6.3, + // n - n = +0 but n - n = -0 when rounding towards -Infinity. + y.s = ROUNDING_MODE == 3 ? -1 : 1; + y.c = [ y.e = 0 ]; + return y; + } + + // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity + // for finite x and y. + return normalise( y, xc, ye ); + }; + + + /* + * n % 0 = N + * n % N = N + * n % I = n + * 0 % n = 0 + * -0 % n = -0 + * 0 % 0 = N + * 0 % N = N + * 0 % I = 0 + * N % n = N + * N % 0 = N + * N % N = N + * N % I = N + * I % n = N + * I % 0 = N + * I % N = N + * I % I = N + * + * Return a new BigNumber whose value is the value of this BigNumber modulo the value of + * BigNumber(y, b). The result depends on the value of MODULO_MODE. + */ + P.modulo = P.mod = function ( y, b ) { + var q, s, + x = this; + + id = 11; + y = new BigNumber( y, b ); + + // Return NaN if x is Infinity or NaN, or y is NaN or zero. + if ( !x.c || !y.s || y.c && !y.c[0] ) { + return new BigNumber(NaN); + + // Return x if y is Infinity or x is zero. + } else if ( !y.c || x.c && !x.c[0] ) { + return new BigNumber(x); + } + + if ( MODULO_MODE == 9 ) { + + // Euclidian division: q = sign(y) * floor(x / abs(y)) + // r = x - qy where 0 <= r < abs(y) + s = y.s; + y.s = 1; + q = div( x, y, 0, 3 ); + y.s = s; + q.s *= s; + } else { + q = div( x, y, 0, MODULO_MODE ); + } + + return x.minus( q.times(y) ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber negated, + * i.e. multiplied by -1. + */ + P.negated = P.neg = function () { + var x = new BigNumber(this); + x.s = -x.s || null; + return x; + }; + + + /* + * n + 0 = n + * n + N = N + * n + I = I + * 0 + n = n + * 0 + 0 = 0 + * 0 + N = N + * 0 + I = I + * N + n = N + * N + 0 = N + * N + N = N + * N + I = N + * I + n = I + * I + 0 = I + * I + N = N + * I + I = I + * + * Return a new BigNumber whose value is the value of this BigNumber plus the value of + * BigNumber(y, b). + */ + P.plus = P.add = function ( y, b ) { + var t, + x = this, + a = x.s; + + id = 12; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.minus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Return ±Infinity if either ±Infinity. + if ( !xc || !yc ) return new BigNumber( a / 0 ); + + // Either zero? + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. + if ( a = xe - ye ) { + if ( a > 0 ) { + ye = xe; + t = yc; + } else { + a = -a; + t = xc; + } + + t.reverse(); + for ( ; a--; t.push(0) ); + t.reverse(); + } + + a = xc.length; + b = yc.length; + + // Point xc to the longer array, and b to the shorter length. + if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; + + // Only start adding at yc.length - 1 as the further digits of xc can be ignored. + for ( a = 0; b; ) { + a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; + xc[b] = BASE === xc[b] ? 0 : xc[b] % BASE; + } + + if (a) { + xc = [a].concat(xc); + ++ye; + } + + // No need to check for zero, as +x + +y != 0 && -x + -y != 0 + // ye = MAX_EXP + 1 possible + return normalise( y, xc, ye ); + }; + + + /* + * Return the number of significant digits of the value of this BigNumber. + * + * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. + */ + P.precision = P.sd = function (z) { + var n, v, + x = this, + c = x.c; + + // 'precision() argument not a boolean or binary digit: {z}' + if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { + if (ERRORS) raise( 13, 'argument' + notBool, z ); + if ( z != !!z ) z = null; + } + + if ( !c ) return null; + v = c.length - 1; + n = v * LOG_BASE + 1; + + if ( v = c[v] ) { + + // Subtract the number of trailing zeros of the last element. + for ( ; v % 10 == 0; v /= 10, n-- ); + + // Add the number of digits of the first element. + for ( v = c[0]; v >= 10; v /= 10, n++ ); + } + + if ( z && x.e + 1 > n ) n = x.e + 1; + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if + * omitted. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'round() decimal places out of range: {dp}' + * 'round() decimal places not an integer: {dp}' + * 'round() rounding mode not an integer: {rm}' + * 'round() rounding mode out of range: {rm}' + */ + P.round = function ( dp, rm ) { + var n = new BigNumber(this); + + if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { + round( n, ~~dp + this.e + 1, rm == null || + !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); + } + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber shifted by k places + * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. + * + * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * + * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity + * otherwise. + * + * 'shift() argument not an integer: {k}' + * 'shift() argument out of range: {k}' + */ + P.shift = function (k) { + var n = this; + return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) + + // k < 1e+21, or truncate(k) will produce exponential notation. + ? n.times( '1e' + truncate(k) ) + : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) + ? n.s * ( k < 0 ? 0 : 1 / 0 ) + : n ); + }; + + + /* + * sqrt(-n) = N + * sqrt( N) = N + * sqrt(-I) = N + * sqrt( I) = I + * sqrt( 0) = 0 + * sqrt(-0) = -0 + * + * Return a new BigNumber whose value is the square root of the value of this BigNumber, + * rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.squareRoot = P.sqrt = function () { + var m, n, r, rep, t, + x = this, + c = x.c, + s = x.s, + e = x.e, + dp = DECIMAL_PLACES + 4, + half = new BigNumber('0.5'); + + // Negative/NaN/Infinity/zero? + if ( s !== 1 || !c || !c[0] ) { + return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); + } + + // Initial estimate. + s = Math.sqrt( +x ); + + // Math.sqrt underflow/overflow? + // Pass x to Math.sqrt as integer, then adjust the exponent of the result. + if ( s == 0 || s == 1 / 0 ) { + n = coeffToString(c); + if ( ( n.length + e ) % 2 == 0 ) n += '0'; + s = Math.sqrt(n); + e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); + + if ( s == 1 / 0 ) { + n = '1e' + e; + } else { + n = s.toExponential(); + n = n.slice( 0, n.indexOf('e') + 1 ) + e; + } + + r = new BigNumber(n); + } else { + r = new BigNumber( s + '' ); + } + + // Check for zero. + // r could be zero if MIN_EXP is changed after the this value was created. + // This would cause a division by zero (x/t) and hence Infinity below, which would cause + // coeffToString to throw. + if ( r.c[0] ) { + e = r.e; + s = e + dp; + if ( s < 3 ) s = 0; + + // Newton-Raphson iteration. + for ( ; ; ) { + t = r; + r = half.times( t.plus( div( x, t, dp, 1 ) ) ); + + if ( coeffToString( t.c ).slice( 0, s ) === ( n = + coeffToString( r.c ) ).slice( 0, s ) ) { + + // The exponent of r may here be one less than the final result exponent, + // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits + // are indexed correctly. + if ( r.e < e ) --s; + n = n.slice( s - 3, s + 1 ); + + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits + // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the + // iteration. + if ( n == '9999' || !rep && n == '4999' ) { + + // On the first iteration only, check to see if rounding up gives the + // exact result as the nines may infinitely repeat. + if ( !rep ) { + round( t, t.e + DECIMAL_PLACES + 2, 0 ); + + if ( t.times(t).eq(x) ) { + r = t; + break; + } + } + + dp += 4; + s += 4; + rep = 1; + } else { + + // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact + // result. If not, then there are further digits and m will be truthy. + if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { + + // Truncate to the first rounding digit. + round( r, r.e + DECIMAL_PLACES + 2, 1 ); + m = !r.times(r).eq(x); + } + + break; + } + } + } + } + + return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); + }; + + + /* + * n * 0 = 0 + * n * N = N + * n * I = I + * 0 * n = 0 + * 0 * 0 = 0 + * 0 * N = N + * 0 * I = N + * N * n = N + * N * 0 = N + * N * N = N + * N * I = N + * I * n = I + * I * 0 = N + * I * N = N + * I * I = I + * + * Return a new BigNumber whose value is the value of this BigNumber times the value of + * BigNumber(y, b). + */ + P.times = P.mul = function ( y, b ) { + var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, + base, sqrtBase, + x = this, + xc = x.c, + yc = ( id = 17, y = new BigNumber( y, b ) ).c; + + // Either NaN, ±Infinity or ±0? + if ( !xc || !yc || !xc[0] || !yc[0] ) { + + // Return NaN if either is NaN, or one is 0 and the other is Infinity. + if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { + y.c = y.e = y.s = null; + } else { + y.s *= x.s; + + // Return ±Infinity if either is ±Infinity. + if ( !xc || !yc ) { + y.c = y.e = null; + + // Return ±0 if either is ±0. + } else { + y.c = [0]; + y.e = 0; + } + } + + return y; + } + + e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); + y.s *= x.s; + xcL = xc.length; + ycL = yc.length; + + // Ensure xc points to longer array and xcL to its length. + if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; + + // Initialise the result array with zeros. + for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); + + base = BASE; + sqrtBase = SQRT_BASE; + + for ( i = ycL; --i >= 0; ) { + c = 0; + ylo = yc[i] % sqrtBase; + yhi = yc[i] / sqrtBase | 0; + + for ( k = xcL, j = i + k; j > i; ) { + xlo = xc[--k] % sqrtBase; + xhi = xc[k] / sqrtBase | 0; + m = yhi * xlo + xhi * ylo; + xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; + c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; + zc[j--] = xlo % base; + } + + zc[j] = c; + } + + if (c) { + ++e; + } else { + zc.splice(0, 1); + } + + return normalise( y, zc, e ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toDigits() precision out of range: {sd}' + * 'toDigits() precision not an integer: {sd}' + * 'toDigits() rounding mode not an integer: {rm}' + * 'toDigits() rounding mode out of range: {rm}' + */ + P.toDigits = function ( sd, rm ) { + var n = new BigNumber(this); + sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; + rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; + return sd ? round( n, sd, rm ) : n; + }; + + + /* + * Return a string representing the value of this BigNumber in exponential notation and + * rounded using ROUNDING_MODE to dp fixed decimal places. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toExponential() decimal places not an integer: {dp}' + * 'toExponential() decimal places out of range: {dp}' + * 'toExponential() rounding mode not an integer: {rm}' + * 'toExponential() rounding mode out of range: {rm}' + */ + P.toExponential = function ( dp, rm ) { + return format( this, + dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounding + * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', + * but e.g. (-0.00001).toFixed(0) is '-0'. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFixed() decimal places not an integer: {dp}' + * 'toFixed() decimal places out of range: {dp}' + * 'toFixed() rounding mode not an integer: {rm}' + * 'toFixed() rounding mode out of range: {rm}' + */ + P.toFixed = function ( dp, rm ) { + return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) + ? ~~dp + this.e + 1 : null, rm, 20 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounded + * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties + * of the FORMAT object (see BigNumber.config). + * + * FORMAT = { + * decimalSeparator : '.', + * groupSeparator : ',', + * groupSize : 3, + * secondaryGroupSize : 0, + * fractionGroupSeparator : '\xA0', // non-breaking space + * fractionGroupSize : 0 + * }; + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFormat() decimal places not an integer: {dp}' + * 'toFormat() decimal places out of range: {dp}' + * 'toFormat() rounding mode not an integer: {rm}' + * 'toFormat() rounding mode out of range: {rm}' + */ + P.toFormat = function ( dp, rm ) { + var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) + ? ~~dp + this.e + 1 : null, rm, 21 ); + + if ( this.c ) { + var i, + arr = str.split('.'), + g1 = +FORMAT.groupSize, + g2 = +FORMAT.secondaryGroupSize, + groupSeparator = FORMAT.groupSeparator, + intPart = arr[0], + fractionPart = arr[1], + isNeg = this.s < 0, + intDigits = isNeg ? intPart.slice(1) : intPart, + len = intDigits.length; + + if (g2) i = g1, g1 = g2, g2 = i, len -= i; + + if ( g1 > 0 && len > 0 ) { + i = len % g1 || g1; + intPart = intDigits.substr( 0, i ); + + for ( ; i < len; i += g1 ) { + intPart += groupSeparator + intDigits.substr( i, g1 ); + } + + if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); + if (isNeg) intPart = '-' + intPart; + } + + str = fractionPart + ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) + ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), + '$&' + FORMAT.fractionGroupSeparator ) + : fractionPart ) + : intPart; + } + + return str; + }; + + + /* + * Return a string array representing the value of this BigNumber as a simple fraction with + * an integer numerator and an integer denominator. The denominator will be a positive + * non-zero value less than or equal to the specified maximum denominator. If a maximum + * denominator is not specified, the denominator will be the lowest value necessary to + * represent the number exactly. + * + * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. + * + * 'toFraction() max denominator not an integer: {md}' + * 'toFraction() max denominator out of range: {md}' + */ + P.toFraction = function (md) { + var arr, d0, d2, e, exp, n, n0, q, s, + k = ERRORS, + x = this, + xc = x.c, + d = new BigNumber(ONE), + n1 = d0 = new BigNumber(ONE), + d1 = n0 = new BigNumber(ONE); + + if ( md != null ) { + ERRORS = false; + n = new BigNumber(md); + ERRORS = k; + + if ( !( k = n.isInt() ) || n.lt(ONE) ) { + + if (ERRORS) { + raise( 22, + 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); + } + + // ERRORS is false: + // If md is a finite non-integer >= 1, round it to an integer and use it. + md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; + } + } + + if ( !xc ) return x.toString(); + s = coeffToString(xc); + + // Determine initial denominator. + // d is a power of 10 and the minimum max denominator that specifies the value exactly. + e = d.e = s.length - x.e - 1; + d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; + md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; + + exp = MAX_EXP; + MAX_EXP = 1 / 0; + n = new BigNumber(s); + + // n0 = d1 = 0 + n0.c[0] = 0; + + for ( ; ; ) { + q = div( n, d, 0, 1 ); + d2 = d0.plus( q.times(d1) ); + if ( d2.cmp(md) == 1 ) break; + d0 = d1; + d1 = d2; + n1 = n0.plus( q.times( d2 = n1 ) ); + n0 = d2; + d = n.minus( q.times( d2 = d ) ); + n = d2; + } + + d2 = div( md.minus(d0), d1, 0, 1 ); + n0 = n0.plus( d2.times(n1) ); + d0 = d0.plus( d2.times(d1) ); + n0.s = n1.s = x.s; + e *= 2; + + // Determine which fraction is closer to x, n0/d0 or n1/d1 + arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( + div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 + ? [ n1.toString(), d1.toString() ] + : [ n0.toString(), d0.toString() ]; + + MAX_EXP = exp; + return arr; + }; + + + /* + * Return the value of this BigNumber converted to a number primitive. + */ + P.toNumber = function () { + return +this; + }; + + + /* + * Return a BigNumber whose value is the value of this BigNumber raised to the power n. + * If m is present, return the result modulo m. + * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. + * If POW_PRECISION is non-zero and m is not present, round to POW_PRECISION using + * ROUNDING_MODE. + * + * The modular power operation works efficiently when x, n, and m are positive integers, + * otherwise it is equivalent to calculating x.toPower(n).modulo(m) (with POW_PRECISION 0). + * + * n {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * [m] {number|string|BigNumber} The modulus. + * + * 'pow() exponent not an integer: {n}' + * 'pow() exponent out of range: {n}' + * + * Performs 54 loop iterations for n of 9007199254740991. + */ + P.toPower = P.pow = function ( n, m ) { + var k, y, z, + i = mathfloor( n < 0 ? -n : +n ), + x = this; + + if ( m != null ) { + id = 23; + m = new BigNumber(m); + } + + // Pass ±Infinity to Math.pow if exponent is out of range. + if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && + ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || + parseFloat(n) != n && !( n = NaN ) ) || n == 0 ) { + k = Math.pow( +x, n ); + return new BigNumber( m ? k % m : k ); + } + + if (m) { + if ( n > 1 && x.gt(ONE) && x.isInt() && m.gt(ONE) && m.isInt() ) { + x = x.mod(m); + } else { + z = m; + + // Nullify m so only a single mod operation is performed at the end. + m = null; + } + } else if (POW_PRECISION) { + + // Truncating each coefficient array to a length of k after each multiplication + // equates to truncating significant digits to POW_PRECISION + [28, 41], + // i.e. there will be a minimum of 28 guard digits retained. + // (Using + 1.5 would give [9, 21] guard digits.) + k = mathceil( POW_PRECISION / LOG_BASE + 2 ); + } + + y = new BigNumber(ONE); + + for ( ; ; ) { + if ( i % 2 ) { + y = y.times(x); + if ( !y.c ) break; + if (k) { + if ( y.c.length > k ) y.c.length = k; + } else if (m) { + y = y.mod(m); + } + } + + i = mathfloor( i / 2 ); + if ( !i ) break; + x = x.times(x); + if (k) { + if ( x.c && x.c.length > k ) x.c.length = k; + } else if (m) { + x = x.mod(m); + } + } + + if (m) return y; + if ( n < 0 ) y = ONE.div(y); + + return z ? y.mod(z) : k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; + }; + + + /* + * Return a string representing the value of this BigNumber rounded to sd significant digits + * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits + * necessary to represent the integer part of the value in fixed-point notation, then use + * exponential notation. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toPrecision() precision not an integer: {sd}' + * 'toPrecision() precision out of range: {sd}' + * 'toPrecision() rounding mode not an integer: {rm}' + * 'toPrecision() rounding mode out of range: {rm}' + */ + P.toPrecision = function ( sd, rm ) { + return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) + ? sd | 0 : null, rm, 24 ); + }; + + + /* + * Return a string representing the value of this BigNumber in base b, or base 10 if b is + * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and + * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent + * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than + * TO_EXP_NEG, return exponential notation. + * + * [b] {number} Integer, 2 to 64 inclusive. + * + * 'toString() base not an integer: {b}' + * 'toString() base out of range: {b}' + */ + P.toString = function (b) { + var str, + n = this, + s = n.s, + e = n.e; + + // Infinity or NaN? + if ( e === null ) { + + if (s) { + str = 'Infinity'; + if ( s < 0 ) str = '-' + str; + } else { + str = 'NaN'; + } + } else { + str = coeffToString( n.c ); + + if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential( str, e ) + : toFixedPoint( str, e ); + } else { + str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); + } + + if ( s < 0 && n.c[0] ) str = '-' + str; + } + + return str; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole + * number. + */ + P.truncated = P.trunc = function () { + return round( new BigNumber(this), this.e + 1, 1 ); + }; + + + /* + * Return as toString, but do not accept a base argument, and include the minus sign for + * negative zero. + */ + P.valueOf = P.toJSON = function () { + var str, + n = this, + e = n.e; + + if ( e === null ) return n.toString(); + + str = coeffToString( n.c ); + + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential( str, e ) + : toFixedPoint( str, e ); + + return n.s < 0 ? '-' + str : str; + }; + + + P.isBigNumber = true; + + if ( config != null ) BigNumber.config(config); + + return BigNumber; + } + + + // PRIVATE HELPER FUNCTIONS + + + function bitFloor(n) { + var i = n | 0; + return n > 0 || n === i ? i : i - 1; + } + + + // Return a coefficient array as a string of base 10 digits. + function coeffToString(a) { + var s, z, + i = 1, + j = a.length, + r = a[0] + ''; + + for ( ; i < j; ) { + s = a[i++] + ''; + z = LOG_BASE - s.length; + for ( ; z--; s = '0' + s ); + r += s; + } + + // Determine trailing zeros. + for ( j = r.length; r.charCodeAt(--j) === 48; ); + return r.slice( 0, j + 1 || 1 ); + } + + + // Compare the value of BigNumbers x and y. + function compare( x, y ) { + var a, b, + xc = x.c, + yc = y.c, + i = x.s, + j = y.s, + k = x.e, + l = y.e; + + // Either NaN? + if ( !i || !j ) return null; + + a = xc && !xc[0]; + b = yc && !yc[0]; + + // Either zero? + if ( a || b ) return a ? b ? 0 : -j : i; + + // Signs differ? + if ( i != j ) return i; + + a = i < 0; + b = k == l; + + // Either Infinity? + if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; + + // Compare exponents. + if ( !b ) return k > l ^ a ? 1 : -1; + + j = ( k = xc.length ) < ( l = yc.length ) ? k : l; + + // Compare digit by digit. + for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; + + // Compare lengths. + return k == l ? 0 : k > l ^ a ? 1 : -1; + } + + + /* + * Return true if n is a valid number in range, otherwise false. + * Use for argument validation when ERRORS is false. + * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. + */ + function intValidatorNoErrors( n, min, max ) { + return ( n = truncate(n) ) >= min && n <= max; + } + + + function isArray(obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; + } + + + /* + * Convert string of baseIn to an array of numbers of baseOut. + * Eg. convertBase('255', 10, 16) returns [15, 15]. + * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. + */ + function toBaseOut( str, baseIn, baseOut ) { + var j, + arr = [0], + arrL, + i = 0, + len = str.length; + + for ( ; i < len; ) { + for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); + arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); + + for ( ; j < arr.length; j++ ) { + + if ( arr[j] > baseOut - 1 ) { + if ( arr[j + 1] == null ) arr[j + 1] = 0; + arr[j + 1] += arr[j] / baseOut | 0; + arr[j] %= baseOut; + } + } + } + + return arr.reverse(); + } + + + function toExponential( str, e ) { + return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + + ( e < 0 ? 'e' : 'e+' ) + e; + } + + + function toFixedPoint( str, e ) { + var len, z; + + // Negative exponent? + if ( e < 0 ) { + + // Prepend zeros. + for ( z = '0.'; ++e; z += '0' ); + str = z + str; + + // Positive exponent + } else { + len = str.length; + + // Append zeros. + if ( ++e > len ) { + for ( z = '0', e -= len; --e; z += '0' ); + str += z; + } else if ( e < len ) { + str = str.slice( 0, e ) + '.' + str.slice(e); + } + } + + return str; + } + + + function truncate(n) { + n = parseFloat(n); + return n < 0 ? mathceil(n) : mathfloor(n); + } + + + // EXPORT + + + BigNumber = constructorFactory(); + BigNumber['default'] = BigNumber.BigNumber = BigNumber; + + + // AMD. + if ( typeof define == 'function' && define.amd ) { + define( function () { return BigNumber; } ); + + // Node.js and other environments that support module.exports. + } else if ( typeof module != 'undefined' && module.exports ) { + module.exports = BigNumber; + + // Browser. + } else { + if ( !globalObj ) globalObj = typeof self != 'undefined' ? self : Function('return this')(); + globalObj.BigNumber = BigNumber; + } +})(this); + +},{}],"web3":[function(require,module,exports){ var Web3 = require('./lib/web3'); // dont override global variable diff --git a/lib/cmd.js b/lib/cmd.js index a3440b974..9a065c6db 100644 --- a/lib/cmd.js +++ b/lib/cmd.js @@ -1,150 +1,170 @@ -var program = require('commander'); -var colors = require('colors'); -var shelljs = require('shelljs'); +let program = require('commander'); +let colors = require('colors'); +let shelljs = require('shelljs'); +let promptly = require('promptly'); +let path = require('path'); +const Embark = require('../lib/index'); +let embark = new Embark; -var Cmd = function(Embark) { - this.Embark = Embark; - program.version(Embark.version); -}; - -Cmd.prototype.process = function(args) { - this.newApp(); - this.demo(); - this.build(); - this.run(); - this.blockchain(); - this.simulator(); - this.test(); - this.upload(); - this.otherCommands(); - - //If no arguments are passed display help by default - if (!process.argv.slice(2).length) { - program.help(); +class Cmd { + constructor() { + program.version(embark.version); } - - program.parse(args); -}; -Cmd.prototype.newApp = function() { - var self = this; - program - .command('new [name]') - .description('new application') - .action(function(name, options) { - if (name === undefined) { - console.log("please specify your app Name".red); - console.log("e.g embark new MyApp".green); - console.log("e.g embark new --help for more information".green); - process.exit(code); + process(args) { + this.newApp(); + this.demo(); + this.build(); + this.run(); + this.blockchain(); + this.simulator(); + this.test(); + this.upload(); + this.otherCommands(); + + //If no arguments are passed display help by default + if (!process.argv.slice(2).length) { + program.help(); } - self.Embark.generateTemplate('boilerplate', './', name); - }); -}; -Cmd.prototype.demo = function() { - var self = this; - program - .command('demo') - .description('create a working dapp with a SimpleStorage contract') - .action(function() { - self.Embark.generateTemplate('demo', './', 'embark_demo'); - }); -}; + program.parse(args); + } -Cmd.prototype.build = function() { - var self = this; - program - .command('build [environment]') - .description('deploy and build dapp at dist/ (default: development)') - .action(function(env, options) { - self.Embark.build({env: env || 'development'}); - }); -}; + newApp(name) { -Cmd.prototype.run = function() { - var self = this; - program - .command('run [environment]') - .option('-p, --port [port]', 'port to run the dev webserver (default: 8000)') - .option('-b, --host [host]', 'host to run the dev webserver (default: localhost)') - .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') - .description('run dapp (default: development)') - .action(function(env, options) { - self.Embark.run({ - env: env || 'development', - serverPort: options.port, - serverHost: options.host, - runWebserver: !options.noserver, - useDashboard: !options.nodashboard - }); - }); -}; + let validateName = function (value) { + try { + if (value.match(/^[a-zA-Z\s\-]+$/)) return value; + } catch (e) { + throw new Error('Name must be only letters, spaces, or dashes'); + } + }; -Cmd.prototype.blockchain = function() { - var self = this; - program - .command('blockchain [environment]') - .option('-c, --client [client]', 'Use a specific ethereum client or simulator (supported: geth, parity, ethersim, testrpc') - .description('run blockchain server (default: development)') - .action(function(env ,options) { - self.Embark.initConfig(env || 'development', { - embarkConfig: 'embark.json', - interceptLogs: false - }); - self.Embark.blockchain(env || 'development', options.client || 'geth'); - }); -}; + program + .command('new [name]') + .description('new application') + .action(function (name) { + if (name === undefined) { + return promptly.prompt("Name your app (default is embarkDApp):", { + default: "embarkDApp", + validator: validateName + }, function (err, inputvalue) { + if (err) { + console.error('Invalid name:', err.message); + // Manually call retry + // The passed error has a retry method to easily prompt again. + err.retry(); + } else { + //slightly different assignment of name since it comes from child prompt + embark.generateTemplate('boilerplate', './', inputvalue); + } + }); + } else { + embark.generateTemplate('boilerplate', './', name); + } -Cmd.prototype.simulator = function() { - var self = this; - program - .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('-h, --host [host]', 'host to run the rpc simulator (default: localhost)') - .action(function(env, options) { - self.Embark.initConfig(env || 'development', { - embarkConfig: 'embark.json', - interceptLogs: false - }); - self.Embark.simulator({port: options.port, host: options.host}); - }); -}; + }); + } -Cmd.prototype.test = function() { - program - .command('test') - .description('run tests') - .action(function() { - shelljs.exec('mocha test/ --no-timeouts'); - }); -}; + demo() { + program + .command('demo') + .description('create a working dapp with a SimpleStorage contract') + .action(function () { + embark.generateTemplate('demo', './', 'embark_demo'); + }); + } -Cmd.prototype.upload = function() { - var self = this; - program - .command('upload [platform] [environment]') - .description('upload your dapp to a decentralized storage. possible options: ipfs, swarm (e.g embark upload swarm)') - .action(function(platform, env, options) { - // TODO: get env in cmd line as well - self.Embark.initConfig(env || 'development', { - embarkConfig: 'embark.json', interceptLogs: false - }); - self.Embark.upload(platform); - }); -}; + build() { + program + .command('build [environment]') + .description('deploy and build dapp at dist/ (default: development)') + .action(function (env, options) { + embark.build({env: env || 'development'}); + }); + } -Cmd.prototype.otherCommands = function() { - program - .action(function(env){ - console.log('unknown command "%s"'.red, env); - console.log("type embark --help to see the available commands"); - process.exit(0); - }); -}; + run() { + program + .command('run [environment]') + .option('-p, --port [port]', 'port to run the dev webserver (default: 8000)') + .option('-b, --host [host]', 'host to run the dev webserver (default: localhost)') + .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') + .description('run dapp (default: development)') + .action(function (env, options) { + embark.run({ + env: env || 'development', + serverPort: options.port, + serverHost: options.host, + runWebserver: !options.noserver, + useDashboard: !options.nodashboard + }); + }); + } + + blockchain() { + program + .command('blockchain [environment]') + .option('-c, --client [client]', 'Use a specific ethereum client or simulator (supported: geth, parity, ethersim, testrpc') + .description('run blockchain server (default: development)') + .action(function (env, options) { + embark.initConfig(env || 'development', { + embarkConfig: 'embark.json', + interceptLogs: false + }); + embark.blockchain(env || 'development', options.client || 'geth'); + }); + } + + simulator() { + program + .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('-h, --host [host]', 'host to run the rpc simulator (default: localhost)') + .action(function (env, options) { + embark.initConfig(env || 'development', { + embarkConfig: 'embark.json', + interceptLogs: false + }); + embark.simulator({port: options.port, host: options.host}); + }); + } + + test() { + program + .command('test') + .description('run tests') + .action(function () { + shelljs.exec('mocha test'); + }); + } + + upload() { + program + .command('upload [platform] [environment]') + .description('upload your dapp to a decentralized storage. possible options: ipfs, swarm (e.g embark upload swarm)') + .action(function (platform, env, options) { + // TODO: get env in cmd line as well + embark.initConfig(env || 'development', { + embarkConfig: 'embark.json', interceptLogs: false + }); + embark.upload(platform); + }); + } + + otherCommands() { + program + .action(function (env) { + console.log('unknown command "%s"'.red, env); + console.log("type embark --help to see the available commands"); + process.exit(0); + }); + } + +} module.exports = Cmd; diff --git a/lib/cmds/blockchain/blockchain.js b/lib/cmds/blockchain/blockchain.js index e83167bc0..45daa5757 100644 --- a/lib/cmds/blockchain/blockchain.js +++ b/lib/cmds/blockchain/blockchain.js @@ -35,9 +35,9 @@ var Blockchain = function(options) { this.client = new options.client({config: this.config, env: this.env}); }; -Blockchain.prototype.runCommand = function(cmd) { +Blockchain.prototype.runCommand = function(cmd, options) { console.log(("running: " + cmd.underline).green); - return shelljs.exec(cmd); + return shelljs.exec(cmd, options); }; Blockchain.prototype.run = function() { @@ -49,7 +49,7 @@ Blockchain.prototype.run = function() { console.log("===============================================================================".magenta); var address = this.initChainAndGetAddress(); this.client.mainCommand(address, function(cmd) { - self.runCommand(cmd); + self.runCommand(cmd, {async: true}); }); }; @@ -65,7 +65,7 @@ Blockchain.prototype.initChainAndGetAddress = function() { // check if an account already exists, create one if not, return address result = this.runCommand(this.client.listAccountsCommand()); - if (result.output === undefined || result.output === '' || result.output.indexOf("Fatal") >= 0) { + if (result.output === undefined || result.output.match(/{(\w+)}/) === null || result.output.indexOf("Fatal") >= 0) { console.log("no accounts found".green); if (this.config.genesisBlock) { console.log("initializing genesis block".green); diff --git a/lib/cmds/blockchain/geth_commands.js b/lib/cmds/blockchain/geth_commands.js index f89c325a6..8712a30f9 100644 --- a/lib/cmds/blockchain/geth_commands.js +++ b/lib/cmds/blockchain/geth_commands.js @@ -1,164 +1,171 @@ -var async = require('async'); +let async = require('async'); // TODO: make all of this async -var GethCommands = function(options) { - this.config = options.config; - this.env = options.env || 'development'; - this.name = "Go-Ethereum (https://github.com/ethereum/go-ethereum)"; - this.geth_bin = this.config.geth_bin || "geth"; -}; - -GethCommands.prototype.commonOptions = function() { - var config = this.config; - var cmd = ""; - - cmd += this.determineNetworkType(config); - - if (config.datadir) { - cmd += "--datadir=\"" + config.datadir + "\" "; +class GethCommands { + constructor(options) { + this.config = options && options.hasOwnProperty('config') ? options.config : {}; + this.env = options && options.hasOwnProperty('env') ? options.env : 'development'; + this.name = "Go-Ethereum (https://github.com/ethereum/go-ethereum)"; + this.geth_bin = this.config.geth_bin || "geth"; } - if (config.light) { - cmd += "--light "; + commonOptions() { + let config = this.config; + let cmd = ""; + + cmd += this.determineNetworkType(config); + + if (config.datadir) { + cmd += "--datadir=\"" + config.datadir + "\" "; + } + + if (config.light) { + cmd += "--light "; + } + + if (config.fast) { + cmd += "--fast "; + } + + if (config.account && config.account.password) { + cmd += "--password " + config.account.password + " "; + } + + return cmd; } - if (config.fast) { - cmd += "--fast "; + determineNetworkType(config) { + let cmd = ""; + if (config.networkType === 'testnet') { + cmd += "--testnet "; + } else if (config.networkType === 'olympic') { + cmd += "--olympic "; + } else if (config.networkType === 'custom') { + cmd += "--networkid " + config.networkId + " "; + } + return cmd; } - if (config.account && config.account.password) { - cmd += "--password " + config.account.password + " "; + initGenesisCommmand() { + let config = this.config; + let cmd = this.geth_bin + " " + this.commonOptions(); + + if (config.genesisBlock) { + cmd += "init \"" + config.genesisBlock + "\" "; + } + + return cmd; } - return cmd; -}; - -GethCommands.prototype.determineNetworkType = function(config) { - var cmd = ""; - if (config.networkType === 'testnet') { - cmd += "--testnet "; - } else if (config.networkType === 'olympic') { - cmd += "--olympic "; - } else if (config.networkType === 'custom') { - cmd += "--networkid " + config.networkId + " "; - } - return cmd; -}; - -GethCommands.prototype.initGenesisCommmand = function() { - var config = this.config; - var cmd = this.geth_bin + " " + this.commonOptions(); - - if (config.genesisBlock) { - cmd += "init \"" + config.genesisBlock + "\" "; + newAccountCommand() { + return this.geth_bin + " " + this.commonOptions() + "account new "; } - return cmd; -}; + listAccountsCommand() { + return this.geth_bin + " " + this.commonOptions() + "account list "; + } -GethCommands.prototype.newAccountCommand = function() { - return this.geth_bin + " " + this.commonOptions() + "account new "; -}; + determineRpcOptions(config) { + let cmd = ""; -GethCommands.prototype.listAccountsCommand = function() { - return this.geth_bin + " " + this.commonOptions() + "account list "; -}; - -GethCommands.prototype.determineRpcOptions = function(config) { - var cmd = ""; - - cmd += "--port " + config.port + " "; - cmd += "--rpc "; - cmd += "--rpcport " + config.rpcPort + " "; - cmd += "--rpcaddr " + config.rpcHost + " "; - if (config.rpcCorsDomain) { - if (config.rpcCorsDomain === '*') { + cmd += "--port " + config.port + " "; + cmd += "--rpc "; + cmd += "--rpcport " + config.rpcPort + " "; + cmd += "--rpcaddr " + config.rpcHost + " "; + if (config.rpcCorsDomain) { + if (config.rpcCorsDomain === '*') { + console.log('=================================='); + console.log('make sure you know what you are doing'); + console.log('=================================='); + } + cmd += "--rpccorsdomain=\"" + config.rpcCorsDomain + "\" "; + } else { console.log('=================================='); - console.log('make sure you know what you are doing'); + console.log('warning: cors is not set'); console.log('=================================='); } - cmd += "--rpccorsdomain=\"" + config.rpcCorsDomain + "\" "; - } else { - console.log('=================================='); - console.log('warning: cors is not set'); - console.log('=================================='); + + return cmd; } - return cmd; -}; + mainCommand(address, done) { + let self = this; + let config = this.config; + let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net']); -GethCommands.prototype.mainCommand = function(address, done) { - var self = this; - var config = this.config; - var rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net']); - - async.series([ - function commonOptions(callback) { - var cmd = self.commonOptions(); - callback(null, cmd); - }, - function rpcOptions(callback) { - var cmd = self.determineRpcOptions(self.config); - callback(null, cmd); - }, - function dontGetPeers(callback) { - if (config.nodiscover) { - return callback(null, "--nodiscover"); + async.series([ + function commonOptions(callback) { + let cmd = self.commonOptions(); + callback(null, cmd); + }, + function rpcOptions(callback) { + let cmd = self.determineRpcOptions(self.config); + callback(null, cmd); + }, + function dontGetPeers(callback) { + if (config.nodiscover) { + return callback(null, "--nodiscover"); + } + callback(null, ""); + }, + function vmDebug(callback) { + if (config.vmdebug) { + return callback(null, "--vmdebug"); + } + callback(null, ""); + }, + function maxPeers(callback) { + let cmd = "--maxpeers " + config.maxpeers; + callback(null, cmd); + }, + function mining(callback) { + if (config.mineWhenNeeded || config.mine) { + return callback(null, "--mine "); + } + callback(""); + }, + function bootnodes(callback) { + if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) { + return callback(null, "--bootnodes " + config.bootnodes); + } + callback(""); + }, + function whisper(callback) { + if (config.whisper) { + rpc_api.push('shh'); + return callback(null, "--shh "); + } + callback(""); + }, + function rpcApi(callback) { + callback(null, '--rpcapi "' + rpc_api.join(',') + '"'); + }, + function accountToUnlock(callback) { + let accountAddress = ""; + if(config.hasOwnProperty('address') && config.account.hasOwnProperty('address')) { + accountAddress = config.account.address; + } else { + accountAddress = address; + } + if (accountAddress) { + return callback(null, "--unlock=" + accountAddress); + } + callback(null, ""); + }, + function mineWhenNeeded(callback) { + if (config.mineWhenNeeded) { + return callback(null, "js .embark/" + self.env + "/js/mine.js"); + } + callback(null, ""); } - callback(null, ""); - }, - function vmDebug(callback) { - if (config.vmdebug) { - return callback(null, "--vmdebug"); + ], function (err, results) { + if (err) { + throw new Error(err.message); } - callback(null, ""); - }, - function maxPeers(callback) { - var cmd = "--maxpeers " + config.maxpeers; - callback(null, cmd); - }, - function mining(callback) { - if (config.mineWhenNeeded || config.mine) { - return callback(null, "--mine "); - } - callback(""); - }, - function bootnodes(callback) { - if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) { - return callback(null, "--bootnodes " + config.bootnodes); - } - callback(""); - }, - function whisper(callback) { - if (config.whisper) { - rpc_api.push('shh'); - return callback(null, "--shh "); - } - callback(""); - }, - function rpcApi(callback) { - callback(null, '--rpcapi "' + rpc_api.join(',') + '"'); - }, - function accountToUnlock(callback) { - var accountAddress = config.account.address || address; - if (accountAddress) { - return callback(null, "--unlock=" + accountAddress); - } - callback(null, ""); - }, - function mineWhenNeeded(callback) { - if (config.mineWhenNeeded) { - return callback(null, "js .embark/" + self.env + "/js/mine.js"); - } - callback(null, ""); - } - ], function(err, results) { - if (err) { - throw new Error(err.message); - } - done(self.geth_bin + " " + results.join(" ")); - }); -}; + done(self.geth_bin + " " + results.join(" ")); + }); + } +} module.exports = GethCommands; diff --git a/lib/cmds/simulator.js b/lib/cmds/simulator.js index 47554c123..8d90e2d91 100644 --- a/lib/cmds/simulator.js +++ b/lib/cmds/simulator.js @@ -1,18 +1,20 @@ -var shelljs = require('shelljs'); +let shelljs = require('shelljs'); -var Simulator = function(options) { - this.blockchainConfig = options.blockchainConfig; -}; +class Simulator { + constructor(options) { + this.blockchainConfig = options.blockchainConfig; + } -Simulator.prototype.run = function(options) { - var cmds = []; + run(options) { + let cmds = []; - 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("-p " + (this.blockchainConfig.rpcPort || options.port || 8545)); + cmds.push("-h " + (this.blockchainConfig.rpcHost || options.host || 'localhost')); + cmds.push("-a " + (options.num || 10)); - shelljs.exec('testrpc ' + cmds.join(' ')); -}; + shelljs.exec('testrpc ' + cmds.join(' '), {async : true}); + } +} module.exports = Simulator; diff --git a/lib/cmds/template_generator.js b/lib/cmds/template_generator.js index a0aa482f9..2b0fdea2a 100644 --- a/lib/cmds/template_generator.js +++ b/lib/cmds/template_generator.js @@ -1,32 +1,35 @@ -var fs = require('../core/fs.js'); -var utils = require('../core/utils.js'); +let fs = require('../core/fs.js'); +let utils = require('../utils/utils.js'); -var TemplateGenerator = function(templateName) { - this.templateName = templateName; -}; - -TemplateGenerator.prototype.generate = function(destinationFolder, name) { - var templatePath = fs.embarkPath(this.templateName); - console.log('Initializing Embark Template....'.green); - - fs.copySync(templatePath, destinationFolder + name); - utils.cd(destinationFolder + name); - utils.sed('package.json', '%APP_NAME%', name); - - console.log('Installing packages.. this can take a few seconds'.green); - utils.runCmd('npm install'); - console.log('Init complete'.green); - console.log('\nApp ready at '.green + destinationFolder + name); - - if (name === 'embark_demo') { - console.log('-------------------'.yellow); - console.log('Next steps:'.green); - console.log(('-> ' + ('cd ' + destinationFolder + name).bold.cyan).green); - 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); +class TemplateGenerator { + constructor(templateName) { + this.templateName = templateName; } -}; + + generate(destinationFolder, name) { + let templatePath = fs.embarkPath(this.templateName); + console.log('Initializing Embark Template....'.green); + let fspath = utils.joinPath(destinationFolder, name); + + fs.copySync(templatePath, fspath); + utils.cd(fspath); + utils.sed('package.json', '%APP_NAME%', name); + + console.log('Installing packages.. this can take a few seconds'.green); + utils.runCmd('npm install'); + console.log('Init complete'.green); + console.log('\nApp ready at '.green + fspath); + + if (name === 'embark_demo') { + console.log('-------------------'.yellow); + console.log('Next steps:'.green); + console.log(('-> ' + ('cd ' + fspath).bold.cyan).green); + 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); + } + } +} module.exports = TemplateGenerator; diff --git a/lib/contracts/abi.js b/lib/contracts/abi.js index 95c444855..3f5eb36e6 100644 --- a/lib/contracts/abi.js +++ b/lib/contracts/abi.js @@ -1,138 +1,170 @@ - -var ABIGenerator = function(options) { - this.blockchainConfig = options.blockchainConfig || {}; - this.storageConfig = options.storageConfig || {}; - this.communicationConfig = options.communicationConfig || {}; - this.contractsManager = options.contractsManager; - this.rpcHost = options.blockchainConfig && options.blockchainConfig.rpcHost; - this.rpcPort = options.blockchainConfig && options.blockchainConfig.rpcPort; - this.plugins = options.plugins; -}; - -ABIGenerator.prototype.generateProvider = function() { - var self = this; - var result = ""; - var providerPlugins; - - if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { - return ""; +class ABIGenerator { + constructor(options) { + this.blockchainConfig = options.blockchainConfig || {}; + this.storageConfig = options.storageConfig || {}; + this.communicationConfig = options.communicationConfig || {}; + this.contractsManager = options.contractsManager; + this.rpcHost = options.blockchainConfig && options.blockchainConfig.rpcHost; + this.rpcPort = options.blockchainConfig && options.blockchainConfig.rpcPort; + this.plugins = options.plugins; } - result += "\nvar whenEnvIsLoaded = function(cb) {"; - result += "\n if (typeof window !== 'undefined' && window !== null) {"; - result += "\n window.addEventListener('load', cb);"; - result += "\n } else {"; - result += "\n cb();"; - result += "\n }"; - result += "\n}"; + generateProvider() { + let self = this; + let result = ""; + let providerPlugins; - if (this.plugins) { - providerPlugins = this.plugins.getPluginsFor('clientWeb3Provider'); - } - - if (this.plugins && providerPlugins.length > 0) { - providerPlugins.forEach(function(plugin) { - result += plugin.generateProvider(self) + "\n"; - }); - } else { - result += "\nwhenEnvIsLoaded(function() {"; - result += "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {"; - result += '\n\tweb3 = new Web3(web3.currentProvider);'; - result += "\n} else if (typeof Web3 !== 'undefined') {"; - result += '\n\tweb3 = new Web3(new Web3.providers.HttpProvider("http://' + this.rpcHost + ':' + this.rpcPort + '"));'; - result += '\n}'; - result += "\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; - result += '\n})'; - } - - return result; -}; - -ABIGenerator.prototype.generateContracts = function(useEmbarkJS) { - var self = this; - var result = "\n"; - var contractsPlugins; - - if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { - return ""; - } - - if (this.plugins) { - contractsPlugins = this.plugins.getPluginsFor('contractGeneration'); - } - - if (this.plugins && contractsPlugins.length > 0) { - contractsPlugins.forEach(function(plugin) { - result += plugin.generateContracts({contracts: self.contractsManager.contracts}); - }); - } else { - for(var className in this.contractsManager.contracts) { - var contract = this.contractsManager.contracts[className]; - - var abi = JSON.stringify(contract.abiDefinition); - var gasEstimates = JSON.stringify(contract.gasEstimates); - - // TODO: refactor this - result += "\nvar whenEnvIsLoaded = function(cb) {"; - result += "\n if (typeof window !== 'undefined' && window !== null) {"; - result += "\n window.addEventListener('load', cb);"; - result += "\n } else {"; - result += "\n cb();"; - result += "\n }"; - result += "\n}"; - - result += "\nwhenEnvIsLoaded(function() {"; - if (useEmbarkJS) { - result += "\n" + className + " = new EmbarkJS.Contract({abi: " + abi + ", address: '" + contract.deployedAddress + "', code: '" + contract.code + "', gasEstimates: " + gasEstimates + "});"; - } else { - result += "\n" + className + "Abi = " + abi + ";"; - result += "\n" + className + "Contract = web3.eth.contract(" + className + "Abi);"; - result += "\n" + className + " = " + className + "Contract.at('" + contract.deployedAddress + "');"; - } - result += '\n});'; + if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { + return ""; } + + result += "\nvar whenEnvIsLoaded = function(cb) {"; + result += "\n if (typeof document !== 'undefined' && document !== null) {"; + result += "\n document.addEventListener('DOMContentLoaded', cb);"; + result += "\n } else {"; + result += "\n cb();"; + result += "\n }"; + result += "\n}"; + + if (this.plugins) { + providerPlugins = this.plugins.getPluginsFor('clientWeb3Provider'); + } + + if (this.plugins && providerPlugins.length > 0) { + providerPlugins.forEach(function(plugin) { + result += plugin.generateProvider(self) + "\n"; + }); + } else { + result += "\nwhenEnvIsLoaded(function() {"; + result += "\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(web3.currentProvider);'; + result += "\n} else if (typeof Web3 !== 'undefined') {"; + result += '\n\tweb3 = new Web3(new Web3.providers.HttpProvider("http://' + this.rpcHost + ':' + this.rpcPort + '"));'; + result += '\n}'; + result += "\nweb3.eth.defaultAccount = web3.eth.accounts[0];"; + result += '\n})'; + } + + return result; } - return result; -}; + generateContracts(useEmbarkJS) { + let self = this; + let result = "\n"; + let contractsPlugins; -ABIGenerator.prototype.generateStorageInitialization = function(useEmbarkJS) { - var self = this; - var result = "\n"; + if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { + return ""; + } - if (!useEmbarkJS || self.storageConfig === {}) return ""; + if (this.plugins) { + contractsPlugins = this.plugins.getPluginsFor('contractGeneration'); + } - if (self.storageConfig.provider === 'ipfs' && self.storageConfig.enabled === true) { - result += "\nEmbarkJS.Storage.setProvider('" + self.storageConfig.provider + "', {server: '" + self.storageConfig.host + "', port: '" + self.storageConfig.port + "'});"; + if (this.plugins && contractsPlugins.length > 0) { + contractsPlugins.forEach(function (plugin) { + result += plugin.generateContracts({contracts: self.contractsManager.contracts}); + }); + } else { + for (let className in this.contractsManager.contracts) { + let contract = this.contractsManager.contracts[className]; + + let abi = JSON.stringify(contract.abiDefinition); + let gasEstimates = JSON.stringify(contract.gasEstimates); + + // TODO: refactor this + result += "\nif (whenEnvIsLoaded === undefined) {"; + result += "\n var whenEnvIsLoaded = function(cb) {"; + result += "\n if (typeof document !== 'undefined' && document !== null) {"; + result += "\n document.addEventListener('DOMContentLoaded', cb);"; + result += "\n } else {"; + result += "\n cb();"; + result += "\n }"; + result += "\n }"; + result += "\n}"; + + result += "\nwhenEnvIsLoaded(function() {"; + if (useEmbarkJS) { + result += "\n" + className + " = new EmbarkJS.Contract({abi: " + abi + ", address: '" + contract.deployedAddress + "', code: '" + contract.code + "', gasEstimates: " + gasEstimates + "});"; + } else { + result += "\n" + className + "Abi = " + abi + ";"; + result += "\n" + className + "Contract = web3.eth.contract(" + className + "Abi);"; + result += "\n" + className + " = " + className + "Contract.at('" + contract.deployedAddress + "');"; + } + result += '\n});'; + } + } + + return result; } - return result; -}; + generateStorageInitialization(useEmbarkJS) { + let self = this; + let result = "\n"; -ABIGenerator.prototype.generateCommunicationInitialization = function(useEmbarkJS) { - var self = this; - var result = "\n"; + if (!useEmbarkJS || self.storageConfig === {}) return ""; - if (!useEmbarkJS || self.communicationConfig === {}) return ""; + if (self.storageConfig.provider === 'ipfs' && self.storageConfig.enabled === true) { + result += "\nEmbarkJS.Storage.setProvider('" + self.storageConfig.provider + "', {server: '" + self.storageConfig.host + "', port: '" + self.storageConfig.port + "'});"; + } - if (self.communicationConfig.provider === 'whisper' && self.communicationConfig.enabled === true) { - result += "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "');"; - } else if (self.communicationConfig.provider === 'orbit' && self.communicationConfig.enabled === true) { - result += "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "', {server: '" + self.communicationConfig.host + "', port: '" + self.communicationConfig.port + "'});"; + return result; } - return result; -}; + generateCommunicationInitialization(useEmbarkJS) { + let self = this; + let result = "\n"; -ABIGenerator.prototype.generateABI = function(options) { - var result = ""; + if (!useEmbarkJS || self.communicationConfig === {}) return ""; - result += this.generateProvider(); - result += this.generateContracts(options.useEmbarkJS); - result += this.generateStorageInitialization(options.useEmbarkJS); - result += this.generateCommunicationInitialization(options.useEmbarkJS); + if (self.communicationConfig.provider === 'whisper' && self.communicationConfig.enabled === true) { + result += "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "');"; + } else if (self.communicationConfig.provider === 'orbit' && self.communicationConfig.enabled === true) { + if (self.communicationConfig.host === undefined && self.communicationConfig.port === undefined) { + result += "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "');"; + } else { + result += "\nEmbarkJS.Messages.setProvider('" + self.communicationConfig.provider + "', {server: '" + self.communicationConfig.host + "', port: '" + self.communicationConfig.port + "'});"; + } + } - return result; -}; + return result; + } + + generateABI(options) { + let result = ""; + + result += this.generateProvider(); + result += this.generateContracts(options.useEmbarkJS); + result += this.generateStorageInitialization(options.useEmbarkJS); + result += this.generateCommunicationInitialization(options.useEmbarkJS); + + return result; + } + + generateContractsJSON() { + let contracts = {}; + + for (let className in this.contractsManager.contracts) { + let contract = this.contractsManager.contracts[className]; + let contractJSON = {}; + + let abi = JSON.stringify(contract.abiDefinition); + let gasEstimates = JSON.stringify(contract.gasEstimates); + + contractJSON.contract_name = className; + contractJSON.code = contract.code; + contractJSON.runtime_bytecode = contract.runtimeBytecode; + contractJSON.real_runtime_bytecode = contract.realRuntimeBytecode; + contractJSON.swarm_hash = contract.swarmHash; + contractJSON.gas_estimates = contract.gasEstimates; + contractJSON.function_hashes = contract.functionHashes; + contractJSON.abi = contract.abiDefinition; + + contracts[className] = contractJSON; + } + + return contracts; + } +} module.exports = ABIGenerator; diff --git a/lib/contracts/compiler.js b/lib/contracts/compiler.js index f7a7c70ef..4644d4bef 100644 --- a/lib/contracts/compiler.js +++ b/lib/contracts/compiler.js @@ -1,118 +1,115 @@ /*jshint esversion: 6, loopfunc: true */ -var async = require('async'); -var SolcW = require('./solcW.js'); +let async = require('../utils/async_extend.js'); +let SolcW = require('./solcW.js'); -function asyncEachObject(object, iterator, callback) { - async.each( - Object.keys(object || {}), - function(key, next){ - iterator(key, object[key], next); - }, - callback - ); -} -async.eachObject = asyncEachObject; - -var Compiler = function(options) { - this.plugins = options.plugins; - this.logger = options.logger; -}; - -Compiler.prototype.compile_contracts = function(contractFiles, cb) { - var self = this; - - var available_compilers = { - //".se": this.compile_serpent - ".sol": this.compile_solidity.bind(this) - }; - - if (this.plugins) { - var compilerPlugins = this.plugins.getPluginsFor('compilers'); - if (compilerPlugins.length > 0) { - compilerPlugins.forEach(function(plugin) { - plugin.compilers.forEach(function(compilerObject) { - available_compilers[compilerObject.extension] = compilerObject.cb; - }); - }); - } +class Compiler { + constructor(options) { + this.plugins = options.plugins; + this.logger = options.logger; } - var compiledObject = {}; + compile_contracts(contractFiles, cb) { - async.eachObject(available_compilers, - function(extension, compiler, callback) { - // TODO: warn about files it doesn't know how to compile - var matchingFiles = contractFiles.filter(function(file) { - return (file.filename.match(/\.[0-9a-z]+$/)[0] === extension); - }); + let available_compilers = { + //".se": this.compile_serpent + ".sol": this.compile_solidity.bind(this) + }; - compiler.call(compiler, matchingFiles || [], function(err, compileResult) { - Object.assign(compiledObject, compileResult); - callback(err, compileResult); - }); - }, - function (err) { - cb(err, compiledObject); + if (this.plugins) { + let compilerPlugins = this.plugins.getPluginsFor('compilers'); + if (compilerPlugins.length > 0) { + compilerPlugins.forEach(function (plugin) { + plugin.compilers.forEach(function (compilerObject) { + available_compilers[compilerObject.extension] = compilerObject.cb; + }); + }); + } } - ); -}; -Compiler.prototype.compile_solidity = function(contractFiles, cb) { - var self = this; - var input = {}; - var solcW; - async.waterfall([ - function prepareInput(callback) { - for (var i = 0; i < contractFiles.length; i++){ - // TODO: this depends on the config - var filename = contractFiles[i].filename.replace('app/contracts/',''); - input[filename] = contractFiles[i].content.toString(); - } - callback(); - }, - function loadCompiler(callback) { - // TODO: there ino need to load this twice - solcW = new SolcW(); - if (solcW.isCompilerLoaded()) { - return callback(); - } + let compiledObject = {}; - 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) { - return callback(new Error ("Solidity errors: " + output.errors).message); + 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) { + return (file.filename.match(/\.[0-9a-z]+$/)[0] === extension); + }); + + compiler.call(compiler, matchingFiles || [], function (err, compileResult) { + Object.assign(compiledObject, compileResult); + callback(err, compileResult); + }); + }, + function (err) { + cb(err, compiledObject); + } + ); + } + + compile_solidity(contractFiles, cb) { + let self = this; + let input = {}; + let solcW; + async.waterfall([ + function prepareInput(callback) { + for (let i = 0; i < contractFiles.length; i++) { + // TODO: this depends on the config + let filename = contractFiles[i].filename.replace('app/contracts/', ''); + input[filename] = contractFiles[i].content.toString(); + } + callback(); + }, + function loadCompiler(callback) { + // TODO: there ino need to load this twice + solcW = new SolcW(); + if (solcW.isCompilerLoaded()) { + return callback(); } - callback(null, output); - }); - }, - function createCompiledObject(output, callback) { - var json = output.contracts; - compiled_object = {}; + 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) { + return callback(new Error("Solidity errors: " + output.errors).message); + } + callback(null, output); + }); + }, + function createCompiledObject(output, callback) { + let json = output.contracts; - for (var className in json) { - var contract = json[className]; + let compiled_object = {}; - 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); + 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]; + + 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); + } + callback(null, compiled_object); } - callback(null, compiled_object); - } - ], function(err, result) { - cb(err, result); - }); -}; + ], function (err, result) { + cb(err, result); + }); + } +} module.exports = Compiler; diff --git a/lib/contracts/contracts.js b/lib/contracts/contracts.js index 4123f543b..54fb8e896 100644 --- a/lib/contracts/contracts.js +++ b/lib/contracts/contracts.js @@ -1,247 +1,251 @@ -var toposort = require('toposort'); -var async = require('async'); +let toposort = require('toposort'); +let async = require('async'); -var Compiler = require('./compiler.js'); +let Compiler = require('./compiler.js'); // TODO: create a contract object -var adjustGas = function(contract) { - var 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; +class ContractsManager { + constructor(options) { + this.contractFiles = options.contractFiles; + this.contractsConfig = options.contractsConfig; + this.contracts = {}; + this.logger = options.logger; + this.plugins = options.plugins; + + this.contractDependencies = {}; } -}; -var ContractsManager = function(options) { - this.contractFiles = options.contractFiles; - this.contractsConfig = options.contractsConfig; - this.contracts = {}; - this.logger = options.logger; - this.plugins = options.plugins; - - this.contractDependencies = {}; -}; - -ContractsManager.prototype.build = function(done) { - var self = this; - async.waterfall([ - function compileContracts(callback) { - var compiler = new Compiler({plugins: self.plugins, logger: self.logger}); - compiler.compile_contracts(self.contractFiles, function(err, compiledObject) { + build(done) { + let self = this; + async.waterfall([ + function compileContracts(callback) { + let compiler = new Compiler({plugins: self.plugins, logger: self.logger}); + compiler.compile_contracts(self.contractFiles, function (err, compiledObject) { self.compiledContracts = compiledObject; callback(err); }); - }, - function prepareContractsFromConfig(callback) { - var className, contract; - for(className in self.contractsConfig.contracts) { - contract = self.contractsConfig.contracts[className]; + }, + function prepareContractsFromConfig(callback) { + let className, contract; + for (className in self.contractsConfig.contracts) { + contract = self.contractsConfig.contracts[className]; - contract.className = className; - contract.args = contract.args || []; + contract.className = className; + contract.args = contract.args || []; - self.contracts[className] = contract; - } - callback(); - }, - function setDeployIntention(callback) { - var className, contract; - for(className in self.contracts) { - contract = self.contracts[className]; - contract.deploy = (contract.deploy === undefined) || contract.deploy; - } - callback(); - }, - function prepareContractsFromCompilation(callback) { - var className, compiledContract, contractConfig, contract; - for(className in self.compiledContracts) { - compiledContract = self.compiledContracts[className]; - contractConfig = self.contractsConfig.contracts[className]; - - contract = self.contracts[className] || {className: className, args: []}; - - contract.code = compiledContract.code; - contract.runtimeBytecode = compiledContract.runtimeBytecode; - contract.realRuntimeBytecode = (contract.realRuntimeBytecode || contract.runtimeBytecode); - contract.swarmHash = compiledContract.swarmHash; - contract.gasEstimates = compiledContract.gasEstimates; - contract.functionHashes = compiledContract.functionHashes; - contract.abiDefinition = compiledContract.abiDefinition; - - contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto'; - adjustGas(contract); - - contract.gasPrice = contract.gasPrice || self.contractsConfig.gasPrice; - contract.type = 'file'; - contract.className = className; - - self.contracts[className] = contract; - } - callback(); - }, - /*eslint complexity: ["error", 11]*/ - function dealWithSpecialConfigs(callback) { - var className, contract, parentContractName, parentContract; - - for(className in self.contracts) { - contract = self.contracts[className]; - - if (contract.instanceOf === undefined) { continue; } - - parentContractName = contract.instanceOf; - parentContract = self.contracts[parentContractName]; - - if (parentContract === className) { - self.logger.error(className + ": instanceOf is set to itself"); - continue; + self.contracts[className] = contract; } - - if (parentContract === undefined) { - self.logger.error(className + ": couldn't find instanceOf contract " + parentContractName); - continue; + 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) { + compiledContract = self.compiledContracts[className]; + contractConfig = self.contractsConfig.contracts[className]; - if (parentContract.args && parentContract.args.length > 0 && ((contract.args && contract.args.length === 0) || contract.args === undefined)) { - contract.args = parentContract.args; + contract = self.contracts[className] || {className: className, args: []}; + + contract.code = compiledContract.code; + contract.runtimeBytecode = compiledContract.runtimeBytecode; + contract.realRuntimeBytecode = (contract.realRuntimeBytecode || contract.runtimeBytecode); + contract.swarmHash = compiledContract.swarmHash; + contract.gasEstimates = compiledContract.gasEstimates; + contract.functionHashes = compiledContract.functionHashes; + contract.abiDefinition = compiledContract.abiDefinition; + + contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto'; + self.adjustGas(contract); + + contract.gasPrice = contract.gasPrice || self.contractsConfig.gasPrice; + contract.type = 'file'; + contract.className = className; + + self.contracts[className] = contract; } + callback(); + }, + /*eslint complexity: ["error", 11]*/ + function dealWithSpecialConfigs(callback) { + let className, contract, parentContractName, parentContract; + + for (className in self.contracts) { + contract = self.contracts[className]; + + if (contract.instanceOf === undefined) { + continue; + } + + parentContractName = contract.instanceOf; + parentContract = self.contracts[parentContractName]; + + if (parentContract === className) { + self.logger.error(className + ": instanceOf is set to itself"); + continue; + } + + if (parentContract === undefined) { + self.logger.error(className + ": couldn't find instanceOf contract " + parentContractName); + continue; + } + + if (parentContract.args && parentContract.args.length > 0 && ((contract.args && contract.args.length === 0) || contract.args === undefined)) { + contract.args = parentContract.args; + } + + if (contract.code !== undefined) { + self.logger.error(className + " has code associated to it but it's configured as an instanceOf " + parentContractName); + } + + contract.code = parentContract.code; + contract.runtimeBytecode = parentContract.runtimeBytecode; + contract.gasEstimates = parentContract.gasEstimates; + contract.functionHashes = parentContract.functionHashes; + contract.abiDefinition = parentContract.abiDefinition; + + contract.gas = contract.gas || parentContract.gas; + contract.gasPrice = contract.gasPrice || parentContract.gasPrice; + contract.type = 'instance'; - if (contract.code !== undefined) { - self.logger.error(className + " has code associated to it but it's configured as an instanceOf " + parentContractName); } + callback(); + }, + function removeContractsWithNoCode(callback) { + let className, contract; + for (className in self.contracts) { + contract = self.contracts[className]; - contract.code = parentContract.code; - contract.runtimeBytecode = parentContract.runtimeBytecode; - contract.gasEstimates = parentContract.gasEstimates; - contract.functionHashes = parentContract.functionHashes; - contract.abiDefinition = parentContract.abiDefinition; - - contract.gas = contract.gas || parentContract.gas; - contract.gasPrice = contract.gasPrice || parentContract.gasPrice; - contract.type = 'instance'; - - } - callback(); - }, - function removeContractsWithNoCode(callback) { - var className, contract; - for(className in self.contracts) { - contract = self.contracts[className]; - - if (contract.code === undefined) { - self.logger.error(className + " has no code associated"); - delete self.contracts[className]; - } - } - self.logger.trace(self.contracts); - callback(); - }, - function determineDependencies(callback) { - var className, contract; - for(className in self.contracts) { - contract = self.contracts[className]; - - if (contract.args === []) continue; - - var ref = contract.args; - for (var j = 0; j < ref.length; j++) { - var arg = ref[j]; - if (arg[0] === "$") { - self.contractDependencies[className] = self.contractDependencies[className] || []; - self.contractDependencies[className].push(arg.substr(1)); + if (contract.code === undefined) { + self.logger.error(className + " has no code associated"); + delete self.contracts[className]; } } + self.logger.trace(self.contracts); + callback(); + }, + function determineDependencies(callback) { + let className, contract; + for (className in self.contracts) { + contract = self.contracts[className]; + + if (contract.args === []) continue; + + let ref = contract.args; + 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)); + } + } + } + callback(); } - callback(); - } - ], function(err, result) { - if (err) { - self.logger.error("Error Compiling/Building contracts: " + err); - } - self.logger.trace("finished".underline); - done(err, self); - }); -}; - -ContractsManager.prototype.getContract = function(className) { - return this.contracts[className]; -}; - -ContractsManager.prototype.sortContracts = function(contractList) { - var converted_dependencies = [], i; - - for(var contract in this.contractDependencies) { - var dependencies = this.contractDependencies[contract]; - for(i=0; i < dependencies.length; i++) { - converted_dependencies.push([contract, dependencies[i]]); - } + ], function (err, result) { + if (err) { + self.logger.error("Error Compiling/Building contracts: " + err); + } + self.logger.trace("finished".underline); + done(err, self); + }); } - var orderedDependencies = toposort(converted_dependencies).reverse(); - - var newList = contractList.sort(function(a,b) { - var order_a = orderedDependencies.indexOf(a.className); - var order_b = orderedDependencies.indexOf(b.className); - return order_a - order_b; - }); - - return newList; -}; - -// TODO: should be built contracts -ContractsManager.prototype.listContracts = function() { - var contracts = []; - for(var className in this.contracts) { - var contract = this.contracts[className]; - contracts.push(contract); + getContract(className) { + return this.contracts[className]; } - return this.sortContracts(contracts); -}; -ContractsManager.prototype.contractsState = function() { - var data = []; + sortContracts(contractList) { + let converted_dependencies = [], i; - for(var className in this.contracts) { - var contract = this.contracts[className]; - - var contractData; - - if (contract.deploy === false) { - contractData = [ - className.green, - 'Interface or set to not deploy'.green, - "\t\tn/a".green - ]; - } else if (contract.error) { - contractData = [ - className.green, - (contract.error).red, - '\t\tError'.red - ]; - } else { - contractData = [ - className.green, - (contract.deployedAddress || '...').green, - ((contract.deployedAddress !== undefined) ? "\t\tDeployed".green : "\t\tPending".magenta) - ]; + for (let contract in this.contractDependencies) { + let dependencies = this.contractDependencies[contract]; + for (i = 0; i < dependencies.length; i++) { + converted_dependencies.push([contract, dependencies[i]]); + } } - data.push(contractData); + let orderedDependencies = toposort(converted_dependencies).reverse(); + + let newList = contractList.sort(function (a, b) { + let order_a = orderedDependencies.indexOf(a.className); + let order_b = orderedDependencies.indexOf(b.className); + return order_a - order_b; + }); + + return newList; } - return data; -}; + // TODO: should be built contracts + listContracts() { + let contracts = []; + for (let className in this.contracts) { + let contract = this.contracts[className]; + contracts.push(contract); + } + return this.sortContracts(contracts); + } + + contractsState() { + let data = []; + + for (let className in this.contracts) { + let contract = this.contracts[className]; + + let contractData; + + if (contract.deploy === false) { + contractData = [ + className.green, + 'Interface or set to not deploy'.green, + "\t\tn/a".green + ]; + } else if (contract.error) { + contractData = [ + className.green, + (contract.error).red, + '\t\tError'.red + ]; + } else { + contractData = [ + className.green, + (contract.deployedAddress || '...').green, + ((contract.deployedAddress !== undefined) ? "\t\tDeployed".green : "\t\tPending".magenta) + ]; + } + + data.push(contractData); + } + + 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; diff --git a/lib/contracts/deploy.js b/lib/contracts/deploy.js index 5e3ae70ac..f9b2259be 100644 --- a/lib/contracts/deploy.js +++ b/lib/contracts/deploy.js @@ -1,166 +1,168 @@ -var async = require('async'); +let async = require('async'); -var RunCode = require('../core/runCode.js'); +let RunCode = require('../core/runCode.js'); -var DeployTracker = require('./deploy_tracker.js'); -var ABIGenerator = require('./abi.js'); +let DeployTracker = require('./deploy_tracker.js'); +let ABIGenerator = require('./abi.js'); -var Deploy = function(options) { - this.web3 = options.web3; - this.contractsManager = options.contractsManager; - this.logger = options.logger; - this.env = options.env; +class Deploy { + constructor(options) { + this.web3 = options.web3; + this.contractsManager = options.contractsManager; + this.logger = options.logger; + this.env = options.env; - this.deployTracker = new DeployTracker({ - logger: options.logger, chainConfig: options.chainConfig, web3: options.web3, env: this.env - }); -}; - -Deploy.prototype.determineArguments = function(suppliedArgs) { - var realArgs = [], l, arg, contractName, referedContract; - - for (l = 0; l < suppliedArgs.length; l++) { - arg = suppliedArgs[l]; - if (arg[0] === "$") { - contractName = arg.substr(1); - referedContract = this.contractsManager.getContract(contractName); - realArgs.push(referedContract.deployedAddress); - } else { - realArgs.push(arg); - } + this.deployTracker = new DeployTracker({ + logger: options.logger, chainConfig: options.chainConfig, web3: options.web3, env: this.env + }); } - return realArgs; -}; + determineArguments(suppliedArgs) { + let realArgs = [], l, arg, contractName, referedContract; -Deploy.prototype.checkAndDeployContract = function(contract, params, callback) { - var self = this; - var suppliedArgs; - var realArgs; - var arg; - var l; - var contractName; - var referedContract; - contract.error = false; - - if (contract.deploy === false) { - self.logger.contractsState(self.contractsManager.contractsState()); - return callback(); - } - - if (contract.address !== undefined) { - - realArgs = self.determineArguments(params || contract.args); - - contract.deployedAddress = contract.address; - self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, contract.address); - self.deployTracker.save(); - self.logger.contractsState(self.contractsManager.contractsState()); - return callback(); - } - - var trackedContract = self.deployTracker.getContract(contract.className, contract.realRuntimeBytecode, contract.args); - - if (trackedContract && this.web3.eth.getCode(trackedContract.address) !== "0x") { - 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 { - - realArgs = self.determineArguments(params || contract.args); - - this.deployContract(contract, realArgs, function(err, address) { - if (err) { - return callback(new Error(err)); + for (l = 0; l < suppliedArgs.length; l++) { + arg = suppliedArgs[l]; + if (arg[0] === "$") { + contractName = arg.substr(1); + referedContract = this.contractsManager.getContract(contractName); + realArgs.push(referedContract.deployedAddress); + } else { + realArgs.push(arg); } - self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, address); + } + + return realArgs; + } + + checkAndDeployContract(contract, params, callback) { + let self = this; + let suppliedArgs; + let realArgs; + let arg; + let l; + let contractName; + let referedContract; + contract.error = false; + + if (contract.deploy === false) { + self.logger.contractsState(self.contractsManager.contractsState()); + return callback(); + } + + if (contract.address !== undefined) { + + realArgs = self.determineArguments(params || contract.args); + + contract.deployedAddress = contract.address; + self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, contract.address); self.deployTracker.save(); self.logger.contractsState(self.contractsManager.contractsState()); + return callback(); + } - if (contract.onDeploy !== undefined) { - self.logger.info('executing onDeploy commands'); - var abiGenerator = new ABIGenerator({contractsManager: self.contractsManager}); - var abi = abiGenerator.generateContracts(false); - var cmds = contract.onDeploy.join(';\n'); + let trackedContract = self.deployTracker.getContract(contract.className, contract.realRuntimeBytecode, contract.args); - RunCode.doEval(abi + "\n" + cmds, self.web3); + if (trackedContract && this.web3.eth.getCode(trackedContract.address) !== "0x") { + 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 { + + realArgs = self.determineArguments(params || contract.args); + + this.deployContract(contract, realArgs, function (err, address) { + if (err) { + return callback(new Error(err)); + } + self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, address); + self.deployTracker.save(); + self.logger.contractsState(self.contractsManager.contractsState()); + + if (contract.onDeploy !== undefined) { + self.logger.info('executing onDeploy commands'); + let abiGenerator = new ABIGenerator({contractsManager: self.contractsManager}); + let abi = abiGenerator.generateContracts(false); + let cmds = contract.onDeploy.join(';\n'); + + RunCode.doEval(abi + "\n" + cmds, self.web3); + } + + callback(); + }); + } + + } + + deployContract(contract, params, callback) { + let self = this; + let contractObject = this.web3.eth.contract(contract.abiDefinition); + + let contractParams = (params || contract.args).slice(); + + this.web3.eth.getAccounts(function (err, accounts) { + if (err) { + return callback(new Error(err)); } - callback(); + // 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" + contract.code, + gas: contract.gas, + gasPrice: contract.gasPrice + }); + + 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'; + } + 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); + } + }); + + contractObject["new"].apply(contractObject, contractParams); }); } -}; + deployAll(done) { + let self = this; + this.logger.info("deploying contracts"); -Deploy.prototype.deployContract = function(contract, params, callback) { - var self = this; - var contractObject = this.web3.eth.contract(contract.abiDefinition); - - var contractParams = (params || contract.args).slice(); - - this.web3.eth.getAccounts(function(err, accounts) { - if (err) { - return callback(new Error(err)); - } - - // 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" + contract.code, - gas: contract.gas, - gasPrice: contract.gasPrice - }); - - 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); - var 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'; + async.eachOfSeries(this.contractsManager.listContracts(), + function (contract, key, callback) { + self.logger.trace(arguments); + self.checkAndDeployContract(contract, null, callback); + }, + function (err, results) { + if (err) { + self.logger.error("error deploying contracts"); + self.logger.error(err.message); + self.logger.debug(err.stack); } - 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.logger.info("finished deploying contracts"); + self.logger.trace(arguments); + done(); } - }); + ); - contractObject["new"].apply(contractObject, contractParams); - }); -}; - -Deploy.prototype.deployAll = function(done) { - var self = this; - this.logger.info("deploying contracts"); - - async.eachOfSeries(this.contractsManager.listContracts(), - function(contract, key, callback) { - self.logger.trace(arguments); - self.checkAndDeployContract(contract, null, callback); - }, - function(err, results) { - if (err) { - self.logger.error("error deploying contracts"); - self.logger.error(err.message); - self.logger.debug(err.stack); - } - self.logger.info("finished deploying contracts"); - self.logger.trace(arguments); - done(); - } - ); - -}; + } +} module.exports = Deploy; diff --git a/lib/contracts/deploy_manager.js b/lib/contracts/deploy_manager.js index 0293b97c2..3b3ec750e 100644 --- a/lib/contracts/deploy_manager.js +++ b/lib/contracts/deploy_manager.js @@ -1,85 +1,90 @@ -var async = require('async'); -var Web3 = require('web3'); +let async = require('async'); +let Deploy = require('./deploy.js'); +let ContractsManager = require('./contracts.js'); -var Deploy = require('./deploy.js'); -var ContractsManager = require('./contracts.js'); - -var DeployManager = function(options) { - this.config = options.config; - this.logger = options.logger; - this.blockchainConfig = this.config.blockchainConfig; - this.plugins = options.plugins; - this.events = options.events; - this.web3 = options.web3; - this.chainConfig = (options.trackContracts !== false) ? this.config.chainTracker : false; -}; - -DeployManager.prototype.deployContracts = function(done) { - var self = this; - - if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { - self.logger.info("Blockchain component is disabled in the config".underline); - self.events.emit('blockchainDisabled', {}); - return done(); +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; } - async.waterfall([ - function buildContracts(callback) { - var contractsManager = new ContractsManager({ - contractFiles: self.config.contractsFiles, - contractsConfig: self.config.contractsConfig, - logger: self.logger, - plugins: self.plugins - }); - contractsManager.build(callback); - }, - function connectWithWeb3(contractsManager, callback) { - var web3; - if (self.web3) { - web3 = self.web3; - } else { - web3 = new Web3(); - var web3Endpoint = 'http://' + self.config.blockchainConfig.rpcHost + ':' + self.config.blockchainConfig.rpcPort; - web3.setProvider(new web3.providers.HttpProvider(web3Endpoint)); - if (!web3.isConnected()) { - self.logger.error(("Couldn't connect to " + web3Endpoint.underline + " 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")); - } + deployContracts(done) { + let self = this; + + if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) { + self.logger.info("Blockchain component is disabled in the config".underline); + this.events.emit('blockchainDisabled', {}); + return done(); } - callback(null, contractsManager, web3); - }, - function setDefaultAccount(contractsManager, web3, callback) { - web3.eth.getAccounts(function(err, accounts) { + + 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); + }, + function checkWeb3IsConnected(contractsManager, callback) { + if (!self.web3) { + return callback(Error("no web3 instance found")); + } + if (self.web3.currentProvider.isConnected !== undefined && !self.web3.isConnected()) { + 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) { + if (err) { + 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) { + return callback(new Error(err)); + } + let accountConfig = self.config.blockchainConfig.account; + let selectedAccount = accountConfig && accountConfig.address; + web3.eth.defaultAccount = (selectedAccount || accounts[0]); + callback(null, contractsManager, web3); + }); + }, + function deployAllContracts(contractsManager, web3, callback) { + let deploy = new Deploy({ + web3: web3, + contractsManager: contractsManager, + logger: self.logger, + chainConfig: self.chainConfig, + env: self.config.env + }); + deploy.deployAll(function () { + self.events.emit('contractsDeployed', contractsManager); + callback(null, contractsManager); + }); + } + ], function (err, result) { if (err) { - return callback(new Error(err)); + done(err, null); + } else { + done(null, result); } - var accountConfig = self.config.blockchainConfig.account; - var selectedAccount = accountConfig && accountConfig.address; - web3.eth.defaultAccount = (selectedAccount || accounts[0]); - callback(null, contractsManager, web3); - }); - }, - function deployAllContracts(contractsManager, web3, callback) { - var deploy = new Deploy({ - web3: web3, - contractsManager: contractsManager, - logger: self.logger, - chainConfig: self.chainConfig, - env: self.config.env - }); - deploy.deployAll(function() { - self.events.emit('contractsDeployed', contractsManager); - callback(null, contractsManager); }); } - ], function(err, result) { - if (err) { - done(err, null); - } else { - done(null, result); - } - }); -}; + +} module.exports = DeployManager; diff --git a/lib/contracts/deploy_tracker.js b/lib/contracts/deploy_tracker.js index 79152057d..38874c115 100644 --- a/lib/contracts/deploy_tracker.js +++ b/lib/contracts/deploy_tracker.js @@ -1,56 +1,62 @@ -var fs = require('../core/fs.js'); +let fs = require('../core/fs.js'); -var DeployTracker = function(options) { - this.logger = options.logger; - this.env = options.env; - this.chainConfig = options.chainConfig; - this.web3 = options.web3; +class DeployTracker { + constructor(options) { + this.logger = options.logger; + this.env = options.env; + this.chainConfig = options.chainConfig; + this.web3 = options.web3; - if (this.chainConfig === false) { - this.currentChain = {contracts: []}; - return; + if (this.chainConfig === false) { + this.currentChain = {contracts: []}; + return; + } + + // TODO: need to make this async + let block = this.web3.eth.getBlock(0); + let chainId = block.hash; + + if (this.chainConfig[chainId] === undefined) { + this.chainConfig[chainId] = {contracts: {}}; + } + + this.currentChain = this.chainConfig[chainId]; + + this.currentChain.name = this.env; + // TODO: add other params + //this.currentChain.networkId = ""; + //this.currentChain.networkType = "custom" } - // TODO: need to make this async - var block = this.web3.eth.getBlock(0); - var chainId = block.hash; - - if (this.chainConfig[chainId] === undefined) { - this.chainConfig[chainId] = {contracts: {}}; + loadConfig(config) { + this.chainConfig = config; + return this; } - this.currentChain = this.chainConfig[chainId]; + trackContract(contractName, code, args, address) { + this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))] = { + name: contractName, + address: address + }; + } - this.currentChain.name = this.env; - // TODO: add other params - //this.currentChain.networkId = ""; - //this.currentChain.networkType = "custom" -}; + getContract(contractName, code, args) { + let contract = this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))]; + if (contract && contract.address === undefined) { + return false; + } + return contract; + } -DeployTracker.prototype.loadConfig = function(config) { - this.chainConfig = config; - return this; -}; - -DeployTracker.prototype.trackContract = function(contractName, code, args, address) { - this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))] = { - name: contractName, - address: address - }; -}; - -DeployTracker.prototype.getContract = function(contractName, code, args) { - var contract = this.currentChain.contracts[this.web3.sha3(code + contractName + args.join(','))]; - if (contract && contract.address === undefined) { return false; } - return contract; -}; - -// TODO: abstract this -// chainConfig can be an abstract PersistentObject -DeployTracker.prototype.save = function() { - if (this.chainConfig === false) { return; } - fs.writeJSONSync("./chains.json", this.chainConfig); -}; + // TODO: abstract this + // chainConfig can be an abstract PersistentObject + save() { + if (this.chainConfig === false) { + return; + } + fs.writeJSONSync("./chains.json", this.chainConfig, {spaces: 2}); + } +} module.exports = DeployTracker; diff --git a/lib/contracts/solcP.js b/lib/contracts/solcP.js index 2332091fd..4c903fc1f 100644 --- a/lib/contracts/solcP.js +++ b/lib/contracts/solcP.js @@ -1,18 +1,18 @@ -var solc; +let solc; -process.on('message', function(msg) { +process.on('message', function (msg) { if (msg.action === 'loadCompiler') { solc = require('solc'); process.send({result: "loadedCompiler"}); } if (msg.action === 'compile') { - var output = solc.compile(msg.obj, msg.optimize); + let output = solc.compile(msg.obj, msg.optimize); process.send({result: "compilation", output: output}); } }); -process.on('exit', function() { +process.on('exit', function () { process.exit(0); }); diff --git a/lib/contracts/solcW.js b/lib/contracts/solcW.js index eec22b10e..f18407a98 100644 --- a/lib/contracts/solcW.js +++ b/lib/contracts/solcW.js @@ -1,36 +1,38 @@ -var utils = require('../core/utils.js'); -var solcProcess; -var compilerLoaded = false; +let utils = require('../utils/utils.js'); +let solcProcess; +let compilerLoaded = false; -var SolcW = function() { -}; +class SolcW { -SolcW.prototype.load_compiler = function(done) { - if (compilerLoaded) { done(); } - solcProcess = require('child_process').fork(utils.joinPath(__dirname, '/solcP.js')); - solcProcess.once('message', function(msg) { - if (msg.result !== 'loadedCompiler') { - return; + load_compiler(done) { + if (compilerLoaded) { + done(); } - compilerLoaded = true; - done(); - }); - solcProcess.send({action: 'loadCompiler'}); -}; + solcProcess = require('child_process').fork(utils.joinPath(__dirname, '/solcP.js')); + solcProcess.once('message', function (msg) { + if (msg.result !== 'loadedCompiler') { + return; + } + compilerLoaded = true; + done(); + }); + solcProcess.send({action: 'loadCompiler'}); + } -SolcW.prototype.isCompilerLoaded = function() { - return (compilerLoaded === true); -}; + isCompilerLoaded() { + return (compilerLoaded === true); + } -SolcW.prototype.compile = function(obj, optimize, done) { - solcProcess.once('message', function(msg) { - if (msg.result !== 'compilation') { - return; - } - done(msg.output); - }); - solcProcess.send({action: 'compile', obj: obj, optimize: optimize}); -}; + 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; diff --git a/lib/core/config.js b/lib/core/config.js index 3db31a9a9..c87b6761e 100644 --- a/lib/core/config.js +++ b/lib/core/config.js @@ -1,6 +1,6 @@ var fs = require('./fs.js'); var Plugins = require('./plugins.js'); -var utils = require('./utils.js'); +var utils = require('../utils/utils.js'); // TODO: add wrapper for fs so it can also work in the browser // can work with both read and save diff --git a/lib/core/core.js b/lib/core/core.js index 424eda08c..04a700d02 100644 --- a/lib/core/core.js +++ b/lib/core/core.js @@ -1,5 +1,5 @@ +class Core { -var Core = function() {}; - +} module.exports = Core; diff --git a/lib/core/engine.js b/lib/core/engine.js index 730ca3470..47af0ad85 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -1,151 +1,251 @@ -var Events = require('./events.js'); -var Logger = require('./logger.js'); -var Config = require('./config.js'); +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 DeployManager = require('../contracts/deploy_manager.js'); +let ABIGenerator = require('../contracts/abi.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'); -var DeployManager = require('../contracts/deploy_manager.js'); -var ABIGenerator = require('../contracts/abi.js'); -var Dashboard = require('../dashboard/dashboard.js'); -var ServicesMonitor = require('./services.js'); -var Pipeline = require('../pipeline/pipeline.js'); -var Server = require('../pipeline/server.js'); -var Watch = require('../pipeline/watch.js'); - -var Engine = function(options) { - this.env = options.env; - this.embarkConfig = options.embarkConfig; - this.interceptLogs = options.interceptLogs; - this.version = "2.4.2"; -}; - -Engine.prototype.init = function(_options) { - var options = _options || {}; - this.events = new Events(); - this.logger = options.logger || new Logger({logLevel: 'debug'}); - this.config = new Config({env: this.env, logger: this.logger, events: this.events}); - this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs}); - this.plugins = this.config.plugins; -}; - -Engine.prototype.startService = function(serviceName, _options) { - var options = _options || {}; - - var services = { - "monitor": this.monitorService, - "pipeline": this.pipelineService, - "abi": this.abiService, - "deployment": this.deploymentService, - "fileWatcher": this.fileWatchService, - "webServer": this.webServerService - }; - - var service = services[serviceName]; - - if (!service) { - throw new Error("unknown service: " + serviceName); +class Engine { + constructor(options) { + this.env = options.env; + this.embarkConfig = options.embarkConfig; + this.interceptLogs = options.interceptLogs; + this.version = options.version; } - // need to be careful with circular references due to passing the web3 object - //this.logger.trace("calling: " + serviceName + "(" + JSON.stringify(options) + ")"); - return service.apply(this, [options]); -}; + 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.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs}); + this.plugins = this.config.plugins; -Engine.prototype.monitorService = function(options) { - var servicesMonitor = new ServicesMonitor({ - logger: this.logger, - config: this.config, - serverHost: options.serverHost, - serverPort: options.serverPort, - runWebserver: options.runWebserver, - version: this.version - }); - servicesMonitor.startMonitor(); -}; + this.servicesMonitor = new ServicesMonitor({events: this.events, logger: this.logger}); + this.servicesMonitor.addCheck('embarkVersion', function (cb) { + return cb({name: 'Embark ' + self.version, status: 'on'}); + }, 0); + } -Engine.prototype.pipelineService = function(options) { - var self = this; - this.logger.setStatus("Building Assets"); - var pipeline = new Pipeline({ - buildDir: this.config.buildDir, - contractsFiles: this.config.contractsFiles, - assetFiles: this.config.assetFiles, - logger: this.logger, - plugins: this.plugins - }); - this.events.on('abi', function(abi) { - self.currentAbi = abi; - pipeline.build(abi); - 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, path); - // self.events.emit('outputDone'); - // } - //}); -}; + startMonitor() { + let self = this; + if (this.plugins) { + let servicePlugins = this.plugins.getPluginsFor('serviceChecks'); + servicePlugins.forEach(function (plugin) { + plugin.serviceChecks.forEach(function (pluginCheck) { + self.servicesMonitor.addCheck(pluginCheck.checkName, pluginCheck.checkFn, pluginCheck.time); + }); + }); + } + this.servicesMonitor.startMonitor(); + } -Engine.prototype.abiService = function(options) { - var self = this; - var generateABICode = function(contractsManager) { - var abiGenerator = new ABIGenerator({ - blockchainConfig: self.config.blockchainConfig, - contractsManager: contractsManager, - plugins: self.plugins, - storageConfig: self.config.storageConfig, - communicationConfig: self.config.communicationConfig + startService(serviceName, _options) { + let options = _options || {}; + + let services = { + "pipeline": this.pipelineService, + "abi": this.abiService, + "deployment": this.deploymentService, + "fileWatcher": this.fileWatchService, + "webServer": this.webServerService, + "ipfs": this.ipfsService, + "web3": this.web3Service + }; + + let service = services[serviceName]; + + if (!service) { + throw new Error("unknown service: " + serviceName); + } + + // need to be careful with circular references due to passing the web3 object + //this.logger.trace("calling: " + serviceName + "(" + JSON.stringify(options) + ")"); + return service.apply(this, [options]); + } + + pipelineService(options) { + let self = this; + this.logger.setStatus("Building Assets"); + let pipeline = new Pipeline({ + buildDir: this.config.buildDir, + contractsFiles: this.config.contractsFiles, + assetFiles: this.config.assetFiles, + logger: this.logger, + plugins: this.plugins }); - var embarkJSABI = abiGenerator.generateABI({useEmbarkJS: true}); - var vanillaABI = abiGenerator.generateABI({useEmbarkJS: false}); - var vanillaContractsABI = abiGenerator.generateContracts(false); + this.events.on('abi', function (abi, contractsJSON) { + self.currentAbi = abi; + self.contractsJSON = contractsJSON; + pipeline.build(abi, contractsJSON); + 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'); + // } + //}); + } - self.events.emit('abi-contracts-vanila', vanillaContractsABI); - self.events.emit('abi-vanila', vanillaABI); - self.events.emit('abi', embarkJSABI); - }; - this.events.on('contractsDeployed', generateABICode); - this.events.on('blockchainDisabled', generateABICode); -}; + abiService(options) { + let self = this; + let generateABICode = function (contractsManager) { + let abiGenerator = new ABIGenerator({ + blockchainConfig: self.config.blockchainConfig, + contractsManager: contractsManager, + plugins: self.plugins, + storageConfig: self.config.storageConfig, + communicationConfig: self.config.communicationConfig + }); + let embarkJSABI = abiGenerator.generateABI({useEmbarkJS: true}); + let vanillaABI = abiGenerator.generateABI({useEmbarkJS: false}); + let vanillaContractsABI = abiGenerator.generateContracts(false); + let contractsJSON = abiGenerator.generateContractsJSON(); -Engine.prototype.deploymentService = function(options) { - var self = this; - this.deployManager = new DeployManager({ - web3: options.web3, - trackContracts: options.trackContracts, - config: this.config, - logger: this.logger, - plugins: this.plugins, - events: this.events - }); + self.events.emit('abi-contracts-vanila', vanillaContractsABI, contractsJSON); + self.events.emit('abi-vanila', vanillaABI, contractsJSON); + self.events.emit('abi', embarkJSABI, contractsJSON); + }; + this.events.on('contractsDeployed', generateABICode); + this.events.on('blockchainDisabled', generateABICode); + } - this.events.on('file-event', function(fileType, path) { - // 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') { + deploymentService(options) { + let self = this; + this.deployManager = new DeployManager({ + web3: options.web3 || self.web3, + trackContracts: options.trackContracts, + config: this.config, + logger: this.logger, + plugins: this.plugins, + events: this.events + }); + + this.events.on('file-event', function (fileType, path) { + // 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') { self.config.reloadConfig(); - self.deployManager.deployContracts(function() {}); - //} - }); -}; + self.deployManager.deployContracts(function () { + }); + //} + }); + } -Engine.prototype.fileWatchService = function(options) { - this.logger.setStatus("Watching for changes"); - var watch = new Watch({logger: this.logger, events: this.events}); - watch.start(); -}; + fileWatchService(options) { + this.logger.setStatus("Watching for changes"); + let watch = new Watch({logger: this.logger, events: this.events}); + watch.start(); + } -Engine.prototype.webServerService = function(options) { - var webServerConfig = this.config.webServerConfig; - if (!webServerConfig.enabled) { return; } - this.logger.setStatus("Starting Server"); - var server = new Server({ - logger: this.logger, - host: options.host || webServerConfig.host, - port: options.port || webServerConfig.port - }); - server.start(function(){}); -}; + 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 () { + }); + } + + 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'}); + }); + }); + } + else { + return cb({name: "IPFS ", status: 'off'}); + } + }); + }); + } + + web3Service(options) { + let self = this; + this.web3 = options.web3; + if (this.web3 === undefined) { + this.web3 = new Web3(); + let web3Endpoint = 'http://' + this.config.blockchainConfig.rpcHost + ':' + this.config.blockchainConfig.rpcPort; + this.web3.setProvider(new this.web3.providers.HttpProvider(web3Endpoint)); + } + + 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 { + return cb({name: "No Blockchain node found", status: 'off'}); + } + }); + + 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'}); + } + }); + }); + } +} module.exports = Engine; diff --git a/lib/core/fs.js b/lib/core/fs.js index dbbf74808..8b3be66a6 100644 --- a/lib/core/fs.js +++ b/lib/core/fs.js @@ -1,5 +1,5 @@ -var fs = require('fs-extra'); -var utils = require('./utils.js'); +let fs = require('fs-extra'); +let utils = require('../utils/utils.js'); function mkdirpSync() { return fs.mkdirpSync.apply(fs.mkdirpSync, arguments); @@ -25,7 +25,7 @@ function writeJSONSync() { return fs.writeJSONSync.apply(fs.writeJSONSync, arguments); } -function existsSync(){ +function existsSync() { return fs.existsSync.apply(fs.existsSync, arguments); } diff --git a/lib/core/logger.js b/lib/core/logger.js index 5007977c5..0bcf0994d 100644 --- a/lib/core/logger.js +++ b/lib/core/logger.js @@ -1,40 +1,52 @@ -var colors = require('colors'); +let colors = require('colors'); -var Logger = function(options) { - this.logLevels = ['error', 'warn', 'info', 'debug', 'trace']; - this.logLevel = options.logLevel || 'info'; - this.logFunction = options.logFunction || console.log; - this.contractsState = options.contractsState || function() {}; - this.availableServices = options.availableServices || function() {}; - this.setStatus = options.setStatus || console.log; -}; +class Logger { + constructor(options) { + 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; + } +} -Logger.prototype.error = function(txt) { - if (!(this.shouldLog('error'))) { return; } +Logger.prototype.error = function (txt) { + if (!txt || !(this.shouldLog('error'))) { + return; + } this.logFunction(txt.red); }; -Logger.prototype.warn = function(txt) { - if (!(this.shouldLog('warn'))) { return; } +Logger.prototype.warn = function (txt) { + if (!txt || !(this.shouldLog('warn'))) { + return; + } this.logFunction(txt.yellow); }; -Logger.prototype.info = function(txt) { - if (!(this.shouldLog('info'))) { return; } +Logger.prototype.info = function (txt) { + if (!txt || !(this.shouldLog('info'))) { + return; + } this.logFunction(txt.green); }; -Logger.prototype.debug = function(txt) { - if (!(this.shouldLog('debug'))) { return; } +Logger.prototype.debug = function (txt) { + if (!txt || !(this.shouldLog('debug'))) { + return; + } this.logFunction(txt); }; -Logger.prototype.trace = function(txt) { - if (!(this.shouldLog('trace'))) { return; } +Logger.prototype.trace = function (txt) { + if (!txt || !(this.shouldLog('trace'))) { + return; + } this.logFunction(txt); }; -Logger.prototype.shouldLog = function(level) { +Logger.prototype.shouldLog = function (level) { return (this.logLevels.indexOf(level) <= this.logLevels.indexOf(this.logLevel)); }; diff --git a/lib/core/plugin.js b/lib/core/plugin.js index 32765ea3e..45a498f07 100644 --- a/lib/core/plugin.js +++ b/lib/core/plugin.js @@ -1,6 +1,6 @@ /*jshint esversion: 6, loopfunc: true */ var fs = require('./fs.js'); -var utils = require('./utils.js'); +var utils = require('../utils/utils.js'); // TODO: pass other params like blockchainConfig, contract files, etc.. var Plugin = function(options) { @@ -17,6 +17,7 @@ var Plugin = function(options) { this.contractsConfigs = []; this.contractsFiles = []; this.compilers = []; + this.serviceChecks = []; this.pluginTypes = []; this.logger = options.logger; this.events = options.events; @@ -98,6 +99,11 @@ Plugin.prototype.registerConsoleCommand = function(cb) { this.pluginTypes.push('console'); }; +Plugin.prototype.registerServiceCheck = function(checkName, checkFn, time) { + this.serviceChecks.push({checkName: checkName, checkFn: checkFn, time: time}); + this.pluginTypes.push('serviceChecks'); +}; + Plugin.prototype.has = function(pluginType) { return this.pluginTypes.indexOf(pluginType) >= 0; }; diff --git a/lib/core/plugins.js b/lib/core/plugins.js index 4132a2803..c3319183a 100644 --- a/lib/core/plugins.js +++ b/lib/core/plugins.js @@ -1,5 +1,5 @@ var Plugin = require('./plugin.js'); -var utils = require('./utils.js'); +var utils = require('../utils/utils.js'); var Plugins = function(options) { this.pluginList = options.plugins || []; diff --git a/lib/core/runCode.js b/lib/core/runCode.js index 7134b94af..6c89be039 100644 --- a/lib/core/runCode.js +++ b/lib/core/runCode.js @@ -1,5 +1,5 @@ -var Web3 = require('web3'); -var web3; +let Web3 = require('web3'); +let web3; // ====================== // the eval is used for evaluating some of the contact calls for different purposes diff --git a/lib/core/services.js b/lib/core/services.js deleted file mode 100644 index a04f672eb..000000000 --- a/lib/core/services.js +++ /dev/null @@ -1,113 +0,0 @@ -var Web3 = require('web3'); -var async = require('async'); -var http = require('http'); -var utils = require('./utils.js'); - -// TODO: needs a refactor and be done in a different way -var ServicesMonitor = function(options) { - this.logger = options.logger; - this.interval = options.interval || 5000; - this.config = options.config; - this.serverHost = options.serverHost || 'localhost'; - this.serverPort = options.serverPort || 8000; - this.runWebserver = options.runWebserver; - this.version = options.version; -}; - -ServicesMonitor.prototype.startMonitor = function() { - this.check(); - this.monitor = setInterval(this.check.bind(this), this.interval); -}; - -ServicesMonitor.prototype.stopMonitor = function() { -}; - -ServicesMonitor.prototype.check = function() { - var self = this; - async.waterfall([ - function connectWeb3(callback) { - self.logger.trace('connectWeb3'); - var web3 = new Web3(); - var web3Endpoint = 'http://' + self.config.blockchainConfig.rpcHost + ':' + self.config.blockchainConfig.rpcPort; - web3.setProvider(new web3.providers.HttpProvider(web3Endpoint)); - callback(null, web3, []); - }, - function addEmbarkVersion(web3, result, callback) { - self.logger.trace('addEmbarkVersion'); - result.push(('Embark ' + self.version).green); - callback(null, web3, result); - }, - function checkEthereum(web3, result, callback) { - self.logger.trace('checkEthereum'); - var service; - if (web3.isConnected()) { - service = (web3.version.node.split("/")[0] + " " + web3.version.node.split("/")[1].split("-")[0] + " (Ethereum)").green; - } else { - service = "No Blockchain node found".red; - } - result.push(service); - callback(null, web3, result); - }, - function checkWhisper(web3, result, callback) { - self.logger.trace('checkWhisper'); - web3.version.getWhisper(function(err, res) { - var service = 'Whisper'; - result.push(err ? service.red : service.green); - callback(null, result); - }); - }, - function checkIPFS(result, callback) { - self.logger.trace('checkIPFS'); - - 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..."); - http.get('http://localhost:5001/api/v0/version', function(res) { - var body = ''; - res.on('data', function(d) { - body += d; - }); - res.on('end', function() { - var parsed = JSON.parse(body); - if(parsed.Version){ - result.push(("IPFS " + parsed.Version).green); - } - else{ - result.push("IPFS".green); - } - callback(null, result); - }); - res.on('error', function(err) { - self.logger.trace("Check IPFS version error: " + err); - result.push("IPFS".green); - callback(null, result); - }); - }); - } - else { - result.push('IPFS'.red); - return callback(null, result); - } - }); - }, - function checkDevServer(result, callback) { - var host = self.serverHost || self.config.webServerConfig.host; - var port = self.serverPort || self.config.webServerConfig.port; - self.logger.trace('checkDevServer'); - var devServer = 'Webserver (http://' + host + ':' + port + ')'; - devServer = (self.runWebserver) ? devServer.green : devServer.red; - result.push(devServer); - callback(null, result); - } - ], function(err, result) { - if (err) { - self.logger.error(err.message); - } else { - self.logger.availableServices(result); - } - }); -}; - -module.exports = ServicesMonitor; diff --git a/lib/core/services_monitor.js b/lib/core/services_monitor.js new file mode 100644 index 000000000..e0abfb1ad --- /dev/null +++ b/lib/core/services_monitor.js @@ -0,0 +1,80 @@ +let async = require('../utils/async_extend.js'); + +class ServicesMonitor { + constructor(options) { + this.events = options.events; + this.logger = options.logger; + this.checkList = {}; + this.checkTimers = {}; + this.checkState = {}; + this.working = false; + } +} + +ServicesMonitor.prototype.initCheck = function (checkName) { + let self = this; + let check = this.checkList[checkName]; + + if (!check) { + return false; + } + + self.events.on('check:' + checkName, function (obj) { + if (check && check.status === 'off' && obj.status === 'on') { + self.events.emit('check:backOnline:' + checkName); + } + if (check && check.status === 'on' && obj.status === 'off') { + self.events.emit('check:wentOffline:' + checkName); + } + self.checkState[checkName] = {name: obj.name, status: obj.status}; + check.status = obj.status; + self.events.emit("servicesState", self.checkState); + }); + + if (check.interval !== 0) { + self.checkTimers[checkName] = setInterval(function () { + check.fn.call(check.fn, function (obj) { + self.events.emit('check:' + checkName, obj); + }); + }, check.interval); + } + + check.fn.call(check.fn, function (obj) { + self.events.emit('check:' + checkName, obj); + }); +}; + +ServicesMonitor.prototype.addCheck = function (checkName, checkFn, time) { + let self = this; + this.logger.trace('add check: ' + checkName); + this.checkList[checkName] = {fn: checkFn, interval: time || 5000}; + + if (this.working) { + this.initCheck(checkName); + } +}; + +ServicesMonitor.prototype.stopCheck = function (name) { + clearInterval(this.checkTimers[name]); + delete this.checkTimers[name]; + delete this.checkList[name]; + delete this.checkState[name]; +}; + +ServicesMonitor.prototype.startMonitor = function () { + let self = this; + this.working = true; + this.logger.trace('startMonitor'); + + async.eachObject(this.checkList, function (checkName, check, callback) { + self.initCheck(checkName); + callback(); + }, function (err) { + if (err) { + self.logger.error("error running service check"); + self.logger.error(err.message); + } + }); +}; + +module.exports = ServicesMonitor; diff --git a/lib/core/test.js b/lib/core/test.js index ac1b5e376..39346762e 100644 --- a/lib/core/test.js +++ b/lib/core/test.js @@ -12,6 +12,7 @@ var Deploy = require('../contracts/deploy.js'); var Config = require('./config.js'); var RunCode = require('./runCode.js'); var TestLogger = require('./test_logger.js'); +var web3; var getSimulator = function() { try { @@ -45,7 +46,7 @@ var Test = function(options) { }); this.engine.init({ - logger: new TestLogger({logLevel: this.options.logLevel || 'debug'}) + logger: new TestLogger({logLevel: 'debug'}) }); this.sim = getSimulator(); diff --git a/lib/core/test_logger.js b/lib/core/test_logger.js index 7795f1a41..0d85baa01 100644 --- a/lib/core/test_logger.js +++ b/lib/core/test_logger.js @@ -1,52 +1,65 @@ -var colors = require('colors'); +let colors = require('colors'); // TODO: just logFunction changes, probably doesn't need a whole new module just // for this -var TestLogger = function(options) { - this.logLevels = ['error', 'warn', 'info', 'debug', 'trace']; - this.logs = []; - this.logLevel = options.logLevel || 'info'; -}; +class TestLogger { + constructor(options) { + this.logLevels = ['error', 'warn', 'info', 'debug', 'trace']; + this.logs = []; + this.logLevel = options.logLevel || 'info'; + } -TestLogger.prototype.logFunction = function() { - this.logs.push(arguments); -}; + logFunction() { + this.logs.push(arguments); + } -TestLogger.prototype.contractsState = function() { - this.logs.push(arguments); -}; + contractsState() { + this.logs.push(arguments); + } -TestLogger.prototype.availableServices = function() { - this.logs.push(arguments); -}; + availableServices() { + this.logs.push(arguments); + } -TestLogger.prototype.error = function(txt) { - if (!(this.shouldLog('error'))) { return; } - this.logFunction(txt.red); -}; + error(txt) { + if (!(this.shouldLog('error'))) { + return; + } + this.logFunction(txt.red); + } -TestLogger.prototype.warn = function(txt) { - if (!(this.shouldLog('warn'))) { return; } - this.logFunction(txt.yellow); -}; + warn(txt) { + if (!(this.shouldLog('warn'))) { + return; + } + this.logFunction(txt.yellow); + } -TestLogger.prototype.info = function(txt) { - if (!(this.shouldLog('info'))) { return; } - this.logFunction(txt.green); -}; + info(txt) { + if (!(this.shouldLog('info'))) { + return; + } + this.logFunction(txt.green); + } -TestLogger.prototype.debug = function(txt) { - if (!(this.shouldLog('debug'))) { return; } - this.logFunction(txt); -}; + debug(txt) { + if (!(this.shouldLog('debug'))) { + return; + } + this.logFunction(txt); + } -TestLogger.prototype.trace = function(txt) { - if (!(this.shouldLog('trace'))) { return; } - this.logFunction(txt); -}; + trace(txt) { + if (!(this.shouldLog('trace'))) { + return; + } + this.logFunction(txt); + } -TestLogger.prototype.shouldLog = function(level) { - return (this.logLevels.indexOf(level) <= this.logLevels.indexOf(this.logLevel)); -}; + shouldLog(level) { + return (this.logLevels.indexOf(level) <= this.logLevels.indexOf(this.logLevel)); + } + +} module.exports = TestLogger; diff --git a/lib/dashboard/command_history.js b/lib/dashboard/command_history.js index 43e1d2d9d..73c575611 100644 --- a/lib/dashboard/command_history.js +++ b/lib/dashboard/command_history.js @@ -1,28 +1,29 @@ - -var CommandHistory = function() { - this.history = []; - this.pointer = -1; -}; - -CommandHistory.prototype.addCommand = function(cmd) { - this.history.push(cmd); - this.pointer = this.history.length; -}; - -CommandHistory.prototype.getPreviousCommand = function(cmd) { - if (this.pointer >= 0) { - this.pointer--; +class CommandHistory { + constructor() { + this.history = []; + this.pointer = -1; } - return this.history[this.pointer]; -}; -CommandHistory.prototype.getNextCommand = function(cmd) { - if (this.pointer >= this.history.length) { - this.pointer = this.history.length - 1; - return ''; + addCommand(cmd) { + this.history.push(cmd); + this.pointer = this.history.length; } - this.pointer++; - return this.history[this.pointer]; -}; + + getPreviousCommand(cmd) { + if (this.pointer >= 0) { + this.pointer--; + } + return this.history[this.pointer]; + } + + getNextCommand(cmd) { + if (this.pointer >= this.history.length) { + this.pointer = this.history.length - 1; + return ''; + } + this.pointer++; + return this.history[this.pointer]; + } +} module.exports = CommandHistory; diff --git a/lib/dashboard/console.js b/lib/dashboard/console.js index c510f204f..ca8ccd828 100644 --- a/lib/dashboard/console.js +++ b/lib/dashboard/console.js @@ -1,60 +1,62 @@ -var utils = require('../core/utils.js'); -var RunCode = require('../core/runCode.js'); +let utils = require('../utils/utils.js'); +let RunCode = require('../core/runCode.js'); -var Console = function(options) { - this.plugins = options.plugins; - this.version = options.version; -}; - -Console.prototype.runCode = function(code) { - RunCode.doEval(code); // jshint ignore:line -}; - -Console.prototype.processEmbarkCmd = function(cmd) { - if (cmd === 'help') { - var helpText = [ - 'Welcome to Embark ' + this.version, - '', - 'possible commands are:', - // 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', - '', - 'The web3 object and the interfaces for the deployed contracts and their methods are also available' - ]; - return helpText.join('\n'); - } else if (cmd === 'quit') { - utils.exit(); - } - return false; -}; - -Console.prototype.executeCmd = function(cmd, callback) { - var plugin, pluginOutput; - var plugins = this.plugins.getPluginsFor('console'); - for (var i = 0; i < plugins.length; i++) { - plugin = plugins[i]; - pluginOutput = plugin.runCommands(cmd, {}); - if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput); +class Console { + constructor(options) { + this.plugins = options.plugins; + this.version = options.version; } - var output = this.processEmbarkCmd(cmd); - if (output) { - return callback(output); + runCode(code) { + RunCode.doEval(code); // jshint ignore:line } - try { - var result = RunCode.doEval(cmd); - return callback(result); + processEmbarkCmd (cmd) { + if (cmd === 'help') { + let helpText = [ + 'Welcome to Embark ' + this.version, + '', + 'possible commands are:', + // 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', + '', + 'The web3 object and the interfaces for the deployed contracts and their methods are also available' + ]; + return helpText.join('\n'); + } else if (cmd === 'quit') { + utils.exit(); + } + return false; } - catch(e) { - if (e.message.indexOf('not defined') > 0) { - return callback(("error: " + e.message).red + ("\nType " + "help".bold + " to see the list of available commands").cyan); - } else { - return callback(e.message); + + executeCmd(cmd, callback) { + var plugin, pluginOutput; + var plugins = this.plugins.getPluginsFor('console'); + for (var i = 0; i < plugins.length; i++) { + plugin = plugins[i]; + pluginOutput = plugin.runCommands(cmd, {}); + if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput); + } + + let output = this.processEmbarkCmd(cmd); + if (output) { + return callback(output); + } + + try { + let result = RunCode.doEval(cmd); + return callback(result); + } + catch (e) { + if (e.message.indexOf('not defined') > 0) { + return callback(("error: " + e.message).red + ("\nType " + "help".bold + " to see the list of available commands").cyan); + } else { + return callback(e.message); + } } } -}; +} module.exports = Console; diff --git a/lib/dashboard/dashboard.js b/lib/dashboard/dashboard.js index d67b142d0..9aa48b0cc 100644 --- a/lib/dashboard/dashboard.js +++ b/lib/dashboard/dashboard.js @@ -1,43 +1,45 @@ -var async = require('async'); +let async = require('async'); -var Monitor = require('./monitor.js'); -var Console = require('./console.js'); +let Monitor = require('./monitor.js'); +let Console = require('./console.js'); -var Dashboard = function(options) { - this.logger = options.logger; - this.plugins = options.plugins; - this.version = options.version; - this.env = options.env; -}; +class Dashboard { + constructor(options) { + this.logger = options.logger; + this.plugins = options.plugins; + this.version = options.version; + this.env = options.env; + } -Dashboard.prototype.start = function(done) { - var console, monitor; - var self = this; + start(done) { + let console, monitor; + let self = this; - async.waterfall([ - function startConsole(callback) { - console = new Console({plugins: self.plugins, version: self.version}); - callback(); - }, - function startMonitor(callback) { - monitor = new Monitor({env: self.env, console: console}); - self.logger.logFunction = monitor.logEntry; - self.logger.contractsState = monitor.setContracts; - self.logger.availableServices = monitor.availableServices; - self.logger.setStatus = monitor.setStatus.bind(monitor); + async.waterfall([ + function startConsole(callback) { + console = new Console({plugins: self.plugins, version: self.version}); + callback(); + }, + function startMonitor(callback) { + monitor = new Monitor({env: self.env, console: console}); + self.logger.logFunction = monitor.logEntry; + self.logger.contractsState = monitor.setContracts; + self.logger.setStatus = monitor.setStatus.bind(monitor); - self.logger.info('========================'.bold.green); - self.logger.info(('Welcome to Embark ' + self.version).yellow.bold); - self.logger.info('========================'.bold.green); + self.logger.info('========================'.bold.green); + self.logger.info(('Welcome to Embark ' + self.version).yellow.bold); + self.logger.info('========================'.bold.green); - // TODO: do this after monitor is rendered - callback(); - } - ], function() { - self.console = console; - self.monitor = monitor; - done(); - }); -}; + // TODO: do this after monitor is rendered + callback(); + } + ], function () { + self.console = console; + self.monitor = monitor; + done(); + }); + } + +} module.exports = Dashboard; diff --git a/lib/dashboard/monitor.js b/lib/dashboard/monitor.js index 54a262ab0..60be81b87 100644 --- a/lib/dashboard/monitor.js +++ b/lib/dashboard/monitor.js @@ -1,348 +1,365 @@ /*jshint esversion: 6 */ -var blessed = require("blessed"); -var CommandHistory = require('./command_history.js'); +let blessed = require("blessed"); +let CommandHistory = require('./command_history.js'); -function Dashboard(options) { - var title = (options && options.title) || "Embark 2.4.2"; - this.env = options.env; - this.console = options.console; - this.history = new CommandHistory(); +class Dashboard { + constructor(options) { + let title = (options && options.title) || "Embark " + options.version; + this.env = options.env; + this.console = options.console; + this.history = new CommandHistory(); - this.color = (options && options.color) || "green"; - this.minimal = (options && options.minimal) || false; + this.color = (options && options.color) || "green"; + this.minimal = (options && options.minimal) || false; - this.screen = blessed.screen({ - smartCSR: true, - title: title, - dockBorders: false, - fullUnicode: true, - autoPadding: true - }); + this.screen = blessed.screen({ + smartCSR: true, + title: title, + dockBorders: false, + fullUnicode: true, + autoPadding: true + }); - this.layoutLog(); - this.layoutStatus(); - this.layoutModules(); - this.layoutCmd(); + this.layoutLog(); + this.layoutStatus(); + this.layoutModules(); + this.layoutCmd(); - this.screen.key(["C-c"], function() { - process.exit(0); - }); + this.screen.key(["C-c"], function () { + process.exit(0); + }); - this.logEntry = this.logEntry.bind(this); - this.setContracts = this.setContracts.bind(this); - this.availableServices = this.availableServices.bind(this); + this.logEntry = this.logEntry.bind(this); + this.setContracts = this.setContracts.bind(this); + this.availableServices = this.availableServices.bind(this); - this.status.setContent(this.env.green); + this.status.setContent(this.env.green); + + this.screen.render(); + this.input.focus(); + } + + 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); + } + } + + this.progress.setContent(services.join('\n')); + this.screen.render(); + } + + setStatus(status) { + this.operations.setContent(status); + this.screen.render(); + } + + setContracts(contracts) { + let data = []; + + data.push(["Contract Name", "Address", "Status"]); + + contracts.forEach(function (row) { + data.push(row); + }); + + this.moduleTable.setData(data); + this.screen.render(); + } + + logEntry(text) { + this.logText.log(text); + this.screen.render(); + } + + layoutLog() { + this.log = blessed.box({ + label: "Logs", + padding: 1, + width: "100%", + height: "55%", + left: "0%", + top: "42%", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.logText = blessed.log({ + parent: this.log, + tags: true, + width: "100%-5", + //height: '90%', + scrollable: true, + input: false, + alwaysScroll: true, + scrollbar: { + ch: " ", + inverse: true + }, + keys: false, + vi: false, + mouse: true + }); + + this.screen.append(this.log); + } + + layoutModules() { + this.modules = blessed.box({ + label: "Contracts", + tags: true, + padding: 1, + width: "75%", + height: "42%", + left: "0%", + top: "0", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.moduleTable = blessed.table({ + parent: this.modules, + height: "100%", + width: "100%-5", + align: "left", + pad: 1, + margin: "auto", + shrink: true, + scrollable: true, + alwaysScroll: true, + scrollbar: { + ch: " ", + inverse: true + }, + keys: false, + vi: false, + mouse: true, + data: [["ContractName", "Address", "Status"]] + }); + + this.screen.append(this.modules); + } + + layoutAssets() { + this.assets = blessed.box({ + label: "Asset Pipeline", + tags: true, + padding: 1, + width: "50%", + height: "55%", + left: "50%", + top: "42%", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.assetTable = blessed.table({ + parent: this.assets, + height: "100%", + width: "100%-5", + align: "left", + pad: 1, + scrollable: true, + alwaysScroll: true, + scrollbar: { + ch: " ", + inverse: true + }, + keys: false, + vi: false, + mouse: true, + data: [["Name", "Size"]] + }); + + this.screen.append(this.assets); + } + + layoutStatus() { + + this.wrapper = blessed.layout({ + width: "25%", + height: "42%", + top: "0%", + left: "75%", + layout: "grid" + }); + + this.status = blessed.box({ + parent: this.wrapper, + label: "Environment", + tags: true, + padding: { + left: 1 + }, + width: "100%", + height: "20%", + valign: "middle", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.operations = blessed.box({ + parent: this.wrapper, + label: "Status", + tags: true, + padding: { + left: 1 + }, + width: "100%", + height: "20%", + valign: "middle", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.progress = blessed.box({ + parent: this.wrapper, + label: "Available Services", + tags: true, + padding: this.minimal ? { + left: 1 + } : 1, + width: "100%", + height: "60%", + valign: "top", + border: { + type: "line" + }, + style: { + fg: -1, + border: { + fg: this.color + } + } + }); + + this.screen.append(this.wrapper); + } + + layoutCmd() { + this.consoleBox = blessed.box({ + label: 'Console', + tags: true, + padding: 0, + width: '100%', + height: '6%', + left: '0%', + top: '95%', + border: { + type: 'line' + }, + style: { + fg: 'black', + border: { + fg: this.color + } + } + }); + + this.input = blessed.textbox({ + parent: this.consoleBox, + name: 'input', + input: true, + keys: false, + top: 0, + left: 1, + height: '50%', + width: '100%-2', + inputOnFocus: true, + style: { + fg: 'green', + bg: 'black', + focus: { + bg: 'black', + fg: 'green' + } + } + }); + + let self = this; + + this.input.key(["C-c"], function () { + process.exit(0); + }); + + this.input.key(["C-w"], function () { + self.input.clearValue(); + self.input.focus(); + }); + + this.input.key(["up"], function () { + let cmd = self.history.getPreviousCommand(); + self.input.setValue(cmd); + self.input.focus(); + }); + + this.input.key(["down"], function () { + let cmd = self.history.getNextCommand(); + self.input.setValue(cmd); + 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.screen.append(this.consoleBox); + } - this.screen.render(); - this.input.focus(); } -Dashboard.prototype.availableServices = function(services) { - this.progress.setContent(services.join('\n')); - this.screen.render(); -}; - -Dashboard.prototype.setStatus = function(status) { - this.operations.setContent(status); - this.screen.render(); -}; - -Dashboard.prototype.setContracts = function(contracts) { - var data = []; - - data.push(["Contract Name", "Address", "Status"]); - - contracts.forEach(function(row) { - data.push(row); - }); - - this.moduleTable.setData(data); - this.screen.render(); -}; - -Dashboard.prototype.logEntry = function(text) { - this.logText.log(text); - this.screen.render(); -}; - -Dashboard.prototype.layoutLog = function() { - this.log = blessed.box({ - label: "Logs", - padding: 1, - width: "100%", - height: "55%", - left: "0%", - top: "42%", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.logText = blessed.log({ - parent: this.log, - tags: true, - width: "100%-5", - //height: '90%', - scrollable: true, - input: false, - alwaysScroll: true, - scrollbar: { - ch: " ", - inverse: true - }, - keys: false, - vi: false, - mouse: true - }); - - this.screen.append(this.log); -}; - -Dashboard.prototype.layoutModules = function() { - this.modules = blessed.box({ - label: "Contracts", - tags: true, - padding: 1, - width: "75%", - height: "42%", - left: "0%", - top: "0", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.moduleTable = blessed.table({ - parent: this.modules, - height: "100%", - width: "100%-5", - align: "left", - pad: 1, - margin: "auto", - shrink: true, - scrollable: true, - alwaysScroll: true, - scrollbar: { - ch: " ", - inverse: true - }, - keys: false, - vi: false, - mouse: true, - data: [["ContractName", "Address", "Status"]] - }); - - this.screen.append(this.modules); -}; - -Dashboard.prototype.layoutAssets = function() { - this.assets = blessed.box({ - label: "Asset Pipeline", - tags: true, - padding: 1, - width: "50%", - height: "55%", - left: "50%", - top: "42%", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.assetTable = blessed.table({ - parent: this.assets, - height: "100%", - width: "100%-5", - align: "left", - pad: 1, - scrollable: true, - alwaysScroll: true, - scrollbar: { - ch: " ", - inverse: true - }, - keys: false, - vi: false, - mouse: true, - data: [["Name", "Size"]] - }); - - this.screen.append(this.assets); -}; - -Dashboard.prototype.layoutStatus = function() { - - this.wrapper = blessed.layout({ - width: "25%", - height: "42%", - top: "0%", - left: "75%", - layout: "grid" - }); - - this.status = blessed.box({ - parent: this.wrapper, - label: "Environment", - tags: true, - padding: { - left: 1 - }, - width: "100%", - height: "20%", - valign: "middle", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.operations = blessed.box({ - parent: this.wrapper, - label: "Status", - tags: true, - padding: { - left: 1 - }, - width: "100%", - height: "20%", - valign: "middle", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.progress = blessed.box({ - parent: this.wrapper, - label: "Available Services", - tags: true, - padding: this.minimal ? { - left: 1 - } : 1, - width: "100%", - height: "60%", - valign: "top", - border: { - type: "line" - }, - style: { - fg: -1, - border: { - fg: this.color - } - } - }); - - this.screen.append(this.wrapper); -}; - - -Dashboard.prototype.layoutCmd = function() { - this.consoleBox = blessed.box({ - label: 'Console', - tags: true, - padding: 0, - width: '100%', - height: '6%', - left: '0%', - top: '95%', - border: { - type: 'line' - }, - style: { - fg: 'black', - border: { - fg: this.color - } - } - }); - - this.input = blessed.textbox({ - parent: this.consoleBox, - name: 'input', - input: true, - keys: false, - top: 0, - left: 1, - height: '50%', - width: '100%-2', - inputOnFocus: true, - style: { - fg: 'green', - bg: 'black', - focus: { - bg: 'black', - fg: 'green' - } - } - }); - - var self = this; - - this.input.key(["C-c"], function() { - process.exit(0); - }); - - this.input.key(["C-w"], function() { - self.input.clearValue(); - self.input.focus(); - }); - - this.input.key(["up"], function() { - var cmd = self.history.getPreviousCommand(); - self.input.setValue(cmd); - self.input.focus(); - }); - - this.input.key(["down"], function() { - var cmd = self.history.getNextCommand(); - self.input.setValue(cmd); - 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.screen.append(this.consoleBox); -}; - module.exports = Dashboard; diff --git a/lib/index.js b/lib/index.js index b06ad4c60..48c7c1426 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,74 +1,68 @@ /*jshint esversion: 6 */ -var async = require('async'); -//require("./core/debug_util.js")(__filename, async); +let async = require('async'); +// require("./utils/debug_util.js")(__filename, async); -var Web3 = require('web3'); -var colors = require('colors'); +let colors = require('colors'); -var Engine = require('./core/engine.js'); +let Engine = require('./core/engine.js'); -var Blockchain = require('./cmds/blockchain/blockchain.js'); -var Simulator = require('./cmds/simulator.js'); -var TemplateGenerator = require('./cmds/template_generator.js'); +let IPFS = require('./upload/ipfs.js'); +let Swarm = require('./upload/swarm.js'); -var Test = require('./core/test.js'); -var Logger = require('./core/logger.js'); -var Config = require('./core/config.js'); -var Events = require('./core/events.js'); +let version = require('../package.json').version; -var Dashboard = require('./dashboard/dashboard.js'); +class Embark { -var IPFS = require('./upload/ipfs.js'); -var Swarm = require('./upload/swarm.js'); + constructor (options) { + this.version = version; + this.options = options || {}; + } -var Cmd = require('./cmd.js'); + initConfig(env, options) { + let Events = require('./core/events.js'); + let Logger = require('./core/logger.js'); + let Config = require('./core/config.js'); -var Embark = { - - version: '2.4.2', - - process: function(args) { - var cmd = new Cmd(Embark); - cmd.process(args); - }, - - initConfig: function(env, options) { this.events = new Events(); this.logger = new Logger({logLevel: 'debug'}); this.config = new Config({env: env, logger: this.logger, events: this.events}); this.config.loadConfigFiles(options); this.plugins = this.config.plugins; - }, + } - blockchain: function(env, client) { - var blockchain = Blockchain(this.config.blockchainConfig, client, env); - blockchain.run(); - }, + blockchain(env, client) { + return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env).run(); + } - simulator: function(options) { - var simulator = new Simulator({blockchainConfig: this.config.blockchainConfig}); + simulator(options) { + let Simulator = require('./cmds/simulator.js'); + let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig}); simulator.run(options); - }, + } - generateTemplate: function(templateName, destinationFolder, name) { - var templateGenerator = new TemplateGenerator(templateName); + generateTemplate(templateName, destinationFolder, name) { + let TemplateGenerator = require('./cmds/template_generator.js'); + let templateGenerator = new TemplateGenerator(templateName); templateGenerator.generate(destinationFolder, name); - }, + } - run: function(options) { - var self = this; - var env = options.env; + run(options) { + let self = this; + let Dashboard = require('./dashboard/dashboard.js'); - var engine = new Engine({ + let env = options.env; + + let engine = new Engine({ env: options.env, - embarkConfig: 'embark.json' + version: this.version, + embarkConfig: options.embarkConfig || 'embark.json' }); engine.init(); if (!options.useDashboard) { console.log('========================'.bold.green); - console.log(('Welcome to Embark ' + Embark.version).yellow.bold); + console.log(('Welcome to Embark ' + this.version).yellow.bold); console.log('========================'.bold.green); } @@ -78,38 +72,47 @@ var Embark = { return callback(); } - var dashboard = new Dashboard({ + let dashboard = new Dashboard({ logger: engine.logger, plugins: engine.plugins, - version: engine.version, + version: self.version, env: engine.env }); - dashboard.start(function() { - engine.events.on('abi-vanila', function(abi) { + dashboard.start(function () { + engine.events.on('abi-vanila', function (abi) { dashboard.console.runCode(abi); }); + engine.logger.info('dashboard start'); + engine.events.on('servicesState', function (servicesState) { + dashboard.monitor.availableServices(servicesState); + }); + callback(); }); }, function (callback) { - var pluginList = engine.plugins.listPlugins(); + let pluginList = engine.plugins.listPlugins(); if (pluginList.length > 0) { engine.logger.info("loaded plugins: " + pluginList.join(", ")); } - if (options.useDashboard) { - engine.startService("monitor", { - serverHost: options.serverHost, - serverPort: options.serverPort, - runWebserver: options.runWebserver - }); - } + engine.startMonitor(); + engine.startService("web3"); engine.startService("pipeline"); engine.startService("abi"); engine.startService("deployment"); + engine.startService("ipfs"); - engine.deployManager.deployContracts(function() { + engine.events.on('check:backOnline:Ethereum', function () { + engine.logger.info('Ethereum node detected..'); + engine.config.reloadConfig(); + engine.deployManager.deployContracts(function () { + engine.logger.info('Deployment Done'); + }); + }); + + engine.deployManager.deployContracts(function (err) { engine.startService("fileWatcher"); if (options.runWebserver) { engine.startService("webServer", { @@ -117,12 +120,13 @@ var Embark = { port: options.serverPort }); } - callback(); + 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); @@ -130,13 +134,13 @@ var Embark = { engine.events.emit('firstDeploymentDone'); } }); - }, + } - build: function(options) { - var self = this; + build(options) { - var engine = new Engine({ + let engine = new Engine({ env: options.env, + version: this.version, embarkConfig: 'embark.json', interceptLogs: false }); @@ -144,43 +148,46 @@ var Embark = { async.waterfall([ function startServices(callback) { - var pluginList = engine.plugins.listPlugins(); + let pluginList = engine.plugins.listPlugins(); if (pluginList.length > 0) { engine.logger.info("loaded plugins: " + pluginList.join(", ")); } + engine.startService("web3"); engine.startService("pipeline"); engine.startService("abi"); engine.startService("deployment"); callback(); }, function deploy(callback) { - engine.deployManager.deployContracts(function() { - callback(); + engine.deployManager.deployContracts(function (err) { + callback(err); }); } - ], function(err, result) { + ], function (err, result) { if (err) { engine.logger.error(err.message); + engine.logger.debug(err.stack); } else { engine.logger.info("finished building".underline); } // needed due to child processes process.exit(); }); - }, + } - initTests: function(options) { + initTests(options) { + let Test = require('./core/test.js'); return new Test(options); - }, + } // TODO: should deploy if it hasn't already - upload: function(platform) { + upload(platform) { if (platform === 'ipfs') { - var ipfs = new IPFS({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig}); + let ipfs = new IPFS({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig}); ipfs.deploy(); } else if (platform === 'swarm') { - var swarm = new Swarm({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig}); + let swarm = new Swarm({buildDir: 'dist/', plugins: this.plugins, storageConfig: this.config.storageConfig}); swarm.deploy(); } else { console.log(("unknown platform: " + platform).red); @@ -188,7 +195,12 @@ var Embark = { } } +} + +// temporary until next refactor +Embark.initTests = function(options) { + let Test = require('./core/test.js'); + return new Test(options); }; module.exports = Embark; - diff --git a/lib/pipeline/pipeline.js b/lib/pipeline/pipeline.js index 08016070a..d5af6cfb6 100644 --- a/lib/pipeline/pipeline.js +++ b/lib/pipeline/pipeline.js @@ -1,80 +1,94 @@ /*jshint esversion: 6, loopfunc: true */ -var fs = require('../core/fs.js'); +let fs = require('../core/fs.js'); -var Pipeline = function(options) { - this.buildDir = options.buildDir; - this.contractsFiles = options.contractsFiles; - this.assetFiles = options.assetFiles; - this.logger = options.logger; - this.plugins = options.plugins; -}; +class Pipeline { -Pipeline.prototype.build = function(abi, path) { - var self = this; - for(var targetFile in this.assetFiles) { + constructor(options) { + this.buildDir = options.buildDir; + this.contractsFiles = options.contractsFiles; + this.assetFiles = options.assetFiles; + this.logger = options.logger; + this.plugins = options.plugins; + } - var contentFiles = this.assetFiles[targetFile].map(file => { - self.logger.trace("reading " + file.filename); + build(abi, contractsJSON, path) { + let self = this; + for (let targetFile in this.assetFiles) { - var pipelinePlugins = this.plugins.getPluginsFor('pipeline'); + let contentFiles = this.assetFiles[targetFile].map(file => { + self.logger.trace("reading " + file.filename); - if (file.filename === 'embark.js') { - return {content: file.content + "\n" + abi, filename: file.filename, path: file.path, modified: true}; - } else if (file.filename === 'abi.js') { - return {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.modified = true; - return file; - } else { + let pipelinePlugins = this.plugins.getPluginsFor('pipeline'); - if (pipelinePlugins.length > 0) { - pipelinePlugins.forEach(function(plugin) { - try { - if (file.options && file.options.skipPipeline) { - return; + if (file.filename === 'embark.js') { + return {content: file.content + "\n" + abi, filename: file.filename, path: file.path, modified: true}; + } else if (file.filename === 'abi.js') { + return {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.modified = true; + return file; + } else { + + if (pipelinePlugins.length > 0) { + pipelinePlugins.forEach(function (plugin) { + try { + if (file.options && file.options.skipPipeline) { + return; + } + file.content = plugin.runPipeline({targetFile: file.filename, source: file.content}); + file.modified = true; } - file.content = plugin.runPipeline({targetFile: file.filename, source: file.content}); - file.modified = true; - } - catch(err) { - self.logger.error(err.message); - } - }); + catch (err) { + self.logger.error(err.message); + } + }); + } + + return file; + } + }); + + let dir = targetFile.split('/').slice(0, -1).join('/'); + self.logger.trace("creating dir " + this.buildDir + dir); + fs.mkdirpSync(this.buildDir + dir); + + // if it's a directory + if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) { + let targetDir = targetFile; + + if (targetDir.slice(-1) !== '/') { + targetDir = targetDir + '/'; } - return file; + contentFiles.map(function (file) { + let filename = file.filename.replace('app/', ''); + filename = filename.replace(targetDir, ''); + self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim); + + fs.copySync(self.buildDir + targetDir + filename, file.path, {overwrite: true}); + }); + } else { + let content = contentFiles.map(function (file) { + return file.content; + }).join("\n"); + + self.logger.info("writing file " + (this.buildDir + targetFile).bold.dim); + fs.writeFileSync(this.buildDir + targetFile, content); } - }); + } - var dir = targetFile.split('/').slice(0, -1).join('/'); - self.logger.trace("creating dir " + this.buildDir + dir); - fs.mkdirpSync(this.buildDir + dir); + this.buildContracts(contractsJSON); + } - // if it's a directory - if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) { - var targetDir = targetFile; + buildContracts(contractsJSON) { + fs.mkdirpSync(this.buildDir + 'contracts'); - if (targetDir.slice(-1) !== '/') { - targetDir = targetDir + '/'; - } - - contentFiles.map(function(file) { - var filename = file.filename.replace('app/', ''); - filename = filename.replace(targetDir, ''); - self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim); - - fs.copySync(self.buildDir + targetDir + filename, file.path, {overwrite: true}); - }); - } else { - var content = contentFiles.map(function(file) { - return file.content; - }).join("\n"); - - self.logger.info("writing file " + (this.buildDir + targetFile).bold.dim); - fs.writeFileSync(this.buildDir + targetFile, content); + for (let className in contractsJSON) { + let contract = contractsJSON[className]; + fs.writeJSONSync(this.buildDir + 'contracts/' + className + ".json", contract, {spaces: 2}); } } -}; +} module.exports = Pipeline; diff --git a/lib/pipeline/server.js b/lib/pipeline/server.js index afbdad2a8..43d25e573 100644 --- a/lib/pipeline/server.js +++ b/lib/pipeline/server.js @@ -1,24 +1,27 @@ -var finalhandler = require('finalhandler'); -var http = require('http'); -var serveStatic = require('serve-static'); +let finalhandler = require('finalhandler'); +let http = require('http'); +let serveStatic = require('serve-static'); -var Server = function(options) { - this.dist = options.dist || 'dist/'; - this.port = options.port || 8000; - this.hostname = options.host || 'localhost'; - this.logger = options.logger; -}; +class Server { + constructor(options) { + this.dist = options.dist || 'dist/'; + this.port = options.port || 8000; + this.hostname = options.host || 'localhost'; + this.logger = options.logger; + } -Server.prototype.start = function(callback) { - var serve = serveStatic(this.dist, {'index': ['index.html', 'index.htm']}); + start(callback) { + let serve = serveStatic(this.dist, {'index': ['index.html', 'index.htm']}); - var server = http.createServer(function onRequest (req, res) { - serve(req, res, finalhandler(req, res)); - }); + 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(); -}; + this.logger.info("webserver available at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green); + server.listen(this.port, this.hostname); + callback(); + } + +} module.exports = Server; diff --git a/lib/pipeline/watch.js b/lib/pipeline/watch.js index d1b1c8b79..1131bfd29 100644 --- a/lib/pipeline/watch.js +++ b/lib/pipeline/watch.js @@ -1,101 +1,104 @@ /*jshint esversion: 6 */ -var chokidar = require('chokidar'); +let chokidar = require('chokidar'); -var fs = require('../core/fs.js'); +let fs = require('../core/fs.js'); // TODO: this should be receiving the config object not re-reading the // embark.json file -var Watch = function(options) { - this.logger = options.logger; - this.events = options.events; -}; - -Watch.prototype.start = function() { - var self = this; - // TODO: should come from the config object instead of reading the file - // directly - var embarkConfig = fs.readJSONSync("embark.json"); - - this.watchAssets(embarkConfig, function() { - self.logger.trace('ready to watch asset changes'); - }); - - this.watchContracts(embarkConfig, function() { - self.logger.trace('ready to watch contract changes'); - }); - - this.watchConfigs(function() { - self.logger.trace('ready to watch config changes'); - }); - - this.logger.info("ready to watch file changes"); -}; - -Watch.prototype.watchAssets = function(embarkConfig, callback) { - var self = this; - var appConfig = embarkConfig.app; - var filesToWatch = []; - - for(var targetFile in appConfig) { - filesToWatch.push(appConfig[targetFile]); +class Watch { + constructor(options) { + this.logger = options.logger; + this.events = options.events; } - this.watchFiles( - filesToWatch, - function(eventName, path) { - self.logger.info(`${eventName}: ${path}`); - self.events.emit('file-' + eventName, 'asset', path); - self.events.emit('file-event', 'asset', path); - }, - function() { - callback(); + start() { + let self = this; + // TODO: should come from the config object instead of reading the file + // directly + let embarkConfig = fs.readJSONSync("embark.json"); + + this.watchAssets(embarkConfig, function () { + self.logger.trace('ready to watch asset changes'); + }); + + this.watchContracts(embarkConfig, function () { + self.logger.trace('ready to watch contract changes'); + }); + + this.watchConfigs(function () { + self.logger.trace('ready to watch config changes'); + }); + + this.logger.info("ready to watch file changes"); + } + + watchAssets(embarkConfig, callback) { + let self = this; + let appConfig = embarkConfig.app; + let filesToWatch = []; + + for (let targetFile in appConfig) { + filesToWatch.push(appConfig[targetFile]); } - ); -}; -Watch.prototype.watchContracts = function(embarkConfig, callback) { - var self = this; - this.watchFiles( - [embarkConfig.contracts], - function(eventName, path) { - self.logger.info(`${eventName}: ${path}`); - self.events.emit('file-' + eventName, 'contract', path); - self.events.emit('file-event', 'contract', path); - }, - function() { - callback(); - } - ); -}; + this.watchFiles( + filesToWatch, + function (eventName, path) { + self.logger.info(`${eventName}: ${path}`); + self.events.emit('file-' + eventName, 'asset', path); + self.events.emit('file-event', 'asset', path); + }, + function () { + callback(); + } + ); + } -Watch.prototype.watchConfigs = function(callback) { - var self = this; - this.watchFiles( - "config/**/contracts.json", - function(eventName, path) { - self.logger.info(`${eventName}: ${path}`); - self.events.emit('file-' + eventName, 'config', path); - self.events.emit('file-event', 'config', path); - }, - function() { - callback(); - } - ); -}; + watchContracts(embarkConfig, callback) { + let self = this; + this.watchFiles( + [embarkConfig.contracts], + function (eventName, path) { + self.logger.info(`${eventName}: ${path}`); + self.events.emit('file-' + eventName, 'contract', path); + self.events.emit('file-event', 'contract', path); + }, + function () { + callback(); + } + ); + } -Watch.prototype.watchFiles = function(files, changeCallback, doneCallback) { - this.logger.trace('watchFiles'); - this.logger.trace(files); + watchConfigs(callback) { + let self = this; + this.watchFiles( + "config/**/contracts.json", + function (eventName, path) { + self.logger.info(`${eventName}: ${path}`); + self.events.emit('file-' + eventName, 'config', path); + self.events.emit('file-event', 'config', path); + }, + function () { + callback(); + } + ); + } - var configWatcher = chokidar.watch(files, { - ignored: /[\/\\]\./, persistent: true, ignoreInitial: true, followSymlinks: true - }); + watchFiles(files, changeCallback, doneCallback) { + this.logger.trace('watchFiles'); + this.logger.trace(files); - configWatcher - .on('add', path => changeCallback('add', path)) - .on('change', path => changeCallback('change', path)) - .on('unlink', path => changeCallback('remove', path)) - .on('ready', doneCallback); -}; + let configWatcher = chokidar.watch(files, { + ignored: /[\/\\]\./, persistent: true, ignoreInitial: true, followSymlinks: true + }); + + configWatcher + .on('add', path => changeCallback('add', path)) + .on('change', path => changeCallback('change', path)) + .on('unlink', path => changeCallback('remove', path)) + .on('ready', doneCallback); + } + +} module.exports = Watch; diff --git a/lib/upload/ipfs.js b/lib/upload/ipfs.js index 09da3cf46..026949560 100644 --- a/lib/upload/ipfs.js +++ b/lib/upload/ipfs.js @@ -1,56 +1,60 @@ -var colors = require('colors'); -var async = require('async'); -var shelljs = require('shelljs'); +let colors = require('colors'); +let async = require('async'); +let shelljs = require('shelljs'); -var IPFS = function(options) { - this.options = options; - this.buildDir = options.buildDir || 'dist/'; - this.plugins = options.plugins; - this.storageConfig = options.storageConfig; - this.configIpfsBin = this.storageConfig.ipfs_bin || "ipfs"; -}; +class IPFS { -IPFS.prototype.deploy = function() { - var self = this; - async.waterfall([ - function findBinary(callback) { - var ipfs_bin = shelljs.exec('which ' + self.configIpfsBin).output.split("\n")[0]; + constructor(options) { + this.options = options; + this.buildDir = options.buildDir || 'dist/'; + this.plugins = options.plugins; + this.storageConfig = options.storageConfig; + this.configIpfsBin = this.storageConfig.ipfs_bin || "ipfs"; + } - 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"; + deploy() { + let self = this; + async.waterfall([ + function findBinary(callback) { + let ipfs_bin = shelljs.exec('which ' + self.configIpfsBin).output.split("\n")[0]; + + 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"; + } + + return 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.log(cmd.green); + let result = shelljs.exec(cmd); + + return callback(null, result); + }, + function getHashFromOutput(result, callback) { + let rows = result.output.split("\n"); + let dir_row = rows[rows.length - 2]; + let dir_hash = dir_row.split(" ")[1]; + + return 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); + + return callback(); } - - return callback(null, ipfs_bin); - }, - function runCommand(ipfs_bin, callback) { - var cmd = ipfs_bin + " add -r " + self.buildDir; - console.log(("=== adding " + self.buildDir + " to ipfs").green); - console.log(cmd.green); - var result = shelljs.exec(cmd); - - return callback(null, result); - }, - function getHashFromOutput(result, callback) { - var rows = result.output.split("\n"); - var dir_row = rows[rows.length - 2]; - var dir_hash = dir_row.split(" ")[1]; - - return 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); - - return callback(); - } - ], function(err, result) { + ], function (err, result) { if (err) { console.log("error uploading to ipfs".red); console.log(err); } - }); -}; + }); + } + +} module.exports = IPFS; diff --git a/lib/upload/swarm.js b/lib/upload/swarm.js index 667e0e6c9..80fa67c56 100644 --- a/lib/upload/swarm.js +++ b/lib/upload/swarm.js @@ -1,55 +1,57 @@ -var colors = require('colors'); -var async = require('async'); -var shelljs = require('shelljs'); +let colors = require('colors'); +let async = require('async'); +let shelljs = require('shelljs'); -var Swarm = function(options) { - this.options = options; - this.buildDir = options.buildDir || 'dist/'; -}; +class Swarm { + constructor(options) { + this.options = options; + this.buildDir = options.buildDir || 'dist/'; + } -Swarm.prototype.deploy = function() { - var self = this; - async.waterfall([ - function findBinary(callback) { - var swarm_bin = shelljs.exec('which swarm').output.split("\n")[0]; + deploy() { + let self = this; + async.waterfall([ + function findBinary(callback) { + let swarm_bin = shelljs.exec('which swarm').output.split("\n")[0]; - if (swarm_bin==='swarm not found' || swarm_bin === ''){ - console.log('=== WARNING: Swarm not in an executable path. Guessing ~/go/bin/swarm for path'.yellow); - swarm_bin = "~/go/bin/swarm"; + if (swarm_bin === 'swarm not found' || swarm_bin === '') { + console.log('=== WARNING: Swarm not in an executable path. Guessing ~/go/bin/swarm for path'.yellow); + swarm_bin = "~/go/bin/swarm"; + } + + return callback(null, swarm_bin); + }, + function runCommand(swarm_bin, callback) { + let cmd = swarm_bin + " --defaultpath " + self.buildDir + "index.html --recursive up " + self.buildDir; + console.log(("=== adding " + self.buildDir + " to swarm").green); + console.log(cmd.green); + let result = shelljs.exec(cmd); + + return callback(null, result); + }, + function getHashFromOutput(result, callback) { + if (result.code !== 0) { + return callback("couldn't upload, is the swarm daemon running?"); + } + + let rows = result.output.split("\n"); + let dir_hash = rows.reverse()[1]; + + return callback(null, dir_hash); + }, + function printUrls(dir_hash, callback) { + console.log(("=== DApp available at http://localhost:8500/bzz:/" + dir_hash + "/").green); + + return callback(); } - - return callback(null, swarm_bin); - }, - function runCommand(swarm_bin, callback) { - var cmd = swarm_bin + " --defaultpath " + self.buildDir + "index.html --recursive up " + self.buildDir; - console.log(("=== adding " + self.buildDir + " to swarm").green); - console.log(cmd.green); - var result = shelljs.exec(cmd); - - return callback(null, result); - }, - function getHashFromOutput(result, callback) { - if (result.code !== 0) { - return callback("couldn't upload, is the swarm daemon running?"); - } - - var rows = result.output.split("\n"); - var dir_hash = rows.reverse()[1]; - - return callback(null, dir_hash); - }, - function printUrls(dir_hash, callback) { - console.log(("=== DApp available at http://localhost:8500/bzz:/" + dir_hash + "/").green); - - return callback(); - } - ], function(err, result) { + ], function (err, result) { if (err) { console.log("error uploading to swarm".red); console.log(err); } - }); -}; + }); + } +} module.exports = Swarm; diff --git a/lib/utils/async_extend.js b/lib/utils/async_extend.js new file mode 100644 index 000000000..616dc5199 --- /dev/null +++ b/lib/utils/async_extend.js @@ -0,0 +1,15 @@ +let async = require('async'); + +function asyncEachObject(object, iterator, callback) { + async.each( + Object.keys(object || {}), + function (key, next) { + iterator(key, object[key], next); + }, + callback + ); +} + +async.eachObject = asyncEachObject; + +module.exports = async; diff --git a/lib/core/debug_util.js b/lib/utils/debug_util.js similarity index 74% rename from lib/core/debug_util.js rename to lib/utils/debug_util.js index 9fa0233ff..3bc2d35d4 100644 --- a/lib/core/debug_util.js +++ b/lib/utils/debug_util.js @@ -5,9 +5,9 @@ function extend(filename, async) { return; } async._waterfall = async.waterfall; - async.waterfall = function(_tasks, callback) { - var tasks = _tasks.map(function(t) { - var fn = function() { + async.waterfall = function (_tasks, callback) { + let tasks = _tasks.map(function (t) { + let fn = function () { console.log("async " + filename + ": " + t.name); t.apply(t, arguments); }; diff --git a/lib/core/utils.js b/lib/utils/utils.js similarity index 72% rename from lib/core/utils.js rename to lib/utils/utils.js index 0870374ea..3460f1810 100644 --- a/lib/core/utils.js +++ b/lib/utils/utils.js @@ -1,9 +1,9 @@ /*global exit */ -var path = require('path'); -var globule = require('globule'); -var merge = require('merge'); -var http = require('http'); -var shelljs = require('shelljs'); +let path = require('path'); +let globule = require('globule'); +let merge = require('merge'); +let http = require('http'); +let shelljs = require('shelljs'); function joinPath() { return path.join.apply(path.join, arguments); @@ -22,15 +22,19 @@ function recursiveMerge(target, source) { } function checkIsAvailable(url, callback) { - http.get(url, function(res) { + http.get(url, function (res) { callback(true); - }).on('error', function(res) { + }).on('error', function (res) { callback(false); }); } +function httpGet(url, callback) { + return http.get(url, callback); +} + function runCmd(cmd, options) { - var result = shelljs.exec(cmd, options || {silent: true}); + let result = shelljs.exec(cmd, options || {silent: true}); if (result.code !== 0) { console.log("error doing.. " + cmd); console.log(result.output); @@ -53,12 +57,14 @@ function exit(code) { process.exit(code); } +//TODO: Maybe desired to just `module.exports = this` ? module.exports = { joinPath: joinPath, filesMatchingPattern: filesMatchingPattern, fileMatchesPattern: fileMatchesPattern, recursiveMerge: recursiveMerge, checkIsAvailable: checkIsAvailable, + httpGet: httpGet, runCmd: runCmd, cd: cd, sed: sed, diff --git a/package.json b/package.json index d9ff11322..0b71a3484 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "embark", - "version": "2.4.2", + "version": "2.5.0", "description": "Embark is a framework that allows you to easily develop and deploy DApps", "scripts": { "test": "grunt jshint && mocha test/ --no-timeouts" @@ -26,11 +26,14 @@ "fs-extra": "^2.0.0", "globule": "^1.1.0", "merge": "^1.2.0", + "promptly": "^2.1.0", "serve-static": "^1.11.1", "shelljs": "^0.5.0", - "solc": "0.4.8", + "solc": "0.4.11", "toposort": "^1.0.0", - "web3": "^0.18.2" + "underscore": "^1.8.3", + "underscore.string": "^3.3.4", + "web3": "^0.19.1" }, "author": "Iuri Matias ", "contributors": [], @@ -49,6 +52,8 @@ "devDependencies": { "grunt": "^1.0.1", "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-coffee": "^1.0.0", "grunt-contrib-jshint": "^1.0.0", "grunt-mocha-test": "^0.13.2", "matchdep": "^1.0.1", @@ -56,5 +61,19 @@ "mocha-sinon": "^1.1.4", "sinon": "^1.15.4", "toposort": "^1.0.0" + }, + "jshintConfig": { + "indent": 2, + "white": true, + "node": true, + "undef": true, + "-W058": true, + "-W014": true, + "expr": true, + "esversion": 6, + "unused": false, + "globals": { + "node": true + } } } diff --git a/test/abi.js b/test/abi.js index 05a443d31..c542a1bdd 100644 --- a/test/abi.js +++ b/test/abi.js @@ -1,23 +1,23 @@ /*globals describe, it*/ -var ABIGenerator = require('../lib/contracts/abi.js'); -var assert = require('assert'); +let ABIGenerator = require('../lib/contracts/abi.js'); +let assert = require('assert'); // TODO: instead 'eval' the code with a fake web3 object // and check the generate code interacts as expected describe('embark.ABIGenerator', function() { - + this.timeout(0); describe('#generateProvider', function() { - var generator = new ABIGenerator({blockchainConfig: {rpcHost: 'somehost', rpcPort: '1234'}, contractsManager: {}}); + let generator = new ABIGenerator({blockchainConfig: {rpcHost: 'somehost', rpcPort: '1234'}, contractsManager: {}}); it('should generate code to connect to a provider', function() { - var providerCode = "\nvar whenEnvIsLoaded = function(cb) {\n if (typeof window !== 'undefined' && window !== null) {\n window.addEventListener('load', cb);\n } else {\n cb();\n }\n}\nwhenEnvIsLoaded(function() {\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(web3.currentProvider);\n} else if (typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(new Web3.providers.HttpProvider(\"http://somehost:1234\"));\n}\nweb3.eth.defaultAccount = web3.eth.accounts[0];\n})" + var providerCode = "\nvar whenEnvIsLoaded = function(cb) {\n if (typeof document !== 'undefined' && document !== null) {\n document.addEventListener('DOMContentLoaded', cb);\n } else {\n cb();\n }\n}\nwhenEnvIsLoaded(function() {\nif (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(web3.currentProvider);\n} else if (typeof Web3 !== 'undefined') {\n\tweb3 = new Web3(new Web3.providers.HttpProvider(\"http://somehost:1234\"));\n}\nweb3.eth.defaultAccount = web3.eth.accounts[0];\n})" assert.equal(generator.generateProvider(), providerCode); }); }); describe('#generateContracts', function() { - var generator = new ABIGenerator({blockchainConfig: {}, contractsManager: { + let generator = new ABIGenerator({blockchainConfig: {}, contractsManager: { contracts: { SimpleStorage: { abiDefinition: [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"type":"constructor"}], @@ -35,19 +35,19 @@ describe('embark.ABIGenerator', function() { }}); describe('with EmbarkJS', function() { - var withEmbarkJS = true; + let withEmbarkJS = true; it('should generate contract code', function() { - var contractCode = "\n\nwhenEnvIsLoaded(function() {\nSimpleStorage = new EmbarkJS.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x123', code: '12345', gasEstimates: 12000});\n});\nwhenEnvIsLoaded(function() {\nFoo = new EmbarkJS.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x124', code: '123456', gasEstimates: 12000});\n});"; + var contractCode = "\n\nif (whenEnvIsLoaded === undefined) {\n var whenEnvIsLoaded = function(cb) {\n if (typeof document !== 'undefined' && document !== null) {\n document.addEventListener('DOMContentLoaded', cb);\n } else {\n cb();\n }\n }\n}\nwhenEnvIsLoaded(function() {\nSimpleStorage = new EmbarkJS.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x123', code: '12345', gasEstimates: 12000});\n});\nif (whenEnvIsLoaded === undefined) {\n var whenEnvIsLoaded = function(cb) {\n if (typeof document !== 'undefined' && document !== null) {\n document.addEventListener('DOMContentLoaded', cb);\n } else {\n cb();\n }\n }\n}\nwhenEnvIsLoaded(function() {\nFoo = new EmbarkJS.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x124', code: '123456', gasEstimates: 12000});\n});"; assert.equal(generator.generateContracts(withEmbarkJS), contractCode); }); }); describe('with default interface', function() { - var withEmbarkJS = false; + let withEmbarkJS = false; it('should generate contract code', function() { - var contractCode = "\n\nwhenEnvIsLoaded(function() {\nSimpleStorageAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nSimpleStorageContract = web3.eth.contract(SimpleStorageAbi);\nSimpleStorage = SimpleStorageContract.at('0x123');\n});\nwhenEnvIsLoaded(function() {\nFooAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nFooContract = web3.eth.contract(FooAbi);\nFoo = FooContract.at('0x124');\n});"; + var contractCode = "\n\nif (whenEnvIsLoaded === undefined) {\n var whenEnvIsLoaded = function(cb) {\n if (typeof document !== 'undefined' && document !== null) {\n document.addEventListener('DOMContentLoaded', cb);\n } else {\n cb();\n }\n }\n}\nwhenEnvIsLoaded(function() {\nSimpleStorageAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nSimpleStorageContract = web3.eth.contract(SimpleStorageAbi);\nSimpleStorage = SimpleStorageContract.at('0x123');\n});\nif (whenEnvIsLoaded === undefined) {\n var whenEnvIsLoaded = function(cb) {\n if (typeof document !== 'undefined' && document !== null) {\n document.addEventListener('DOMContentLoaded', cb);\n } else {\n cb();\n }\n }\n}\nwhenEnvIsLoaded(function() {\nFooAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nFooContract = web3.eth.contract(FooAbi);\nFoo = FooContract.at('0x124');\n});"; assert.equal(generator.generateContracts(withEmbarkJS), contractCode); }); }); diff --git a/test/blockchain.js b/test/blockchain.js index 43fefaf59..b7690b5f2 100644 --- a/test/blockchain.js +++ b/test/blockchain.js @@ -1,17 +1,18 @@ /*globals describe, it*/ -var Blockchain = require('../lib/cmds/blockchain/blockchain.js'); -var assert = require('assert'); +const Blockchain = require('../lib/cmds/blockchain/blockchain'); -describe('embark.Blockchain', function() { - //var Client = function() {}; +const assert = require('assert'); + +describe('embark.Blockchain', function () { + //let Client = function() {}; //Client.prototype.name = "ClientName"; - describe('#initializer', function() { - //var client = new Client(); + describe('#initializer', function () { + //let client = new Client(); - describe('with empty config', function() { - it('should have a default config', function() { - var config = { + describe('with empty config', function () { + it('should have a default config', function (done) { + let config = { networkType: 'custom', genesisBlock: false, geth_bin: 'geth', @@ -31,15 +32,16 @@ describe('embark.Blockchain', function() { account: {}, bootnodes: "" }; - var blockchain = Blockchain(config, 'geth'); + let blockchain = new Blockchain(config, 'geth'); assert.deepEqual(blockchain.config, config); + done(); }); }); - describe('with config', function() { - it('should take config params', function() { - var config = { + describe('with config', function () { + it('should take config params', function (done) { + let config = { networkType: 'livenet', genesisBlock: 'foo/bar/genesis.json', geth_bin: 'geth', @@ -59,9 +61,10 @@ describe('embark.Blockchain', function() { account: {}, bootnodes: "" }; - var blockchain = Blockchain(config, 'geth'); + let blockchain = new Blockchain(config, 'geth'); assert.deepEqual(blockchain.config, config); + done(); }); }); diff --git a/test/cmd.js b/test/cmd.js new file mode 100644 index 000000000..45e99ebcd --- /dev/null +++ b/test/cmd.js @@ -0,0 +1,52 @@ +let Embark = require('../lib/index'); +let Cmd = require('../lib/cmd'); + +// Function to send a line to stdin +function sendLine(line) { + setImmediate(function () { + process.stdin.emit('data', line + '\n'); + }); +} + +let passingLines = function () { + let lines = []; + lines.push('Initializing Embark Template....'); + lines.push('Installing packages.. this can take a few seconds'); + lines.push('Init complete'); + return lines; +}; + +describe('embark.Cmd', function () { + this.timeout(0); + + describe('#new', function () { + it('it should create an app with a name', function (done) { + let cmd = new Cmd(Embark); + let pl = passingLines(); + let appname = 'deleteapp'; + cmd.newApp(appname, function (output) { + let lines = output.split('\n'); + console.log(lines); + assert.equal(lines[0], pl[0]); + assert.equal(lines[1], pl[1]); + assert.equal(lines[2], pl[2]); + assert.equal(lines[3], 'App ready at ./' + appname); + }); + done(); + }); + + it('it should prompt when given an empty app name', function (done) { + let cmd = new Cmd(Embark); + let pl = passingLines(); + let appname = 'deleteapp'; + + cmd.newApp(undefined, function (output) { + let lines = output.split('\n'); + console.log(lines); + sendLine(appname + '\n'); + assert.equal(lines[0], pl[0]); + }); + done(); + }); + }); +}); \ No newline at end of file diff --git a/test/compiler.js b/test/compiler.js index c44d3ed9b..c829b09e1 100644 --- a/test/compiler.js +++ b/test/compiler.js @@ -1,22 +1,24 @@ /*globals describe, it*/ -var Compiler = require('../lib/contracts/compiler.js'); -var TestLogger = require('../lib/core/test_logger.js'); -var assert = require('assert'); -var fs = require('fs'); +let Compiler = require('../lib/contracts/compiler.js'); +let TestLogger = require('../lib/core/test_logger.js'); +let assert = require('assert'); +let fs = require('fs'); -var readFile = function(file) { +let readFile = function(file) { return {filename: file, content: fs.readFileSync(file).toString()}; }; describe('embark.Compiler', function() { - var compiler = new Compiler({logger: new TestLogger({})}); + let compiler = new Compiler({logger: new TestLogger({})}); describe('#compile_solidity', function() { - var expectedObject = {}; + this.timeout(0); - expectedObject["SimpleStorage"] = {"code":"606060405234610000576040516020806100f083398101604052515b60008190555b505b60bf806100316000396000f300606060405263ffffffff60e060020a6000350416632a1afcd98114603657806360fe47b11460525780636d4ce63c146061575b6000565b346000576040607d565b60408051918252519081900360200190f35b34600057605f6004356083565b005b346000576040608c565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820a250be048d43f54e9afbb37211dc73ba843d23b95863b60afe703903500077220029","realRuntimeBytecode": "606060405263ffffffff60e060020a6000350416632a1afcd98114603657806360fe47b11460525780636d4ce63c146061575b6000565b346000576040607d565b60408051918252519081900360200190f35b34600057605f6004356083565b005b346000576040608c565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820","runtimeBytecode":"606060405263ffffffff60e060020a6000350416632a1afcd98114603657806360fe47b11460525780636d4ce63c146061575b6000565b346000576040607d565b60408051918252519081900360200190f35b34600057605f6004356083565b005b346000576040608c565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820a250be048d43f54e9afbb37211dc73ba843d23b95863b60afe703903500077220029","swarmHash": "a250be048d43f54e9afbb37211dc73ba843d23b95863b60afe70390350007722","gasEstimates":{"creation":[20131,38200],"external":{"get()":269,"set(uint256)":20163,"storedData()":224},"internal":{}},"functionHashes":{"get()":"6d4ce63c","set(uint256)":"60fe47b1","storedData()":"2a1afcd9"},"abiDefinition":[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"payable":false,"type":"constructor"}]}; + let expectedObject = {}; - expectedObject["Token"] = {"code":"6060604052346100005760405160208061048e83398101604052515b600160a060020a033316600090815260208190526040902081905560028190555b505b6104418061004d6000396000f3006060604052361561005c5763ffffffff60e060020a600035041663095ea7b3811461006157806318160ddd1461009157806323b872dd146100b057806370a08231146100e6578063a9059cbb14610111578063dd62ed3e14610141575b610000565b346100005761007d600160a060020a0360043516602435610172565b604080519115158252519081900360200190f35b346100005761009e6101dd565b60408051918252519081900360200190f35b346100005761007d600160a060020a03600435811690602435166044356101e4565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435166102f8565b60408051918252519081900360200190f35b346100005761007d600160a060020a0360043516602435610317565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435811690602435166103da565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a0383166000908152602081905260408120548290101561020a57610000565b600160a060020a03808516600090815260016020908152604080832033909416835292905220548290101561023e57610000565b600160a060020a0383166000908152602081905260409020546102619083610407565b151561026c57610000565b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a0333166000908152602081905260408120548290101561033d57610000565b600160a060020a0383166000908152602081905260409020546103609083610407565b151561036b57610000565b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a7230582017291fa7c1b9234972e866bb8b730096a40f8610da4684f7977498fc0ee8f75b0029","realRuntimeBytecode": "6060604052361561005c5763ffffffff60e060020a600035041663095ea7b3811461006157806318160ddd1461009157806323b872dd146100b057806370a08231146100e6578063a9059cbb14610111578063dd62ed3e14610141575b610000565b346100005761007d600160a060020a0360043516602435610172565b604080519115158252519081900360200190f35b346100005761009e6101dd565b60408051918252519081900360200190f35b346100005761007d600160a060020a03600435811690602435166044356101e4565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435166102f8565b60408051918252519081900360200190f35b346100005761007d600160a060020a0360043516602435610317565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435811690602435166103da565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a0383166000908152602081905260408120548290101561020a57610000565b600160a060020a03808516600090815260016020908152604080832033909416835292905220548290101561023e57610000565b600160a060020a0383166000908152602081905260409020546102619083610407565b151561026c57610000565b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a0333166000908152602081905260408120548290101561033d57610000565b600160a060020a0383166000908152602081905260409020546103609083610407565b151561036b57610000565b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a72305820","runtimeBytecode":"6060604052361561005c5763ffffffff60e060020a600035041663095ea7b3811461006157806318160ddd1461009157806323b872dd146100b057806370a08231146100e6578063a9059cbb14610111578063dd62ed3e14610141575b610000565b346100005761007d600160a060020a0360043516602435610172565b604080519115158252519081900360200190f35b346100005761009e6101dd565b60408051918252519081900360200190f35b346100005761007d600160a060020a03600435811690602435166044356101e4565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435166102f8565b60408051918252519081900360200190f35b346100005761007d600160a060020a0360043516602435610317565b604080519115158252519081900360200190f35b346100005761009e600160a060020a03600435811690602435166103da565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a0383166000908152602081905260408120548290101561020a57610000565b600160a060020a03808516600090815260016020908152604080832033909416835292905220548290101561023e57610000565b600160a060020a0383166000908152602081905260409020546102619083610407565b151561026c57610000565b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a0333166000908152602081905260408120548290101561033d57610000565b600160a060020a0383166000908152602081905260409020546103609083610407565b151561036b57610000565b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a7230582017291fa7c1b9234972e866bb8b730096a40f8610da4684f7977498fc0ee8f75b0029","swarmHash": "17291fa7c1b9234972e866bb8b730096a40f8610da4684f7977498fc0ee8f75b","gasEstimates":{"creation":[40422,217800],"external":{"allowance(address,address)":598,"approve(address,uint256)":22273,"balanceOf(address)":462,"totalSupply()":265,"transfer(address,uint256)":42894,"transferFrom(address,address,uint256)":63334},"internal":{"safeToAdd(uint256,uint256)":41}},"functionHashes":{"allowance(address,address)":"dd62ed3e","approve(address,uint256)":"095ea7b3","balanceOf(address)":"70a08231","totalSupply()":"18160ddd","transfer(address,uint256)":"a9059cbb","transferFrom(address,address,uint256)":"23b872dd"},"abiDefinition":[{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"supply","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"value","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"_allowance","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initial_balance","type":"uint256"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]} + expectedObject["SimpleStorage"] = {"code":"6060604052341561000c57fe5b60405160208061011383398101604052515b60008190555b505b60df806100346000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632a1afcd98114604d57806360fe47b114606c5780636d4ce63c14607e575bfe5b3415605457fe5b605a609d565b60408051918252519081900360200190f35b3415607357fe5b607c60043560a3565b005b3415608557fe5b605a60ac565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820a89e7fdce74e002378f275124c03a50f7796db2cf1f0d1ad12dd8bafe6a8892c0029","realRuntimeBytecode": "606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632a1afcd98114604d57806360fe47b114606c5780636d4ce63c14607e575bfe5b3415605457fe5b605a609d565b60408051918252519081900360200190f35b3415607357fe5b607c60043560a3565b005b3415608557fe5b605a60ac565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820","runtimeBytecode":"606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632a1afcd98114604d57806360fe47b114606c5780636d4ce63c14607e575bfe5b3415605457fe5b605a609d565b60408051918252519081900360200190f35b3415607357fe5b607c60043560a3565b005b3415608557fe5b605a60ac565b60408051918252519081900360200190f35b60005481565b60008190555b50565b6000545b905600a165627a7a72305820a89e7fdce74e002378f275124c03a50f7796db2cf1f0d1ad12dd8bafe6a8892c0029","swarmHash": "a89e7fdce74e002378f275124c03a50f7796db2cf1f0d1ad12dd8bafe6a8892c","gasEstimates":{"creation":[20141,44600],"external":{"get()":400,"set(uint256)":20144,"storedData()":355},"internal":{}},"functionHashes":{"get()":"6d4ce63c","set(uint256)":"60fe47b1","storedData()":"2a1afcd9"},"abiDefinition":[{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"payable":false,"type":"constructor"}]}; + + expectedObject["Token"] = {"code":"6060604052341561000c57fe5b6040516020806104be83398101604052515b600160a060020a033316600090815260208190526040902081905560028190555b505b61046e806100506000396000f300606060405236156100755763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007757806318160ddd146100aa57806323b872dd146100cc57806370a0823114610105578063a9059cbb14610133578063dd62ed3e14610166575bfe5b341561007f57fe5b610096600160a060020a036004351660243561019a565b604080519115158252519081900360200190f35b34156100b257fe5b6100ba610205565b60408051918252519081900360200190f35b34156100d457fe5b610096600160a060020a036004358116906024351660443561020c565b604080519115158252519081900360200190f35b341561010d57fe5b6100ba600160a060020a0360043516610323565b60408051918252519081900360200190f35b341561013b57fe5b610096600160a060020a0360043516602435610342565b604080519115158252519081900360200190f35b341561016e57fe5b6100ba600160a060020a0360043581169060243516610407565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a038316600090815260208190526040812054829010156102335760006000fd5b600160a060020a0380851660009081526001602090815260408083203390941683529290522054829010156102685760006000fd5b600160a060020a03831660009081526020819052604090205461028b9083610434565b15156102975760006000fd5b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a033316600090815260208190526040812054829010156103695760006000fd5b600160a060020a03831660009081526020819052604090205461038c9083610434565b15156103985760006000fd5b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a72305820f1e53450d0d5723ba8f341856e7786c43607109cbb25ff3975ce9ccd042506b30029","realRuntimeBytecode": "606060405236156100755763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007757806318160ddd146100aa57806323b872dd146100cc57806370a0823114610105578063a9059cbb14610133578063dd62ed3e14610166575bfe5b341561007f57fe5b610096600160a060020a036004351660243561019a565b604080519115158252519081900360200190f35b34156100b257fe5b6100ba610205565b60408051918252519081900360200190f35b34156100d457fe5b610096600160a060020a036004358116906024351660443561020c565b604080519115158252519081900360200190f35b341561010d57fe5b6100ba600160a060020a0360043516610323565b60408051918252519081900360200190f35b341561013b57fe5b610096600160a060020a0360043516602435610342565b604080519115158252519081900360200190f35b341561016e57fe5b6100ba600160a060020a0360043581169060243516610407565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a038316600090815260208190526040812054829010156102335760006000fd5b600160a060020a0380851660009081526001602090815260408083203390941683529290522054829010156102685760006000fd5b600160a060020a03831660009081526020819052604090205461028b9083610434565b15156102975760006000fd5b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a033316600090815260208190526040812054829010156103695760006000fd5b600160a060020a03831660009081526020819052604090205461038c9083610434565b15156103985760006000fd5b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a72305820","runtimeBytecode":"606060405236156100755763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007757806318160ddd146100aa57806323b872dd146100cc57806370a0823114610105578063a9059cbb14610133578063dd62ed3e14610166575bfe5b341561007f57fe5b610096600160a060020a036004351660243561019a565b604080519115158252519081900360200190f35b34156100b257fe5b6100ba610205565b60408051918252519081900360200190f35b34156100d457fe5b610096600160a060020a036004358116906024351660443561020c565b604080519115158252519081900360200190f35b341561010d57fe5b6100ba600160a060020a0360043516610323565b60408051918252519081900360200190f35b341561013b57fe5b610096600160a060020a0360043516602435610342565b604080519115158252519081900360200190f35b341561016e57fe5b6100ba600160a060020a0360043581169060243516610407565b60408051918252519081900360200190f35b600160a060020a03338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6002545b90565b600160a060020a038316600090815260208190526040812054829010156102335760006000fd5b600160a060020a0380851660009081526001602090815260408083203390941683529290522054829010156102685760006000fd5b600160a060020a03831660009081526020819052604090205461028b9083610434565b15156102975760006000fd5b600160a060020a038085166000818152600160209081526040808320338616845282528083208054889003905583835282825280832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9392505050565b600160a060020a0381166000908152602081905260409020545b919050565b600160a060020a033316600090815260208190526040812054829010156103695760006000fd5b600160a060020a03831660009081526020819052604090205461038c9083610434565b15156103985760006000fd5b600160a060020a0333811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b92915050565b600160a060020a038083166000908152600160209081526040808320938516835292905220545b92915050565b808201829010155b929150505600a165627a7a72305820f1e53450d0d5723ba8f341856e7786c43607109cbb25ff3975ce9ccd042506b30029","swarmHash": "f1e53450d0d5723ba8f341856e7786c43607109cbb25ff3975ce9ccd042506b3","gasEstimates":{"creation":[40472,226800],"external":{"allowance(address,address)":809,"approve(address,uint256)":22334,"balanceOf(address)":673,"totalSupply()":396,"transfer(address,uint256)":43635,"transferFrom(address,address,uint256)":64415},"internal":{"safeToAdd(uint256,uint256)":41}},"functionHashes":{"allowance(address,address)":"dd62ed3e","approve(address,uint256)":"095ea7b3","balanceOf(address)":"70a08231","totalSupply()":"18160ddd","transfer(address,uint256)":"a9059cbb","transferFrom(address,address,uint256)":"23b872dd"},"abiDefinition":[{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"supply","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"value","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"_allowance","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initial_balance","type":"uint256"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]} it('should generate compiled code and abi', function(done) { compiler.compile_solidity([ diff --git a/test/config.js b/test/config.js index 669455206..cffc1f63f 100644 --- a/test/config.js +++ b/test/config.js @@ -1,11 +1,10 @@ /*globals describe, it*/ -var Config = require('../lib/core/config.js'); -var Plugins = require('../lib/core/plugins.js'); -var assert = require('assert'); -var fs = require('fs'); +let Config = require('../lib/core/config.js'); +let Plugins = require('../lib/core/plugins.js'); +let assert = require('assert'); describe('embark.Config', function() { - var config = new Config({ + let config = new Config({ env: 'myenv', configDir: './test/test1/config/' }); @@ -14,7 +13,7 @@ describe('embark.Config', function() { describe('#loadBlockchainConfigFile', function() { it('should load blockchain config correctly', function() { config.loadBlockchainConfigFile(); - var expectedConfig = { + let expectedConfig = { "enabled": true, "networkType": "custom", "genesisBlock": "config/development/genesis.json", @@ -36,7 +35,7 @@ describe('embark.Config', function() { describe('#loadContractsConfigFile', function() { it('should load contract config correctly', function() { config.loadContractsConfigFile(); - var expectedConfig = { + let expectedConfig = { "gas": "auto", "contracts": { "SimpleStorage": { diff --git a/test/console.js b/test/console.js index a1d987e12..71b6998e5 100644 --- a/test/console.js +++ b/test/console.js @@ -1,11 +1,12 @@ /*globals describe, it*/ -var Console = require('../lib/dashboard/console.js'); -var Plugins = require('../lib/core/plugins.js'); -var assert = require('assert'); +let Console = require('../lib/dashboard/console.js'); +let Plugins = require('../lib/core/plugins.js'); +let assert = require('assert'); +let version = require('../package.json').version; describe('embark.Console', function() { - var plugins = new Plugins({plugins: {}}); - var console = new Console({plugins: plugins, version: '2.3.1'}); + let plugins = new Plugins({plugins: {}}); + let console = new Console({plugins: plugins, version: version}); describe('#executeCmd', function() { @@ -13,8 +14,8 @@ describe('embark.Console', function() { it('it should provide a help text', function(done) { console.executeCmd('help', function(output) { - var lines = output.split('\n'); - assert.equal(lines[0], 'Welcome to Embark 2.3.1'); + let lines = output.split('\n'); + assert.equal(lines[0], 'Welcome to Embark ' + version); assert.equal(lines[2], 'possible commands are:'); done(); }); diff --git a/test/contracts.js b/test/contracts.js index 873383a19..211eabe51 100644 --- a/test/contracts.js +++ b/test/contracts.js @@ -1,16 +1,17 @@ /*globals describe, it*/ -var ContractsManager = require('../lib/contracts/contracts.js'); -var Logger = require('../lib/core/logger.js'); -var assert = require('assert'); -var fs = require('fs'); +let ContractsManager = require('../lib/contracts/contracts.js'); +let Logger = require('../lib/core/logger.js'); +let assert = require('assert'); +let fs = require('fs'); -var readFile = function(file) { +let readFile = function(file) { return {filename: file, content: fs.readFileSync(file).toString()}; }; describe('embark.Contratcs', function() { + this.timeout(0); describe('simple', function() { - var contractsManager = new ContractsManager({ + let contractsManager = new ContractsManager({ contractFiles: [ readFile('test/contracts/simple_storage.sol'), readFile('test/contracts/token.sol') @@ -40,7 +41,7 @@ describe('embark.Contratcs', function() { throw err; } - var contracts = contractsManager.listContracts(); + let contracts = contractsManager.listContracts(); assert.equal(contracts.length, 2); assert.equal(contracts[0].deploy, true); @@ -69,7 +70,7 @@ describe('embark.Contratcs', function() { }); describe('config with contract instances', function() { - var contractsManager = new ContractsManager({ + let contractsManager = new ContractsManager({ contractFiles: [ readFile('test/contracts/simple_storage.sol'), readFile('test/contracts/token_storage.sol') @@ -109,7 +110,7 @@ describe('embark.Contratcs', function() { throw err; } - var contracts = contractsManager.listContracts(); + let contracts = contractsManager.listContracts(); assert.equal(contracts.length, 4); assert.equal(contracts[0].className, "MySimpleStorage"); @@ -126,7 +127,7 @@ describe('embark.Contratcs', function() { //assert.equal(contracts[3].code, ''); //assert.equal(contracts[3].runtimeBytecode, ''); - var parentContract = contracts[2]; + let parentContract = contracts[2]; //MySimpleStorage assert.equal(contracts[0].deploy, true); diff --git a/test_app/app/contracts/simple_storage.sol b/test_app/app/contracts/simple_storage.sol index 7ca17d62c..1035d12d8 100644 --- a/test_app/app/contracts/simple_storage.sol +++ b/test_app/app/contracts/simple_storage.sol @@ -2,6 +2,8 @@ pragma solidity ^0.4.7; contract SimpleStorage { uint public storedData; + function() payable { } + function SimpleStorage(uint initialValue) { storedData = initialValue; } diff --git a/test_app/app/css/main.css b/test_app/app/css/main.css index abfcd67f5..bdec3790f 100644 --- a/test_app/app/css/main.css +++ b/test_app/app/css/main.css @@ -24,3 +24,27 @@ div { margin-bottom: 0; } +.status-offline { + vertical-align: middle; + margin-left: 5px; + margin-top: 4px; + width: 12px; + height: 12px; + background: red; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; +} + +.status-online { + vertical-align: middle; + margin-left: 5px; + margin-top: 4px; + width: 12px; + height: 12px; + background: mediumseagreen; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; +} + diff --git a/test_app/app/index.html b/test_app/app/index.html index 417f60960..10b27f231 100644 --- a/test_app/app/index.html +++ b/test_app/app/index.html @@ -9,8 +9,8 @@
@@ -37,7 +37,8 @@
- note: You need to have an IPFS node running + +

Save text to IPFS

@@ -72,11 +73,12 @@

EmbarkJS.Storage.setProvider('ipfs',{server: 'localhost', port: '5001'})
- +
- The node you are using does not support whisper - + + +

Listen To channel

@@ -97,7 +99,7 @@

EmbarkJS.Messages.setProvider('whisper')
- +
diff --git a/test_app/app/js/index.js b/test_app/app/js/index.js index 27416e49c..72e335b38 100644 --- a/test_app/app/js/index.js +++ b/test_app/app/js/index.js @@ -28,23 +28,59 @@ $(document).ready(function() { // 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.setProvider('ipfs') + .then(function(){ + console.log('Provider set to IPFS'); + 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(); + } + }); + }) + .catch(function(err){ + console.log('Failed to set IPFS as Provider:', 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); + } }); - addToLog("#storage", "EmbarkJS.Storage.saveText('" + value + "').then(function(hash) { })"); }); $("#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); + } }); - addToLog("#storage", "EmbarkJS.Storage.get('" + value + "').then(function(content) { })"); }); $("#storage button.uploadFile").click(function() { @@ -52,8 +88,13 @@ $(document).ready(function() { 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); + } }); - addToLog("#storage", "EmbarkJS.Storage.uploadFile($('input[type=file]')).then(function(hash) { })"); }); $("#storage button.loadIpfsFile").click(function() { @@ -73,13 +114,21 @@ $(document).ready(function() { $(document).ready(function() { $("#communication .error").hide(); - //web3.version.getWhisper(function(err, res) { - // if (err) { - // $("#communication .error").show(); - // } else { - // EmbarkJS.Messages.setProvider('whisper'); - // } - //}); + $("#communication .errorVersion").hide(); + web3.version.getWhisper(function(err, version) { + if (err) { + $("#communication .error").show(); + $("#communication-controls").hide(); + $("#status-communication").addClass('status-offline'); + } else if (version >= 5) { + $("#communication .errorVersion").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(); @@ -98,4 +147,3 @@ $(document).ready(function() { }); }); - diff --git a/test_app/config/development/genesis.json b/test_app/config/development/genesis.json index 9f2d15625..8df404021 100644 --- a/test_app/config/development/genesis.json +++ b/test_app/config/development/genesis.json @@ -1,4 +1,5 @@ { + "config": {}, "nonce": "0x0000000000000042", "difficulty": "0x0", "alloc": { diff --git a/test_app/embark.json b/test_app/embark.json index 5cdaae975..0dd46dd19 100644 --- a/test_app/embark.json +++ b/test_app/embark.json @@ -14,6 +14,7 @@ "buildDir": "dist/", "config": "config/", "plugins": { - "embark-babel": {"files": ["**/*.js", "**/*.jsx", "!**/_vendor/*.js"]} + "embark-babel": {"files": ["**/*.js", "**/*.jsx", "!**/_vendor/*.js"]}, + "embark-service": {} } } diff --git a/test_app/extensions/embark-service/index.js b/test_app/extensions/embark-service/index.js new file mode 100644 index 000000000..1108dacb5 --- /dev/null +++ b/test_app/extensions/embark-service/index.js @@ -0,0 +1,5 @@ +module.exports = function(embark) { + embark.registerServiceCheck('PluginService', function(cb) { + cb({name: "ServiceName", status: "on"}); + }); +}; diff --git a/test_app/extensions/embark-service/package.json b/test_app/extensions/embark-service/package.json new file mode 100644 index 000000000..b20033a19 --- /dev/null +++ b/test_app/extensions/embark-service/package.json @@ -0,0 +1,11 @@ +{ + "name": "embark-service", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/test_app/package.json b/test_app/package.json index 29cd34ffa..d1ea282a1 100644 --- a/test_app/package.json +++ b/test_app/package.json @@ -10,11 +10,12 @@ "license": "ISC", "homepage": "", "devDependencies": { - "embark": "../", + "embark": "file:../", "mocha": "^2.2.5" }, "dependencies": { "embark-babel": "^1.0.0", + "embark-service": "./extensions/embark-service", "ethereumjs-testrpc": "^3.0.3" } } diff --git a/test_app/test/another_storage_spec.js b/test_app/test/another_storage_spec.js index e06e11bbd..d0e90c60d 100644 --- a/test_app/test/another_storage_spec.js +++ b/test_app/test/another_storage_spec.js @@ -5,6 +5,7 @@ var web3 = EmbarkSpec.web3; describe("AnotherStorage", function() { before(function(done) { + this.timeout(0); var contractsConfig = { "SimpleStorage": { args: [100] diff --git a/test_app/test/simple_storage_spec.js b/test_app/test/simple_storage_spec.js index 83ad1610f..b2d3cb178 100644 --- a/test_app/test/simple_storage_spec.js +++ b/test_app/test/simple_storage_spec.js @@ -5,6 +5,7 @@ var web3 = EmbarkSpec.web3; describe("SimpleStorage", function() { before(function(done) { + this.timeout(0); var contractsConfig = { "SimpleStorage": { args: [100] diff --git a/test_app/test/token_spec.js b/test_app/test/token_spec.js index 3ed82f67b..362ce058b 100644 --- a/test_app/test/token_spec.js +++ b/test_app/test/token_spec.js @@ -5,6 +5,7 @@ var web3 = EmbarkSpec.web3; describe("Token", function() { before(function(done) { + this.timeout(0); var contractsConfig = { "SimpleStorage": { args: [100]