├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin ├── dbServer ├── nether └── settings.json ├── defaults.json ├── index.js ├── lib ├── account.js ├── ethPeerManagner.js ├── index.js ├── manifest.json ├── mine.js ├── networking.js ├── processTxs.js ├── rpc │ ├── eth.js │ ├── index.js │ ├── net.js │ └── server │ │ ├── http.js │ │ └── ws.js ├── syncManager.js └── upnp.js ├── package.json ├── script ├── eslint.json └── eslint.sh └── test ├── app.js ├── rpc.js └── settings.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.seed 2 | *.log 3 | *.csv 4 | *.dat 5 | *.out 6 | *.pid 7 | *.gz 8 | 9 | db 10 | 11 | pids 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | test/testdb 18 | 19 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.12" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-ethereum [![Build Status](https://travis-ci.org/ethereum/node-ethereum.svg)](https://travis-ci.org/ethereum/node-ethereum) 2 | =============== 3 | [DEPRECATED] 4 | this is being broken into micro-services, Like 5 | - [node blockchain server](https://github.com/ethereum/node-blockchain-server) 6 | 7 | Install 8 | === 9 | `git clone https://github.com/ethereum/node-ethereum` 10 | `cd ./node-ethereum` 11 | `npm install .` 12 | 13 | Run 14 | === 15 | `./bin/neth` 16 | 17 | Embed 18 | === 19 | ```javacsript 20 | App = require('../') 21 | app = new App(); 22 | app.start(function(){ 23 | console.log("Ethereum has started"); 24 | }); 25 | ``` 26 | -------------------------------------------------------------------------------- /bin/dbServer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var argv = require('minimist')(process.argv.slice(2)); 3 | var multilevel = require('multilevel'); 4 | var net = require('net'); 5 | var level = require('level'); 6 | var Sublevel = require('level-sublevel'); 7 | var enableDestroy = require('server-destroy'); 8 | 9 | if (argv.help) { 10 | var help = 'Usage [] \n' 11 | + 'Start the DB server for node-Ethereum \n' 12 | + ' The path to the DB \n' 13 | + ' The port that the DB server listening to \n'; 14 | 15 | console.log(help); 16 | process.exit(); 17 | } 18 | 19 | 20 | if(!argv._[0]){ 21 | console.log(' is required. usage `dbServer 838383 ./myDB`'); 22 | process.exit(); 23 | }else if(!argv._[1]){ 24 | console.log(' is required'); 25 | process.exit(); 26 | } 27 | 28 | var port = argv._[1]; 29 | var path = argv._[0]; 30 | console.log(path); 31 | var db = Sublevel(level(path)); 32 | var isShutingDown = false; 33 | 34 | db.sublevel('state'); 35 | db.sublevel('blocks'); 36 | db.sublevel('details'); 37 | db.sublevel('peers'); 38 | 39 | var server = net.createServer(function (con) { 40 | con.pipe(multilevel.server(db)).pipe(con); 41 | }); 42 | 43 | enableDestroy(server); 44 | 45 | server.listen(port, function () { 46 | process.once('SIGTERM', shutdown); 47 | process.once('SIGINT', shutdown); 48 | console.log('dbServer - started: /:' + port ); 49 | if(process.send){ 50 | process.send('started'); 51 | } 52 | process.on('disconnect', shutdown); 53 | }); 54 | 55 | function shutdown (){ 56 | if(!isShutingDown){ 57 | isShutingDown = true; 58 | server.destroy(function(){ 59 | console.log('dbServer - closed'); 60 | process.exit(); 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /bin/nether: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var log = require('npmlog'), 4 | App = require('../'); 5 | 6 | var path = process.env['HOME'] + '/.ethereum/node'; 7 | 8 | //try to load settings from home dir. or use the the standlone default settings 9 | try { 10 | var settings = require(path + '/settings.json'); 11 | } catch (e) { 12 | var settings = require('./settings.json'); 13 | } 14 | 15 | var app = new App(settings), 16 | argv = require('minimist')(process.argv.slice(2)); 17 | 18 | if (argv.help) { 19 | console.log('TODO: write help'); 20 | process.exit(); 21 | } 22 | 23 | app.start(function (err) { 24 | if (err) log.error('start', err); 25 | process.on('SIGTERM', shutdown); 26 | process.on('SIGINT', shutdown); 27 | 28 | if(argv.address){ 29 | const port = argv.port ? argv.port : 30303; 30 | app.network.connect(argv.address, port); 31 | } 32 | 33 | }); 34 | 35 | function shutdown() { 36 | app.log.info('app', 'shutting down'); 37 | app.stop(function () { 38 | app.log.info('app', 'bye'); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /bin/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "logging": 3, 3 | "network": { 4 | "port": 30301, 5 | "host": "0.0.0.0" 6 | }, 7 | "dbServer": true, 8 | "db": { 9 | "port": 30304 10 | }, 11 | "upnp": false, 12 | "rpc": { 13 | "ws": false, 14 | "xhr": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "logging": 3, 3 | "network": { 4 | "port": 30303, 5 | "host": "0.0.0.0" 6 | }, 7 | "db": { 8 | "port": 30304 9 | }, 10 | "dbServer": true, 11 | "webui":false, 12 | "upnp": false, 13 | "rpc": false 14 | } 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/'); 2 | -------------------------------------------------------------------------------- /lib/account.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | 3 | /** 4 | * @constructor 5 | */ 6 | var Account = module.exports = function (name, raw, db) { 7 | this.nonce = raw[0]; 8 | this.pks = raw[1]; 9 | this.addressLabels = raw[2]; 10 | this.txLabels = raw[3]; 11 | this.name = name; 12 | this.db = db; 13 | }; 14 | 15 | Account.prototype.recreateKeys = function () { 16 | for (var i = 0; i < this.nonce; i++) { 17 | var key = crypto.pbkdf2Sync(this.pks[0], i, 1000, 16); 18 | this.pks.push(key); 19 | } 20 | }; 21 | 22 | Account.prototype.addAddressLabel = function(account, label, cb){ 23 | 24 | }; 25 | 26 | Account.prototype.addTxLabel = function(tx, label, cb){ 27 | 28 | }; 29 | 30 | Account.prototype.serialize = function(){ 31 | 32 | }; 33 | 34 | module.exports.start = function() { 35 | 36 | } 37 | 38 | module.exports.stop = function() { 39 | 40 | } 41 | -------------------------------------------------------------------------------- /lib/ethPeerManagner.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const EventEmitter = require('events').EventEmitter; 3 | const Transaction = require('ethereumjs-lib').Transaction; 4 | const Block = require('ethereumjs-lib').Block; 5 | const utils = require('ethereumjs-util'); 6 | const rlp = require('rlp'); 7 | 8 | const Version = 54; 9 | const OFFSET = 0x10; 10 | const TYPES = { 11 | 0x0: 'status', 12 | 0x1: 'getTransactions', 13 | 0x2: 'transactions', 14 | 0x3: 'getBlockHashes', 15 | 0x4: 'blockHashes', 16 | 0x5: 'getBlocks', 17 | 0x6: 'blocks', 18 | 0x7: 'newBlock' 19 | }; 20 | 21 | const OFFSETS = { 22 | 'status': 0x0, 23 | 'getTransactions': 0x1, 24 | 'transactions': 0x2, 25 | 'getBlockHashes': 0x3, 26 | 'blockHashes': 0x4, 27 | 'getBlocks': 0x5, 28 | 'blocks': 0x6, 29 | 'newBlock': 0x7 30 | }; 31 | 32 | var Manager = module.exports = function(stream) { 33 | // Register as event emitter 34 | EventEmitter.call(this); 35 | var self = this; 36 | this.version = 60; 37 | this.stream = stream; 38 | this.stream._prefix = false; 39 | stream.on('data', function(data){ 40 | self.parse(data); 41 | }); 42 | 43 | stream.on('end', function(){ 44 | self._end = true; 45 | }); 46 | 47 | stream.on('error', function(){ 48 | self._end = true; 49 | }); 50 | }; 51 | 52 | util.inherits(Manager, EventEmitter); 53 | 54 | //parses an array of transactions 55 | function parseTxs(payload) { 56 | var txs = []; 57 | for (var i = 0; i < payload.length; i++) { 58 | txs.push(new Transaction(payload[i])); 59 | } 60 | return txs; 61 | } 62 | 63 | function parseBlocks(payload) { 64 | //blocks 65 | var blocks = []; 66 | for (var i = 0; i < payload.length; i++) { 67 | blocks.push(new Block(payload[i])); 68 | } 69 | return blocks; 70 | } 71 | 72 | 73 | //packet parsing methods 74 | var parsingFunc = { 75 | status: function(payload) { 76 | return { 77 | ethVersion: payload[0][0], 78 | networkID: payload[1], 79 | td: payload[2], 80 | bestHash: payload[3], 81 | genesisHash: payload[4] 82 | }; 83 | }, 84 | transactions: function(payload) { 85 | return parseTxs(payload); 86 | }, 87 | getBlockHashes: function(payload) { 88 | return { 89 | hash: payload[0], 90 | maxBlocks: payload[1] 91 | }; 92 | }, 93 | blockHashes: function(payload) { 94 | return payload; 95 | }, 96 | getBlocks: function(payload) { 97 | return payload; 98 | }, 99 | blocks: function(payload) { 100 | return parseBlocks(payload); 101 | }, 102 | newBlock: function(payload) { 103 | return { 104 | block: new Block(payload[0]), 105 | td: payload[1] 106 | }; 107 | } 108 | }; 109 | 110 | Manager.prototype.parse = function(data) { 111 | var type = TYPES[data.slice(0, 1)[0] - OFFSET]; 112 | //try{ 113 | var parsed = parsingFunc[type](rlp.decode(data.slice(1))) 114 | if(type === 'status'){ 115 | this.status = parsed; 116 | } 117 | 118 | this.emit(type, parsed); 119 | //}catch(e){ 120 | // this.emit('error', e); 121 | //} 122 | }; 123 | 124 | Manager.prototype.send = function(type, data, cb){ 125 | var msg = Buffer.concat([new Buffer([type]), rlp.encode(data)]) 126 | console.log('sending!'); 127 | console.log(msg.toString('hex')); 128 | this.stream.write(msg, cb); 129 | }; 130 | 131 | //packet sending methods 132 | Manager.prototype.sendStatus = function(id, td, bestHash, genesisHash, cb) { 133 | var msg = [ 134 | new Buffer([this.version]), 135 | id, 136 | td, 137 | bestHash, 138 | genesisHash 139 | ]; 140 | 141 | this.send(OFFSETS.status, msg, cb); 142 | }; 143 | 144 | /** 145 | * Specify (a) transaction(s) that the peer should make sure is included on its 146 | * transaction queue. 147 | * @method sendTransactions 148 | * @param {Array.} transaction 149 | * @param {Function} cb 150 | */ 151 | Manager.prototype.sendTransactions = function(transactions, cb) { 152 | var msg = []; 153 | transactions.forEach(function(tx) { 154 | msg.push(tx.serialize()); 155 | }); 156 | this.send(OFFSETS.transactions, msg, cb); 157 | }; 158 | 159 | Manager.prototype.sendGetBlockHashes = function(startHash, max, cb) { 160 | var msg = [startHash, utils.intToBuffer(max)]; 161 | this.send(OFFSETS.getBlockHashes, msg, cb); 162 | }; 163 | 164 | Manager.prototype.sendBlockHashes = function(hashes, cb) { 165 | this.send(OFFSETS.blockHashes, cb) 166 | }; 167 | 168 | Manager.prototype.sendGetBlocks = function(hashes, cb) { 169 | hashes = hashes.slice(); 170 | this.send(OFFSETS.getBlocks, hashes, cb); 171 | }; 172 | 173 | /** 174 | * Specify (a) block(s) that the peer should know about. 175 | * @method sendBlocks 176 | * @param {Array.} blocks 177 | * @param {Function} cb 178 | */ 179 | Manager.prototype.sendBlocks = function(blocks) { 180 | var msg = []; 181 | 182 | blocks.forEach(function(block) { 183 | msg.push(block.serialize()); 184 | }); 185 | 186 | return msg; 187 | }; 188 | 189 | /** 190 | * Specify (a) block(s) that the peer should know about. 191 | * @method sendBlocks 192 | * @param {Array.} block 193 | * @param {Number} td tottal difficulty 194 | * @param {Function} cb 195 | */ 196 | Manager.prototype.sendNewBlock = function(block, td) { 197 | var msg = [block.serialize(false), td]; 198 | return msg; 199 | }; 200 | 201 | Manager.prototype.fetchBlockHashes = function(startHash, max, cb) { 202 | this.once('blockHashes', cb); 203 | this.sendGetBlockHashes(startHash, max); 204 | }; 205 | 206 | Manager.prototype.fetchBlocks = function(hashes, cb) { 207 | this.once('blocks', cb); 208 | this.sendGetBlocks(hashes); 209 | }; 210 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const cp = require('child_process') 3 | const path = require('path') 4 | const crypto = require('crypto') 5 | const net = require('net') 6 | const Ethereum = require('ethereumjs-lib') 7 | const manifest = require('./manifest.json') 8 | const multilevel = require('multilevel') 9 | const LevelWriteStream = require('level-writestream') 10 | const log = require('npmlog') 11 | const async = require('async') 12 | const mkdirp = require('mkdirp') 13 | const defaults = require('../defaults.json') 14 | const upnp = require('./upnp.js') 15 | const mining = require('./mine.js') 16 | const EthRpc = require('./rpc/index.js') 17 | const HttpRpc = require('./rpc/server/http.js') 18 | const WsRpc = require('./rpc/server/ws.js') 19 | var genesisAllotments = require('ethereum-common').allotments 20 | const networking = require('./networking.js') 21 | 22 | var dbConnection 23 | var dbServer 24 | 25 | //a no-op 26 | function noop(done) { 27 | done() 28 | } 29 | 30 | /** 31 | * @constructor 32 | */ 33 | var App = module.exports = function(settings) { 34 | 35 | this.settings = settings || {} 36 | this.plugins = {} 37 | this.log = log 38 | 39 | //a queue of transaction that have yet to be inculded in the blockchain 40 | this.pendingTxs = [] 41 | this.isSyncing = false 42 | 43 | //set the default path for the config and database files 44 | defaults.path = process.env.HOME + '/.ethereum/node' 45 | 46 | //add the defaults 47 | for (var prop in defaults) { 48 | if (this.settings[prop] === void 0) this.settings[prop] = defaults[prop] 49 | } 50 | } 51 | 52 | //attach mining functions 53 | App.prototype.startMining = mining.start 54 | App.prototype.stopMining = mining.stop 55 | App.prototype.toggleMining = mining.toggle 56 | 57 | /** 58 | * Starts the client 59 | * @method start 60 | * @param {Function} cb a callback 61 | */ 62 | App.prototype.start = function(cb) { 63 | 64 | var self = this 65 | 66 | /** 67 | * Checks the for the db folder and creates a new folder if it doesn't exist 68 | * @method checkPath" 69 | * @param {Function} done 70 | * @private 71 | */ 72 | function checkPath(done) { 73 | fs.exists(self.settings.path, function(exists) { 74 | if (exists) { 75 | done() 76 | } else { 77 | mkdirp(self.settings.path, done) 78 | } 79 | }) 80 | } 81 | 82 | function setup(done) { 83 | //open DBs 84 | var db = multilevel.client(manifest) 85 | dbConnection = net.connect(self.settings.db.port) 86 | dbConnection.pipe(db.createRpcStream()).pipe(dbConnection) 87 | 88 | var stateDB = self.stateDB = LevelWriteStream(db.sublevel('state')) 89 | var blockDB = self.blockDB = db.sublevel('blocks') 90 | var detailsDB = self.detailsDB = db.sublevel('details') 91 | var peersDB = self.peersDB = db.sublevel('peers') 92 | 93 | //create the blockchain 94 | self.blockchain = new Ethereum.Blockchain(blockDB, detailsDB) 95 | //create a VM 96 | self.vm = new Ethereum.VM(stateDB, self.blockchain) 97 | 98 | //account manager doesn't do anything yet 99 | // self.accountMan = new AccountMan(detailsDB) 100 | self.rpc = new EthRpc(self) 101 | done() 102 | } 103 | 104 | //generates the genesis hash if needed 105 | function genesis(done) { 106 | self.blockchain.getHead(function(err, head){ 107 | console.log('get header!') 108 | 109 | if (!head) { 110 | //generate new genesis block 111 | if(self.settings.allotments) genesisAllotments = self.settings.allotments 112 | log.info('state', 'using provided genesisAllotments.') 113 | 114 | self.vm.generateGenesis(genesisAllotments, function() { 115 | var block = new Ethereum.Block() 116 | block.header.stateRoot = self.vm.trie.root 117 | log.info('state', 'root: ' + self.vm.trie.root.toString('hex')) 118 | log.info('state', 'genesis hash:' + block.hash().toString('hex')) 119 | log.info('rlp', block.serialize().toString('hex')) 120 | self.blockchain.addBlock(block, done) 121 | }) 122 | } else { 123 | self.vm.trie.root = head.header.stateRoot 124 | log.info('state', 'starting with state root of: ' + head.header.stateRoot.toString('hex') + 125 | ' height:' + head.header.number.toString('hex')) 126 | 127 | done() 128 | } 129 | }) 130 | } 131 | 132 | //get the unquie id of the client. If there isn't one then generate one 133 | function getId(done) { 134 | self.detailsDB.get('id', function(err, id) { 135 | if (!id) { 136 | var hash = crypto.createHash('sha512') 137 | hash.update((Math.random()) 138 | .toString()) 139 | 140 | id = hash.digest('hex') 141 | 142 | self.detailsDB.put('id', id, function(err) { 143 | done(err, id) 144 | }) 145 | 146 | } else { 147 | done(err, id) 148 | } 149 | }) 150 | } 151 | 152 | // XHR RPC Server 153 | function startHttpRpcServer(done) { 154 | self.xhrRpc = new HttpRpc(self.rpc) 155 | self.xhrRpc.start(self.settings.rpc.xhr, done) 156 | } 157 | 158 | // WS RPC Server 159 | function startWsRpcServer(done) { 160 | self.wsRpc = new WsRpc(self.rpc) 161 | self.wsRpc.start(self.settings.rpc.ws, done) 162 | } 163 | 164 | var tasks = { 165 | checkPath: checkPath, 166 | startDB: noop, 167 | setup: ['checkPath', 'startDB', setup], 168 | genesis: ['setup', genesis], 169 | ip: noop, 170 | upnp: noop, 171 | rcp: noop, 172 | id: ['setup', getId], 173 | //accountMan: ['setup', initAccountMan], 174 | network: noop, 175 | } 176 | 177 | if (this.settings.network) { 178 | tasks.network = ['ip', 'upnp', 'id', networking.bind(self)] 179 | 180 | if (this.settings.upnp) { 181 | tasks.ip = upnp.extrenalIp 182 | tasks.upnp = async.apply(upnp.map, self.settings.network.port) 183 | } 184 | } 185 | 186 | if (this.settings.rpc) { 187 | if(this.settings.rpc.xhr) tasks.startHttpRpcServer = ['setup', startHttpRpcServer] 188 | if(this.settings.rpc.ws) tasks.startWsRpcServer = ['setup', startWsRpcServer] 189 | } 190 | 191 | //start the db server 192 | if(this.settings.dbServer){ 193 | tasks.startDB = function startDBserver(done){ 194 | dbServer = cp.fork(path.join( __dirname, '/../bin/dbServer'), [ self.settings.path, self.settings.db.port], {execArgv: []} ) 195 | dbServer.on('message', function(){ 196 | done() 197 | }) 198 | } 199 | } 200 | 201 | //run everything 202 | async.auto(tasks, cb) 203 | } 204 | 205 | /** 206 | * Stops everything every 207 | * @method stop 208 | * @param {Function} cb calls this callback when everything is done 209 | */ 210 | App.prototype.stop = function(cb) { 211 | 212 | var self = this 213 | var tasks = [upnp.unmap] 214 | 215 | if(this.settings.network){ 216 | tasks.push(self.network.close.bind(self.network)) 217 | } 218 | 219 | dbConnection.end() 220 | 221 | if (this.settings.rpc) { 222 | if(this.settings.rpc.ws) tasks.push(self.wsRpc.stop.bind(self.wsRpc)) 223 | if(this.settings.rpc.xhr) tasks.push(self.xhrRpc.stop.bind(self.xhrRpc)) 224 | } 225 | 226 | async.parallel(tasks, cb) 227 | } 228 | -------------------------------------------------------------------------------- /lib/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "methods": { 3 | "createReadStream": { 4 | "type": "readable" 5 | }, 6 | "readStream": { 7 | "type": "readable" 8 | }, 9 | "createValueStream": { 10 | "type": "readable" 11 | }, 12 | "valueStream": { 13 | "type": "readable" 14 | }, 15 | "createKeyStream": { 16 | "type": "readable" 17 | }, 18 | "keyStream": { 19 | "type": "readable" 20 | }, 21 | "createWriteStream": { 22 | "type": "writable" 23 | }, 24 | "writeStream": { 25 | "type": "writable" 26 | }, 27 | "isOpen": { 28 | "type": "sync" 29 | }, 30 | "isClosed": { 31 | "type": "sync" 32 | }, 33 | "put": { 34 | "type": "async" 35 | }, 36 | "get": { 37 | "type": "async" 38 | }, 39 | "del": { 40 | "type": "async" 41 | }, 42 | "batch": { 43 | "type": "async" 44 | }, 45 | "approximateSize": { 46 | "type": "async" 47 | } 48 | }, 49 | "sublevels": { 50 | "state": { 51 | "methods": { 52 | "createReadStream": { 53 | "type": "readable" 54 | }, 55 | "readStream": { 56 | "type": "readable" 57 | }, 58 | "createValueStream": { 59 | "type": "readable" 60 | }, 61 | "valueStream": { 62 | "type": "readable" 63 | }, 64 | "createKeyStream": { 65 | "type": "readable" 66 | }, 67 | "keyStream": { 68 | "type": "readable" 69 | }, 70 | "createWriteStream": { 71 | "type": "writable" 72 | }, 73 | "writeStream": { 74 | "type": "writable" 75 | }, 76 | "isOpen": { 77 | "type": "sync" 78 | }, 79 | "isClosed": { 80 | "type": "sync" 81 | }, 82 | "put": { 83 | "type": "async" 84 | }, 85 | "get": { 86 | "type": "async" 87 | }, 88 | "del": { 89 | "type": "async" 90 | }, 91 | "batch": { 92 | "type": "async" 93 | }, 94 | "approximateSize": { 95 | "type": "async" 96 | } 97 | }, 98 | "sublevels": {} 99 | }, 100 | "blocks": { 101 | "methods": { 102 | "createReadStream": { 103 | "type": "readable" 104 | }, 105 | "readStream": { 106 | "type": "readable" 107 | }, 108 | "createValueStream": { 109 | "type": "readable" 110 | }, 111 | "valueStream": { 112 | "type": "readable" 113 | }, 114 | "createKeyStream": { 115 | "type": "readable" 116 | }, 117 | "keyStream": { 118 | "type": "readable" 119 | }, 120 | "createWriteStream": { 121 | "type": "writable" 122 | }, 123 | "writeStream": { 124 | "type": "writable" 125 | }, 126 | "isOpen": { 127 | "type": "sync" 128 | }, 129 | "isClosed": { 130 | "type": "sync" 131 | }, 132 | "put": { 133 | "type": "async" 134 | }, 135 | "get": { 136 | "type": "async" 137 | }, 138 | "del": { 139 | "type": "async" 140 | }, 141 | "batch": { 142 | "type": "async" 143 | }, 144 | "approximateSize": { 145 | "type": "async" 146 | } 147 | }, 148 | "sublevels": {} 149 | }, 150 | "details": { 151 | "methods": { 152 | "createReadStream": { 153 | "type": "readable" 154 | }, 155 | "readStream": { 156 | "type": "readable" 157 | }, 158 | "createValueStream": { 159 | "type": "readable" 160 | }, 161 | "valueStream": { 162 | "type": "readable" 163 | }, 164 | "createKeyStream": { 165 | "type": "readable" 166 | }, 167 | "keyStream": { 168 | "type": "readable" 169 | }, 170 | "createWriteStream": { 171 | "type": "writable" 172 | }, 173 | "writeStream": { 174 | "type": "writable" 175 | }, 176 | "isOpen": { 177 | "type": "sync" 178 | }, 179 | "isClosed": { 180 | "type": "sync" 181 | }, 182 | "put": { 183 | "type": "async" 184 | }, 185 | "get": { 186 | "type": "async" 187 | }, 188 | "del": { 189 | "type": "async" 190 | }, 191 | "batch": { 192 | "type": "async" 193 | }, 194 | "approximateSize": { 195 | "type": "async" 196 | } 197 | }, 198 | "sublevels": {} 199 | }, 200 | "peers": { 201 | "methods": { 202 | "createReadStream": { 203 | "type": "readable" 204 | }, 205 | "readStream": { 206 | "type": "readable" 207 | }, 208 | "createValueStream": { 209 | "type": "readable" 210 | }, 211 | "valueStream": { 212 | "type": "readable" 213 | }, 214 | "createKeyStream": { 215 | "type": "readable" 216 | }, 217 | "keyStream": { 218 | "type": "readable" 219 | }, 220 | "createWriteStream": { 221 | "type": "writable" 222 | }, 223 | "writeStream": { 224 | "type": "writable" 225 | }, 226 | "isOpen": { 227 | "type": "sync" 228 | }, 229 | "isClosed": { 230 | "type": "sync" 231 | }, 232 | "put": { 233 | "type": "async" 234 | }, 235 | "get": { 236 | "type": "async" 237 | }, 238 | "del": { 239 | "type": "async" 240 | }, 241 | "batch": { 242 | "type": "async" 243 | }, 244 | "approximateSize": { 245 | "type": "async" 246 | } 247 | }, 248 | "sublevels": {} 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /lib/mine.js: -------------------------------------------------------------------------------- 1 | var Ethereum = require('ethereumjs-lib'); 2 | var crypto = require('crypto'); 3 | 4 | var utils = Ethereum.utils; 5 | 6 | exports.toggle = exports.start = function () { 7 | 8 | console.log('starting mining!'); 9 | 10 | var self = this; 11 | 12 | var head = this.blockchain.head; 13 | var vm = this.vm; 14 | //the current block 15 | var currentBlock = new Ethereum.Block(); 16 | 17 | currentBlock.header.timestamp = utils.intToBuffer(Date.now() / 1000 | 0); 18 | currentBlock.header.number = bignum.fromBuffer(head.header.number).add(1).toBuffer(); 19 | currentBlock.header.difficulty = utils.intToBuffer(currentBlock.header.canonicalDifficulty(head)); 20 | currentBlock.header.gasLimit = utils.intToBuffer(currentBlock.header.canonicalGaslimit(head)); 21 | currentBlock.header.parentHash = head.hash(); 22 | currentBlock.header.coinbase = new Buffer('fdad8ec94f3eca927e576b880defb6f69a7d5d9c', 'hex'); 23 | 24 | vm.runBlock({ block: currentBlock, root: head.header.state, gen: true }, function () { 25 | 26 | var notFound = true; 27 | var start = process.hrtime(); 28 | var hashes = 0; 29 | 30 | //generate state; 31 | while (notFound) { 32 | currentBlock.header.nonce = crypto.pseudoRandomBytes(32); 33 | hashes++; 34 | if (currentBlock.header.validatePOW()) { 35 | console.log('nonce found!'); 36 | notFound = false; 37 | } 38 | } 39 | 40 | var precision = 3; // 3 decimal places 41 | var elapsed = process.hrtime(start)[1] / 1000000; // divide by a million to get nano to milli 42 | var sec = process.hrtime(start)[0]; 43 | 44 | currentBlock.validate(self.blockchain, function () { 45 | self.network.broadcastNewBlock(currentBlock, bignum.fromBuffer(currentBlock.header.difficulty).add(bignum.fromBuffer(head.header.difficulty)).toBuffer()); 46 | }); 47 | 48 | console.log(sec + ' s, ' + elapsed.toFixed(precision) + ' ms - '); // print message + time 49 | console.log('hash/s: ' + (hashes / sec)); 50 | }); 51 | }; 52 | 53 | exports.stop = function () {}; 54 | -------------------------------------------------------------------------------- /lib/networking.js: -------------------------------------------------------------------------------- 1 | const Network = require('devp2p'); 2 | const Ethereum = require('ethereumjs-lib'); 3 | const log = require('npmlog'); 4 | const processTx = require('./processTxs'); 5 | const EthPeerManager = require('./ethPeerManagner.js'); 6 | const SyncManager = require('./syncManager.js'); 7 | 8 | var blockQueue = [], 9 | utils = Ethereum.utils; 10 | 11 | module.exports = function(done, options) { 12 | var self = this; 13 | 14 | //get the external ip 15 | var exip = this.settings.network.externalIp || options.ip, 16 | port = this.settings.network.port || 30303; 17 | 18 | //todo restore id 19 | this.network = new Network({ 20 | address: '0.0.0.0', 21 | port: port, 22 | // peerDB: peerDB, 23 | secretKey: new Buffer('a153387bcc66f16b6aeaed404d3c3e2ec04f3a85c6942e9de107fb8b2f71e322', 'hex'), 24 | capabilities: { 25 | eth: 60 26 | } 27 | }); 28 | 29 | var syncManager = new SyncManager(this); 30 | 31 | this.network.on('connection', function(peer) { 32 | log.info('connection!'); 33 | if (peer.caps.eth) { 34 | var stream = peer.createStream(); 35 | stream.on('end', function() { 36 | console.log('stream ended'); 37 | }) 38 | var peerMan = new EthPeerManager(stream); 39 | peerMan.on('status', function(status) { 40 | //dissconect if wrong genesis 41 | if (self.blockchain.meta.genesis !== status.genesisHash.toString('hex')) { 42 | peer.sendDisconnect(peer.DISCONNECT_REASONS.SUBPROTOCOL_REASON); 43 | }else{ 44 | syncManager.sync(peerMan); 45 | } 46 | }); 47 | peerMan.sendStatus(new Buffer([]), utils.intToBuffer(self.blockchain.td), self.blockchain.head.hash(), new Buffer(self.blockchain.meta.genesis, 'hex')); 48 | } 49 | }); 50 | 51 | // this.network.on('hello', function(hello, peer) { 52 | // peer.eth.status(bignum(self.blockchain.td).toBuffer(), self.blockchain.head.hash(), new Buffer(self.blockchain.meta.genesis, 'hex')); 53 | // log.info('networking', 'hello from: ' + hello.clientId + ' version:' + hello.protocolVersion); 54 | // }); 55 | 56 | // this.network.on('eth.status', function(status, peer) { 57 | 58 | // peer.td = status.td; 59 | // peer.bestHash = status.bestHash; 60 | 61 | // log.info('networking', peer.internalId + ' status'); 62 | 63 | 64 | // //try to sync the blockchain 65 | // // self._sync.bind(self)(peer, function() { 66 | // // self.blockProcesser.run(blockQueue); 67 | // // log.info('sync', 'done syncing!'); 68 | // // }); 69 | // }); 70 | 71 | // this.network.on('eth.blocks', function(blocks, peer) { 72 | // log.info('networking', peer.toString() + ' got ' + blocks.length + ' blocks'); 73 | // }); 74 | 75 | this.network.on('disconnect', function(dis) { 76 | log.info('networking', 'dissconect: ' + dis.reason); 77 | }); 78 | 79 | // this.network.on('eth.transactions', function(transactions, peer) { 80 | // log.info('networking', peer.toString() + ' got transactions'); 81 | 82 | // //check to make sure we dont alread have the tx 83 | // transactions.forEach(function(tx) { 84 | // var hash = tx.hash().toString('hex'); 85 | // if (tx.validate()) { 86 | 87 | // var pos = self.pendingTxs.map(function(t) { 88 | // return t.hash().toString('hex'); 89 | // }).indexOf(hash); 90 | 91 | // if (!pos) { 92 | // //save the tx 93 | // pos = self.pendingTxs.push(tx) - 1; 94 | // processTx.bind(self)(tx, function(p, err) { 95 | // //if it is an invalid tx remove it from the list 96 | // if (err) { 97 | // log.info('tx', 'invalid state: ' + hash); 98 | // self.orderedTxs.splice(p, p + 1); 99 | // } 100 | // }.bind(this, pos)); 101 | // } 102 | // } else { 103 | // log.info('tx', 'invalid signature or stucture' + hash); 104 | // } 105 | // }); 106 | 107 | // //TODO: check if transaction is in the DB 108 | // //check if the transaction is valid 109 | // //push tx to txlist 110 | // //save in db 111 | // }); 112 | 113 | // this.network.on('peers', function(peers, peer) { 114 | // log.info('networking', peer.toString() + ' got peers'); 115 | // }); 116 | 117 | // this.network.on('getPeers', function(peers, peer) { 118 | // //sending peers is implemented in the logic 119 | // log.info('networking', peer.toString() + ' got get peers'); 120 | // }); 121 | 122 | // this.network.on('eth.getBlockHashes', function(message, peer) { 123 | // log.info('networking', peer.toString() + ' got Get Block Hashes'); 124 | // self.blockchain.getBlockHashes(message.hash, utils.bufferToInt(message.maxBlocks), function(err, hashes) { 125 | // console.log(err + hashes); 126 | // }); 127 | // }); 128 | 129 | // this.network.on('eth.blockHashes', function(message, peer) { 130 | // log.info('networking', peer.toString() + ' got Block Hashes'); 131 | // }); 132 | 133 | // this.network.on('eth.getBlocks', function(message, peer) { 134 | // this.blockchain.getBlocks(message, function(blocks) { 135 | // peer.eth.sendBlocks(blocks); 136 | // }); 137 | // log.info('networking', peer.toString() + ' got get Blocks'); 138 | // }); 139 | 140 | // this.network.on('eth.newBlock', function(message, peer) { 141 | // log.info('networking', peer.toString() + ' got new Block:' + message.block.hash().toString('hex')); 142 | // if (self.isSyncing) { 143 | // blockQueue.push(message); 144 | // } else { 145 | // //TODO: check td 146 | // self.blockProcesser.run([message.block]); 147 | // } 148 | // }); 149 | 150 | // this.network.on('eth.getTransactions', function(message, peer) { 151 | // log.info('networking', peer.toString() + ' got request for transactions'); 152 | // peer.eth.transactions(self.pendingTxs); 153 | // }); 154 | 155 | // this.network.on('closing', function(peer) { 156 | // log.info('networking', peer.toString() + ' closing'); 157 | // }); 158 | 159 | // this.network.on('socket.error', function(e) { 160 | // log.error('networking', 'socket error: ' + e); 161 | // }); 162 | 163 | // this.network.on('parsing.error', function(e) { 164 | // log.error('networking', 'parse error: ' + e); 165 | // }); 166 | 167 | // this.network.on('ping', function(blocks, peer) { 168 | // log.info('networking', peer.toString() + ' got ping'); 169 | // }); 170 | 171 | var dpt = this.network.dpt; 172 | 173 | dpt.socket.on('message', function(msg, rinfo) { 174 | // console.log('server got msg from ' + rinfo.address + ":" + rinfo.port); 175 | }); 176 | 177 | dpt.on('ping', function(ping, peer) { 178 | console.log('got ping ---- '); 179 | }); 180 | 181 | dpt.on('pong', function(pong, peer) { 182 | console.log('got pong----'); 183 | }); 184 | 185 | dpt.on('findNode', function(findNode, peer) { 186 | // console.log('findNode----'); 187 | // console.log(findNode.id.toString('hex')); 188 | }); 189 | 190 | dpt.on('neighbors', function(neighbors, peer) { 191 | // console.log('neighbors----'); 192 | // neighbors.forEach(function(n) { 193 | // console.log('adding: ' + n.id.toString('hex')); 194 | // }); 195 | }); 196 | 197 | dpt.on('newPeer', function(peer) { 198 | console.log('newPeer'); 199 | }); 200 | 201 | dpt.on('error', function() { 202 | console.log('eeeerorrr'); 203 | }) 204 | 205 | this.network.listen(this.settings.network.port, this.settings.network.host, function() { 206 | self.network.connect({ 207 | address: '127.0.0.1', 208 | port: '30303' 209 | }); 210 | 211 | done() 212 | }); 213 | }; 214 | -------------------------------------------------------------------------------- /lib/processTxs.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var unprocessedTxs = []; 4 | 5 | var q = async.queue(function (params, cb) { 6 | params.vm.runTx(params.tx, params.block, cb); 7 | }, 1); 8 | 9 | /** 10 | * Runs transaction as they come in 11 | */ 12 | module.exports = function (txs, cb) { 13 | 14 | var self = this; 15 | 16 | if (!Array.isArray(txs)) { 17 | txs = [txs]; 18 | } 19 | 20 | unprocessedTxs.concat(txs); 21 | 22 | if (!this.isSyncing && this.isMining) { 23 | unprocessedTxs.forEach(function(tx){ 24 | q.push({ 25 | tx: tx, 26 | block: self.currentBlock, 27 | vm: self.vm 28 | }, cb); 29 | }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/rpc/eth.js: -------------------------------------------------------------------------------- 1 | const Account = require('ethereumjs-lib').Account; 2 | const VM = require('ethereumjs-lib').VM; 3 | const Transaction = require('ethereumjs-lib').Transaction; 4 | const netDefs = require('ethereumjs-lib').networkDef; 5 | const ethUtil = require('ethereumjs-util'); 6 | const crypto = require('crypto'); 7 | const hexStrPrefix = '0x'; 8 | 9 | module.exports = { 10 | protocolVersion: protocolVersion, 11 | coinbase: coinbase, 12 | mining: mining, 13 | accounts: accounts, 14 | blockNumber: blockNumber, 15 | getBalance: getBalance, 16 | getStorageAt: getStorageAt, 17 | getTransactionCount: getTransactionCount, 18 | getCode: getCode, 19 | call: call, 20 | getBlockByHash: getBlockByHash, 21 | getBlockByNumber: getBlockByNumber, 22 | getTransactionByBlockNumberAndIndex: getTransactionByBlockNumberAndIndex, 23 | getUncleByBlockHashAndIndex: getUncleByBlockHashAndIndex, 24 | newFilter: newFilter, 25 | newBlockFilter: newBlockFilter, 26 | uninstallFilter: uninstallFilter, 27 | getFilterChanges: getFilterChanges, 28 | getFilterLogs: getFilterLogs, 29 | _signedTransact: _signedTransact, 30 | } 31 | 32 | 33 | function getBalance(params, cb) { 34 | var address = ethUtil.stripHexPrefix(params[0]); 35 | var blockHash = ethUtil.stripHexPrefix(params[1]); 36 | 37 | this.getStateAtBlock(blockHash, function(err, trie) { 38 | if (err) return cb(err); 39 | trie.get(new Buffer(address, 'hex'), function(err, data) { 40 | if (err) return cb(err); 41 | var account = new Account(data); 42 | var balance = account.balance.toString('hex'); 43 | if (balance === '') balance = '0'; 44 | cb(null, hexStrPrefix + balance); 45 | }); 46 | }); 47 | } 48 | 49 | function getStorageAt(params, cb) { 50 | var address = ethUtil.stripHexPrefix(params[0]); 51 | var blockHash = ethUtil.stripHexPrefix(params[1]); 52 | var self = this; 53 | 54 | function dumpState(err, trie) { 55 | if (err) return cb(err); 56 | trie.get(new Buffer(address, 'hex'), function(err, data) { 57 | if (err) return cb(err); 58 | 59 | var account = new Account(data); 60 | var t = trie.copy(); 61 | t.root = account.stateRoot; 62 | var stream = t.createReadStream(); 63 | var returnVals = {}; 64 | 65 | stream.on('data', function(data) { 66 | returnVals[data.key.toString('hex')] = data.value.toString('hex'); 67 | }); 68 | 69 | stream.on('end', function() { 70 | cb(null, returnVals); 71 | }); 72 | }); 73 | } 74 | 75 | this.getStateAtBlock(blockHash, dumpState); 76 | } 77 | 78 | 79 | function getTransactionCount(params, cb) { 80 | var address = ethUtil.stripHexPrefix(params[0]); 81 | var blockHash = ethUtil.stripHexPrefix(params[1]); 82 | var self = this; 83 | 84 | function getNonce(err, trie) { 85 | if (err) return cb(err); 86 | trie.get(new Buffer(address, 'hex'), function(err, data) { 87 | if (err) return cb(err); 88 | var account = new Account(data); 89 | cb(null, hexStrPrefix + account.nonce.toString('hex')); 90 | }); 91 | } 92 | 93 | this.getStateAtBlock(blockHash, getNonce); 94 | } 95 | 96 | function getCode(params, cb) { 97 | var address = ethUtil.stripHexPrefix(params[0]); 98 | var blockHash = ethUtil.stripHexPrefix(params[1]); 99 | var self = this; 100 | 101 | function getCode(err, trie) { 102 | if (err) return cb(err); 103 | trie.get(new Buffer(address, 'hex'), function(err, data) { 104 | if (err) return cb(err); 105 | var account = new Account(data); 106 | account.getCode(trie, function(err, code) { 107 | if (err) return cb(err); 108 | cb(null, hexStrPrefix + code.toString('hex')); 109 | }); 110 | }); 111 | } 112 | 113 | this.getStateAtBlock(blockHash, getCode); 114 | } 115 | 116 | function _signedTransact(params, cb) { 117 | 118 | var hexString = ethUtil.stripHexPrefix(params[0]) 119 | var enableTrace = params[1]; 120 | var raw = new Buffer(hexString, 'hex'); 121 | var transaction = new Transaction(raw); 122 | var self = this; 123 | 124 | if (enableTrace) { 125 | var vmTrace = ''; 126 | var stream = this.app.vm.logReadStream(); 127 | 128 | stream.on('data', function(data){ 129 | vmTrace += data; 130 | }) 131 | } 132 | 133 | this.app.vm.runTx({ 134 | tx: transaction, 135 | block: this.app.blockchain.head 136 | }, function(err, result) { 137 | 138 | // cleanup 139 | if (enableTrace) { 140 | stream.end(); 141 | } 142 | 143 | // abort on error 144 | if (err) return cb(err); 145 | 146 | if (self.app.network) { 147 | self.app.network.broadcastTransactions([transaction]); 148 | } 149 | 150 | // process results 151 | var returnValue; 152 | if (result.createdAddress) { 153 | returnValue = hexStrPrefix + result.createdAddress.toString('hex'); 154 | } else { 155 | returnValue = hexStrPrefix + transaction.hash().toString('hex'); 156 | } 157 | 158 | if (enableTrace) { 159 | returnValue = { 160 | result: returnValue, 161 | vmTrace: vmTrace, 162 | } 163 | } 164 | 165 | cb(null, returnValue); 166 | }); 167 | } 168 | 169 | function call(params, cb) { 170 | var txParams = params[0] 171 | var blockHash = ethUtil.stripHexPrefix(params[1]); 172 | var enableTrace = params[2]; 173 | var self = this; 174 | 175 | this.getStateAtBlock(blockHash, function(err, stateTrie){ 176 | if (err) return cb(err); 177 | 178 | var vm = new VM(stateTrie, self.app.blockchain); 179 | var transaction = new Transaction({ 180 | to: txParams.to, 181 | from: txParams.from, 182 | value: txParams.value, 183 | data: txParams.data, 184 | gasLimit: txParams.gas || '0xffffffffffffffff', 185 | gasPrice: txParams.gasPrice, 186 | }); 187 | 188 | //from is special unfortally and is not normilized by the setter 189 | transaction.from = new Buffer(ethUtil.stripHexPrefix(txParams.from), 'hex'); 190 | 191 | stateTrie.checkpoint(); 192 | 193 | if (enableTrace) { 194 | var vmTrace = ''; 195 | var stream = vm.logReadStream(); 196 | 197 | stream.on('data', function(data){ 198 | vmTrace += data; 199 | }) 200 | } 201 | 202 | vm.runTx({ 203 | tx: transaction, 204 | block: self.app.blockchain.head, 205 | skipNonce: true, 206 | }, function(err, results) { 207 | 208 | // cleanup 209 | stateTrie.revert(); 210 | if (enableTrace) { 211 | stream.end(); 212 | } 213 | 214 | // abort on error 215 | if (err) return cb(err); 216 | 217 | // process results 218 | if (results.vm.returnValue) { 219 | var returnValue = hexStrPrefix + results.vm.returnValue.toString('hex') 220 | if (enableTrace) { 221 | returnValue = { 222 | result: returnValue, 223 | vmTrace: vmTrace 224 | } 225 | } 226 | cb(null, returnValue); 227 | } else { 228 | cb(null, null); 229 | } 230 | }); 231 | }); 232 | } 233 | 234 | function getBlockByHash(params, cb) { 235 | var hash = ethUtil.stripHexPrefix(params[0]) 236 | this.app.blockchain.getBlock(hash, cb); 237 | } 238 | 239 | function getBlockByNumber(params, cb) { 240 | this.app.blockchain.getBlockByNumber(ethUtil.intToBuffer(params[0]), cb); 241 | } 242 | 243 | function getTransactionByBlockNumberAndIndex(params, cb) { 244 | this.app.blockchain.getBlockByNumber(params[0], function(err, block) { 245 | if (err) return cb(err); 246 | cb(null, block.transactions[params[1]]); 247 | }); 248 | } 249 | 250 | function getUncleByBlockHashAndIndex(params, cb) { 251 | this.app.blockchain.getBlockByNumber(params[0], function(err, block) { 252 | if (err) return cb(err); 253 | cb(null, block.uncles[params[1]]); 254 | }); 255 | } 256 | 257 | function newFilter(params, cb) { 258 | var self = this; 259 | var id = crypto.randomBytes(8).toString('hex'); 260 | var filter = params[0]; 261 | 262 | var topics = filter.topics || []; 263 | topics = topics 264 | .filter(function(item){ 265 | return !!item; 266 | }) 267 | .map(function(t){ 268 | return ethUtil.stripHexPrefix(t); 269 | }) 270 | 271 | if (filter.address) topics.push(ethUtil.stripHexPrefix(filter.address)); 272 | self.logFilters[id] = { 273 | topics: topics, 274 | results: [], 275 | }; 276 | 277 | cb(null, id); 278 | 279 | } 280 | 281 | function newBlockFilter(params, cb) { 282 | var self = this; 283 | var id = crypto.randomBytes(8).toString('hex'); 284 | var topic = params[0]; 285 | 286 | if (topic === 'pending' || topic === 'latest'){ 287 | self.blockFilters[topic][id] = { results: [] }; 288 | cb(null, id); 289 | } else { 290 | cb(new Error('Unknown block filter type: "'+topic+'"')) 291 | } 292 | } 293 | 294 | function getFilterChanges(params, cb) { 295 | var self = this; 296 | var filterId = params[0]; 297 | var filter = self.logFilters[filterId] 298 | || self.blockFilters.pending[filterId] 299 | || self.blockFilters.latest[filterId]; 300 | 301 | if (filter) { 302 | cb(null, filter.results); 303 | filter.results = []; 304 | } else { 305 | cb(new Error('No filter for id: "'+filterId+'"')); 306 | } 307 | } 308 | 309 | function getFilterLogs(params, cb) { 310 | var self = this; 311 | var filterId = params[0]; 312 | var filter = self.logFilters[filterId] 313 | || self.blockFilters.pending[filterId] 314 | || self.blockFilters.latest[filterId]; 315 | 316 | if (filter) { 317 | cb(null, filter.results); 318 | } else { 319 | cb(new Error('No filter for id: "'+filterId+'"')); 320 | } 321 | } 322 | 323 | function uninstallFilter(params, cb) { 324 | var filterId = params[0]; 325 | var filter = self.logFilters[filterId] 326 | || self.blockFilters.pending[filterId] 327 | || self.blockFilters.latest[filterId]; 328 | var exists = !!filter; 329 | delete self.logFilters[filterId]; 330 | delete self.blockFilters.pending[filterId]; 331 | delete self.blockFilters.latest[filterId]; 332 | cb(null, exists); 333 | } 334 | 335 | function protocolVersion(params, cb) { 336 | cb(null, netDefs.meta.version); 337 | } 338 | 339 | function blockNumber(params, cb){ 340 | cb(null, hexStrPrefix + this.app.blockchain.head.header.number.toString('hex')); 341 | } 342 | 343 | 344 | // 345 | // STUBS - these are not fully implemented 346 | // 347 | 348 | function accounts(params, cb) { 349 | cb(null, []); 350 | } 351 | 352 | function mining(params, cb) { 353 | cb(null, false); 354 | } 355 | 356 | function coinbase(params, cb) { 357 | var address = nullAddress; 358 | cb(null, hexStrPrefix + address.toString('hex')); 359 | } 360 | -------------------------------------------------------------------------------- /lib/rpc/index.js: -------------------------------------------------------------------------------- 1 | const Block = require('ethereumjs-lib').Block; 2 | const ethUtil = require('ethereumjs-util'); 3 | const prettyHrtime = require('pretty-hrtime'); 4 | const eth = require('./eth.js'); 5 | const net = require('./net.js'); 6 | const SUPPORTED_RPC_VERSION = '2.0'; 7 | const hexStrPrefix = '0x'; 8 | 9 | module.exports = EthRPC; 10 | 11 | 12 | function EthRPC(app) { 13 | var self = this; 14 | this.app = app; 15 | 16 | this._setupHandlers(); 17 | this._setupFilters(); 18 | } 19 | 20 | 21 | EthRPC.prototype.processRpc = function(rpcMessage, cb) { 22 | var start = process.hrtime(); 23 | console.log('processing rpc:', rpcMessage); 24 | // parse rpc arguments 25 | var method = rpcMessage.method; 26 | var params = rpcMessage.params; 27 | var requestId = rpcMessage.id; 28 | var version = rpcMessage.jsonrpc; 29 | // extract relevant function 30 | var fn = this._fnForMethod(method); 31 | // check rpc version 32 | if (version !== SUPPORTED_RPC_VERSION) { 33 | fn = versionNotSupported; 34 | } 35 | // execute rpc method 36 | fn.call(this, params, function(err, result) { 37 | if (err) { 38 | cb(err); 39 | } else { 40 | var duration = process.hrtime(start); 41 | console.log('completed rpc (', rpcMessage.id, ') ', prettyHrtime(duration)) 42 | cb(null, wrapResult(requestId, result)); 43 | } 44 | }); 45 | } 46 | 47 | /** 48 | * Fetches a block given a blockHash or A block Number. The callback is then 49 | * given a Trie that is set to the stateRoot of the found block if there is one 50 | */ 51 | EthRPC.prototype.getStateAtBlock = function(blockHash, cb) { 52 | blockHash = ethUtil.stripHexPrefix(blockHash) 53 | var self = this; 54 | 55 | function createBlock(err, data) { 56 | if (err) return cb(err); 57 | 58 | var block = new Block(data); 59 | var stateRoot = block.header.stateRoot; 60 | var trie = self.app.vm.trie.copy(); 61 | 62 | trie.root = stateRoot; 63 | cb(null, trie); 64 | } 65 | 66 | if (blockHash === 'last') { 67 | blockhash = this.app.blockchain.meta.genesis; 68 | } 69 | 70 | if (blockHash === 'latest' || blockHash === 'pending') { 71 | cb(null, this.app.vm.trie); 72 | } else if (blockHash) { 73 | 74 | //lookup a hash 75 | if (blockHash.length === 64) { 76 | this.app.blockDB.get(new Buffer(blockHash, 'hex'), { 77 | valueEncoding: 'binary' 78 | }, createBlock); 79 | } else { 80 | //find block by number 81 | this.app.blockchain.getBlockByNumber(blockHash, createBlock); 82 | } 83 | } else { 84 | cb(null, this.app.vm.trie); 85 | } 86 | } 87 | 88 | EthRPC.prototype._setupHandlers = function() { 89 | this.map = { 90 | // -- web3 -- 91 | web3_clientVersion: notImplemented, 92 | web3_sha3: notImplemented, 93 | 94 | // -- net -- 95 | net_version: notImplemented, 96 | net_peerCount: net.peerCount, 97 | net_listening: net.listening, 98 | 99 | // -- eth -- 100 | eth_protocolVersion: eth.protocolVersion, 101 | eth_coinbase: eth.coinbase, 102 | eth_mining: eth.mining, 103 | eth_hashrate: notImplemented, 104 | eth_gasPrice: notImplemented, 105 | eth_accounts: eth.accounts, 106 | eth_blockNumber: eth.blockNumber, 107 | eth_getBalance: eth.getBalance, 108 | eth_getStorageAt: eth.getStorageAt, 109 | eth_getTransactionCount: eth.getTransactionCount, 110 | eth_getBlockTransactionCountByHash: notImplemented, 111 | eth_getBlockTransactionCountByNumber: notImplemented, 112 | eth_getUncleCountByBlockHash: notImplemented, 113 | eth_getUncleCountByBlockNumber: notImplemented, 114 | eth_getCode: eth.getCode, 115 | eth_sign: notImplemented, 116 | eth_sendTransaction: notImplemented, 117 | eth_call: eth.call, 118 | eth_estimateGas: notImplemented, 119 | eth_getBlockByHash: eth.getBlockByHash, 120 | eth_getBlockByNumber: eth.getBlockByNumber, 121 | eth_getTransactionByHash: notImplemented, 122 | eth_getTransactionByBlockHashAndIndex: notImplemented, 123 | eth_getTransactionByBlockNumberAndIndex: eth.getTransactionByBlockNumberAndIndex, 124 | eth_getUncleByBlockHashAndIndex: notImplemented, 125 | eth_getUncleByBlockNumberAndIndex: notImplemented, 126 | eth_getCompilers: notImplemented, 127 | eth_compileLLL: notImplemented, 128 | eth_compileSolidity: notImplemented, 129 | eth_compileSerpent: notImplemented, 130 | eth_newFilter: eth.newFilter, 131 | eth_newBlockFilter: eth.newBlockFilter, 132 | eth_newPendingTransactionFilter: notImplemented, 133 | eth_uninstallFilter: eth.uninstallFilter, 134 | eth_getFilterChanges: eth.getFilterChanges, 135 | eth_getFilterLogs: eth.getFilterLogs, 136 | eth_getLogs: notImplemented, 137 | eth_getWork: notImplemented, 138 | eth_submitWork: notImplemented, 139 | // extra 140 | eth_signedTransact: eth._signedTransact, 141 | 142 | // -- db -- 143 | db_putString: notImplemented, 144 | db_getString: notImplemented, 145 | db_putHex: notImplemented, 146 | db_getHex: notImplemented, 147 | 148 | // -- shh -- 149 | shh_post: notImplemented, 150 | shh_version: notImplemented, 151 | shh_newIdentity: notImplemented, 152 | shh_hasIdentity: notImplemented, 153 | shh_newGroup: notImplemented, 154 | shh_addToGroup: notImplemented, 155 | shh_newFilter: notImplemented, 156 | shh_uninstallFilter: notImplemented, 157 | shh_getFilterChanges: notImplemented, 158 | shh_getMessages: notImplemented, 159 | } 160 | } 161 | 162 | EthRPC.prototype._setupFilters = function() { 163 | var self = this; 164 | this.logFilters = {}; 165 | this.blockFilters = { pending: {}, latest: {} }; 166 | 167 | // update "pending" block filters 168 | this.app.vm.on('afterTx', function(){ 169 | for (id in self.blockFilters.pending) { 170 | var pendingFilter = self.blockFilters.pending[id] 171 | // "For filters created with eth_newBlockFilter log objects are null." 172 | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges 173 | pendingFilter.results.push(null) 174 | } 175 | }) 176 | 177 | // update "latest" block filters 178 | this.app.vm.on('block', function(){ 179 | for (id in self.blockFilters.latest) { 180 | var blockFilter = self.blockFilters.latest[id] 181 | // "For filters created with eth_newBlockFilter log objects are null." 182 | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges 183 | blockFilter.results.push(null) 184 | } 185 | }) 186 | 187 | // update log filters 188 | this.app.vm.on('logs', function(logEvent) { 189 | for (var id in self.logFilters) { 190 | var filter = self.logFilters[id]; 191 | // check for log filter match 192 | if (logEvent.bloom.multiCheck(filter.topics)) { 193 | onLogFilterBloomMatch(filter, logEvent, id) 194 | } 195 | } 196 | }); 197 | 198 | function onLogFilterBloomMatch(filter, logEvent, id) { 199 | logEvent.logs.forEach(function(log, logIndex) { 200 | //push to the filter queue 201 | var match = true; 202 | var logTopics = log[1]; 203 | // add address as topic 204 | logTopics.push(log[0]); 205 | 206 | filter.topics.forEach(function(filterTopic) { 207 | var filterMatch = false 208 | 209 | logTopics.forEach(function(logTopic) { 210 | filterMatch |= (logTopic.toString('hex') === filterTopic); 211 | }); 212 | match &= filterMatch; 213 | }); 214 | 215 | if (match) { 216 | var transactionIndex; 217 | if(logEvent.block){ 218 | transactionIndex = logEvent.block.transactions.indexOf(logEvent.tx) 219 | //if -1 220 | if(transactionIndex < 0) transactionIndex = 0; 221 | }else{ 222 | transactionIndex = 0; 223 | logEvent.block = new Block(); 224 | } 225 | 226 | filter.results.push({ 227 | // HEX String - integer of the log index position in the block. 228 | logIndex: hexStrPrefix+ethUtil.intToHex(logIndex), 229 | // HEX String - integer of the transactions index position log was created from. 230 | transactionIndex: hexStrPrefix+ethUtil.intToHex(transactionIndex), 231 | // HEX String - hash of the transactions this log was created from. 232 | transactionHash: hexStrPrefix+logEvent.tx.hash().toString('hex'), 233 | // HEX String - 32-byte hash of the block where this log was in. null when the log is pending. 234 | blockHash: logEvent.block.hash().toString('hex'), 235 | // HEX String - integer of the block number where this log was in. null when the log is pending. 236 | blockNumber: hexStrPrefix+logEvent.block.header.number.toString('hex'), 237 | // HEX String - address from which this log originated. 238 | address: log[0].toString('hex'), 239 | // HEX String - contains the non-indexed arguments of the log. 240 | data: log[2].toString('hex'), 241 | // Array - Array of 0 to 4 HEX Strings of indexed log arguments. 242 | topics: logTopics.map(function(buffer) { return buffer.toString('hex') }), 243 | }); 244 | } 245 | }); 246 | } 247 | } 248 | 249 | // 250 | // Method map 251 | // 252 | 253 | EthRPC.prototype._fnForMethod = function(method) { 254 | return this.map[method] || noSuchMethod(method); 255 | } 256 | 257 | // util 258 | 259 | function notImplemented(params, cb) { 260 | var errMessage = 'RPC Method Not Implemented.' 261 | console.error(errMessage) 262 | cb(new Error(errMessage)) 263 | } 264 | 265 | function noSuchMethod(method) { 266 | return function(params, cb){ 267 | var errMessage = 'Unknown RPC Method "'+method+'".' 268 | console.error(errMessage) 269 | cb(new Error(errMessage)) 270 | } 271 | } 272 | 273 | function versionNotSupported(params, cb) { 274 | var errMessage = 'Unsupported RPC Version.' 275 | console.error(errMessage) 276 | cb(new Error(errMessage)) 277 | } 278 | 279 | function wrapResult(requestId, value) { 280 | var responseObj = { 281 | id: requestId, 282 | jsonrpc: SUPPORTED_RPC_VERSION, 283 | result: value 284 | }; 285 | // if trace is present, move trace onto response object 286 | // and use correct return variable 287 | if (value && value.vmTrace) { 288 | responseObj.result = value.result; 289 | responseObj.vmTrace = value.vmTrace; 290 | } 291 | return responseObj; 292 | } -------------------------------------------------------------------------------- /lib/rpc/net.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | listening: listening, 4 | peerCount: peerCount, 5 | } 6 | 7 | 8 | function listening(params, cb) { 9 | if (this.app.network) { 10 | cb(null, this.app.network.listening); 11 | } else { 12 | cb(null, false); 13 | } 14 | } 15 | 16 | function peerCount(params, cb) { 17 | var peers; 18 | if (this.app.network) { 19 | peers = this.app.network.peers.length; 20 | } else { 21 | peers = 0; 22 | } 23 | cb(null, peers); 24 | } -------------------------------------------------------------------------------- /lib/rpc/server/http.js: -------------------------------------------------------------------------------- 1 | const Express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const async = require('async'); 4 | 5 | module.exports = HttpRpc; 6 | 7 | 8 | function HttpRpc(rpc){ 9 | 10 | var server = this.server = Express(); 11 | 12 | // parse application/json even if it doesnt say its json (*/* does not work) 13 | server.use(bodyParser.json({ type: 'application/*' })); 14 | server.use(bodyParser.json({ type: 'text/*' })); 15 | 16 | // allow any origin 17 | // explicitly listing the request origin instead on wildcard b/c of below issue 18 | // when resolved, could use ghub.io/cors instead 19 | // https://github.com/ethereum/ethereum.js/issues/36 20 | server.use(function(req, res, next) { 21 | if (req.headers.origin) { 22 | res.header('Access-Control-Allow-Origin', req.headers.origin); 23 | res.header('Access-Control-Allow-Credentials', 'true'); 24 | res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 25 | res.header('Access-Control-Allow-Headers', 'origin, content-type, accept'); 26 | } 27 | next(); 28 | }); 29 | 30 | // helpful message for wrong http verb 31 | server.get('/', function (req, res) { 32 | res.send('XHR JSON RPC available via POST'); 33 | }); 34 | 35 | // extract message and call rpc 36 | server.post('/', function (req, res) { 37 | 38 | // handle request 39 | var message = req.body; 40 | if (message && Object.keys(message).length) { 41 | 42 | var isBatch = Array.isArray(message); 43 | 44 | if (isBatch) { 45 | 46 | var messages = message 47 | async.mapSeries(messages, function(message, cb){ 48 | rpc.processRpc(message, function(err, data){ 49 | if (err) { 50 | cb(null, jsonErrorObject(err)); 51 | } else { 52 | cb(null, data); 53 | } 54 | }); 55 | }, respondToRequest); 56 | 57 | } else { 58 | 59 | rpc.processRpc(message, respondToRequest); 60 | 61 | } 62 | 63 | } else { 64 | 65 | respondToRequest(new Error('Could not parse RPC body.')); 66 | 67 | } 68 | 69 | function respondToRequest(err, data) { 70 | if (err) { 71 | res.status(500).send(jsonErrorObject(err)); 72 | } else { 73 | res.send(data); 74 | } 75 | } 76 | 77 | function jsonErrorObject(err) { 78 | return { 79 | error: { 80 | code: err.code || -32603, 81 | message: err.message || err, 82 | stack: err.stack, 83 | } 84 | }; 85 | } 86 | 87 | }); 88 | 89 | } 90 | 91 | //the function that starts the application 92 | HttpRpc.prototype.start = function(options, done) { 93 | var port = options.port || 8080; 94 | console.log('HttpRpcServer - opening: /:' + port); 95 | this.httpServer = this.server.listen(port); 96 | process.once('SIGTERM', this.stop.bind(this)); 97 | process.once('SIGINT', this.stop.bind(this)); 98 | 99 | done(); 100 | }; 101 | 102 | HttpRpc.prototype.stop = function(done) { 103 | this.httpServer.close(); 104 | if (done) done(); 105 | }; 106 | -------------------------------------------------------------------------------- /lib/rpc/server/ws.js: -------------------------------------------------------------------------------- 1 | const WebSocketServer = require('ws').Server; 2 | 3 | module.exports = WsRpc; 4 | 5 | 6 | function WsRpc(rpc){ 7 | this.rpc = rpc; 8 | } 9 | 10 | WsRpc.prototype.start = function(options, done) { 11 | var self = this; 12 | 13 | options = options || {}; 14 | 15 | var port = options.port || 40404; 16 | 17 | var server = this.server = new WebSocketServer({ 18 | port: port 19 | }); 20 | 21 | console.log('WebsocketRpcServer - opening: /:'+port); 22 | 23 | server.on('connection', function(ws) { 24 | ws.on('message', function(message) { 25 | 26 | try { 27 | message = JSON.parse(message); 28 | self.rpc.processRpc(message, function(err, data){ 29 | if (err) { 30 | sendError(err); 31 | } else { 32 | sendSuccess(data); 33 | } 34 | }); 35 | 36 | } catch (error) { 37 | // invalid json 38 | error.code = -32700; 39 | sendError(error); 40 | } 41 | 42 | function sendError(error) { 43 | sendResponse({ 44 | status: 'failed', 45 | message: error.message, 46 | code: error.code 47 | }); 48 | } 49 | 50 | function sendSuccess(data) { 51 | sendResponse(data); 52 | } 53 | 54 | function sendResponse(data) { 55 | ws.send(JSON.stringify(data)); 56 | } 57 | 58 | }); 59 | }); 60 | 61 | server.broadcast = function(data) { 62 | for (var i in this.clients) { 63 | this.clients[i].send(data); 64 | } 65 | }; 66 | 67 | done(); 68 | }; 69 | 70 | WsRpc.prototype.stop = function(done) { 71 | console.log('WebsocketRpcServer - closing.') 72 | this.server.close(); 73 | done(); 74 | }; 75 | -------------------------------------------------------------------------------- /lib/syncManager.js: -------------------------------------------------------------------------------- 1 | const BN = require('bn.js'); 2 | const async = require('async'); 3 | 4 | var SyncManager = module.exports = function(app) { 5 | this.maxNumToDownload = 32; // the number of hashed to get per request TODO: vary on rating of peer 6 | this.syncingPeers = {}; 7 | this.app = app; 8 | this.currentBlock = this.largestTD; 9 | /** 10 | * hash enum 11 | * fetching, fetched, needed 12 | */ 13 | this.hashes = {}; 14 | }; 15 | 16 | /** 17 | * adds 18 | * peer.skipList // list of hashes where being fetched by someone else 19 | * peer.orderedHashes // list of hashes 20 | * peer.startBlockNumber //the blocknumber which we are starting to sync from 21 | */ 22 | SyncManager.prototype.sync = function(peer, cb) { 23 | if (!cb) cb = function() {}; 24 | 25 | var td = new BN(peer.status.td); 26 | if (new BN(this.app.blockchain.td).cmp(td) === -1) { 27 | peer.doneSyncing = false; //is the ordered hash list full? 28 | peer.skipList = [peer.status.bestHash]; 29 | peer.startBlockNumber = this.app.blockchain.meta.height; 30 | var bestHash = peer.status.bestHash.toString('hex'); 31 | this.hashes[bestHash] = 'needed'; 32 | this.hashes[this.app.blockchain.genesis] = 'have'; 33 | this.downloadChain(peer.status.bestHash, peer, cb); 34 | } else { 35 | cb(); 36 | } 37 | }; 38 | 39 | SyncManager.prototype.downloadChain = function(startHash, peer, cb) { 40 | console.log('syncing starting with: ' + startHash.toString('hex')); 41 | var self = this; 42 | 43 | if(peer.doneSyncing || startHash.toString('hex') === this.app.blockchain.meta.genesis) { 44 | cb(); 45 | return; 46 | } 47 | 48 | peer.fetchBlockHashes(startHash, this.maxNumToDownload, function(hashes) { 49 | console.log('got hashing: ' + hashes.length); 50 | //if no hashes returned then jump backwards 51 | self.app.blockchain.selectNeededHashes(hashes, function(err, neededHashes) { 52 | 53 | if(err) 54 | return cb(err) 55 | 56 | if (neededHashes.length < hashes.length){ 57 | peer.doneSyncing = true; 58 | } 59 | 60 | var hashesToFetch = []; 61 | 62 | peer.skipList.forEach(function(h) { 63 | if (self.hashes[h] !== 'have') { 64 | hashesToFetch.push(h); 65 | } 66 | }); 67 | 68 | peer.skipList = []; 69 | 70 | neededHashes.forEach(function(h) { 71 | h = h.toString('hex'); 72 | 73 | if (!self.hashes[h]) { 74 | self.hashes[h] = 'fetching'; 75 | hashesToFetch.push(h); 76 | } else if (self.hashes[h] === 'needed') { 77 | self.hashes[h] = 'fetching'; 78 | hashesToFetch.push(h); 79 | } else if (self.hashes[h] === 'fetching') { 80 | //someelse is downloading this hash 81 | //save it a check back later 82 | peer.skipList.push(h); 83 | } 84 | }); 85 | 86 | if (hashesToFetch.length) { 87 | 88 | hashesToFetch = hashesToFetch.map(function(h){ 89 | return new Buffer(h, 'hex'); 90 | }); 91 | 92 | console.log('fetching: '); 93 | peer.fetchBlocks(hashesToFetch, function(blocks) { 94 | console.log('got blocks: ' + blocks.length); 95 | blocks.forEach(function(block) { 96 | // console.log(block.hash().toString('hex')); 97 | var bh = block.hash().toString('hex'); 98 | self.hashes[bh] = 'have'; 99 | }); 100 | 101 | async.eachSeries(function(block, done) { 102 | self.app.blockchain.addBlockRaw(block, done); 103 | }, function() { 104 | self.downloadChain(hashes.pop(), peer, cb); 105 | }); 106 | }); 107 | } else { 108 | self.downloadChain(hashes.pop(), peer, cb); 109 | } 110 | }); 111 | }); 112 | }; 113 | -------------------------------------------------------------------------------- /lib/upnp.js: -------------------------------------------------------------------------------- 1 | const natUpnp = require('nat-upnp'); 2 | const getIP = require('external-ip')(); 3 | const log = require('npmlog'); 4 | 5 | const client = natUpnp.createClient(); 6 | var started = false; 7 | var port; 8 | 9 | //map ports 10 | exports.map = function(p, cb) { 11 | started = true; 12 | port = p; 13 | 14 | client.portMapping({ 15 | public: port, 16 | private: port, 17 | description: 'ethereum-node', 18 | ttl: 0 19 | }, function(err) { 20 | if (err) { 21 | if(err){ 22 | log.warn('unpn', err.toString()); 23 | } 24 | cb(); 25 | } 26 | }); 27 | }; 28 | 29 | //unmaps port 30 | exports.unmap = function(cb) { 31 | if (started) { 32 | client.portUnmapping({ 33 | public: port 34 | }, function() { 35 | started = false; 36 | client.close(); 37 | cb(); 38 | }); 39 | } else { 40 | try { 41 | client.close(); 42 | } catch (e) {} 43 | cb(); 44 | } 45 | }; 46 | 47 | //wraped for async 48 | exports.extrenalIp = function(done) { 49 | if (started) { 50 | client.externalIp(function(err, ip) { 51 | err = null; 52 | //ignore timeout erros 53 | done(null, ip); 54 | }); 55 | } else { 56 | getIP(function(err, ip) { 57 | done(err, ip); 58 | }); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-ethereum", 3 | "version": "1.2.2", 4 | "description": "a simple standalone or embeddable Ethereum client written for Node.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/wanderer/ethereum-client" 8 | }, 9 | "homepage": "https://github.com/wanderer/ethereum-client", 10 | "bugs": "https://github.com/wanderer/ethereum-client/issues", 11 | "main": "index.js", 12 | "bin": { 13 | "nether": "./bin/nether" 14 | }, 15 | "scripts": { 16 | "test": "mocha --timeout 5000 --reporter spec ./test/*.js" 17 | }, 18 | "keywords": [ 19 | "ethereum" 20 | ], 21 | "contributors": [ 22 | "Martin Becze (github.com/wanderer)", 23 | "kumavis (https://github.com/kumavis)", 24 | "Ethers (github.com/ethers)", 25 | "Jacob Payne (github.com/latrasis)" 26 | ], 27 | "license": "GPL2", 28 | "dependencies": { 29 | "async": "*", 30 | "bn.js": "^2.0.1", 31 | "body-parser": "~1.10.1", 32 | "devp2p": "0.0.2", 33 | "ethereum-common": "0.0.3", 34 | "ethereumjs-lib": "0.5.0", 35 | "ethereumjs-util": "1.3.2", 36 | "express": "~4.11.0", 37 | "external-ip": "^0.2.3", 38 | "is-my-json-valid": "^2.3.0", 39 | "level": "^0.18.0", 40 | "level-sublevel": "^6.4.0", 41 | "level-writestream": "^0.1.3", 42 | "levelup": "^0.19.0", 43 | "minimist": "*", 44 | "mkdirp": "^0.5.0", 45 | "multilevel": "^6.0.1", 46 | "nat-upnp": "0.2.11", 47 | "npmlog": "*", 48 | "pretty-hrtime": "^1.0.0", 49 | "request": "^2.40.0", 50 | "rlp": "1.0.1", 51 | "semaphore": "~1.0.1", 52 | "server-destroy": "^1.0.0", 53 | "stream-to-buffer": "^0.1.0", 54 | "ws": "git+https://github.com/wanderer/ws" 55 | }, 56 | "devDependencies": { 57 | "secp256k1": "0.0.16", 58 | "eslint": "^0.9.2", 59 | "ethereum-tests": "git+https://github.com/ethereum/tests.git#64a5c7b5961e7979344f4205fb999eff33b60019", 60 | "mocha": "^2.1.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /script/eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "rules": { 6 | "strict": 0, 7 | "no-underscore-dangle": 0, 8 | "no-comma-dangle": 0, 9 | "curly": 0, 10 | "new-cap": 0, 11 | "no-use-before-define": 0, 12 | "no-shadow": 1, 13 | "quotes": [1, "single"], 14 | "no-trailing-spaces": 1, 15 | "no-path-concat": 1, 16 | "semi": 1, 17 | "space-infix-ops": 1, 18 | "no-unused-vars": 1, 19 | "eol-last": 1, 20 | "no-undef": 1, 21 | "camelcase": 1, 22 | "key-spacing": 1, 23 | "handle-callback-err": 2 24 | }, 25 | "globals":{ 26 | "it": false, 27 | "describe": false, 28 | "module": false, 29 | "require": false, 30 | "before": false, 31 | "beforeEach": false, 32 | "after": false, 33 | "afterEach": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /script/eslint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # run eslint on modified js files 4 | git diff $(git merge-base HEAD origin/HEAD) --name-only --diff-filter=ACM | grep -v node_modules | grep ".js$" | xargs node_modules/.bin/eslint -c ./script/eslint.json 5 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | var App = require('../'), 2 | fs = require('fs'), 3 | path = require('path'), 4 | cp = require('child_process'), 5 | async = require('async'); 6 | 7 | var app, 8 | peers = [], 9 | numberOfPeers = 5, //the amount of peers to create in this test must be more than 3 for the tests to work properly 10 | startPort = 30316, 11 | dbServer; 12 | 13 | describe('basic app functions', function() { 14 | 15 | it('should start', function(done) { 16 | app = new App(); 17 | app.start(done); 18 | }); 19 | 20 | it('should stop', function(done) { 21 | app.stop(done); 22 | }); 23 | 24 | it('should it should start the db server', function(done) { 25 | try { 26 | fs.mkdirSync(path.join(__dirname, '/testdb')); 27 | } catch (e) {} 28 | 29 | dbServer = cp.fork(path.join(__dirname, '/../bin/dbServer'), [path.join(__dirname, '/testdb'), 30306]); 30 | dbServer.once('message', function(){ 31 | done(); 32 | }); 33 | }); 34 | 35 | it('should start serveral instances', function(done) { 36 | var count = 0; 37 | 38 | async.whilst( 39 | 40 | function() { 41 | return count < numberOfPeers; 42 | }, 43 | 44 | function(callback) { 45 | var settings = { 46 | 'network': { 47 | 'port': startPort, 48 | 'host': '0.0.0.0' 49 | }, 50 | 'dbServer': false, 51 | 'upnp': false, 52 | 'rpc': false 53 | }; 54 | 55 | count++; 56 | settings.network.port += count; 57 | 58 | app = new App(settings); 59 | peers.push(app); 60 | app.start(callback); 61 | }, 62 | 63 | function() { 64 | done(); 65 | } 66 | ); 67 | }); 68 | 69 | it.skip('two peers should connect to each other', function(done) { 70 | async.parallel([ 71 | function(cb2) { 72 | peers[1].network.once('hello', function() { 73 | cb2(); 74 | }); 75 | }, 76 | function(cb2) { 77 | peers[0].network.once('hello', function() { 78 | cb2(); 79 | }); 80 | } 81 | ], done); 82 | 83 | peers[0].network.connect({ 84 | port: startPort + 2, 85 | address: '0.0.0.0' 86 | }); 87 | }); 88 | 89 | it.skip('if a third peer joins then first two peers should both connect to it', function(done) { 90 | async.parallel([ 91 | function(cb2) { 92 | peers[2].network.once('hello', function() { 93 | cb2(); 94 | }); 95 | }, 96 | function(cb2) { 97 | peers[1].network.once('hello', function() { 98 | cb2(); 99 | }); 100 | }, 101 | function(cb2) { 102 | peers[0].network.once('hello', function() { 103 | cb2(); 104 | }); 105 | } 106 | ], done); 107 | 108 | peers[2].network.connect({ 109 | port: startPort + 1, 110 | address: '0.0.0.0' 111 | }); 112 | }); 113 | 114 | it.skip('should send only peers that the peer does\'t know about on getPeers', function(done) { 115 | 116 | // peers[3].max =1 117 | 118 | // peers[3].network.on('getPeers', function(peers){ 119 | // peers === 4 120 | // }); 121 | 122 | // peers[3].network.connect(startPort + 1, '0.0.0.0'); 123 | done(); 124 | 125 | }); 126 | 127 | it('should shut down peers', function(done){ 128 | async.each(peers, function(peer, done2){ 129 | peer.stop(done2); 130 | }, done); 131 | }); 132 | 133 | it('the database should shutdown', function(){ 134 | dbServer.kill(); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /test/rpc.js: -------------------------------------------------------------------------------- 1 | const App = require('../'); 2 | const assert = require('assert'); 3 | const Ws = require('ws'); 4 | const Block = require('ethereumjs-lib').Block; 5 | const Account = require('ethereumjs-lib').Account; 6 | const Tx = require('ethereumjs-lib').Transaction; 7 | 8 | const crypto = require('crypto'); 9 | const ethUtil = require('ethereumjs-util'); 10 | const ecdsa = require('secp256k1'); 11 | 12 | 13 | const allotment = { 14 | "a06ef3ed1ce41ade87f764de6ce8095c569d6d57": "1606938044258990275541962092341162602522202993782792835301376", 15 | "e4157b34ea9615cfbde6b4fda419828124b70c78": "1606938044258990275541962092341162602522202993782792835301376", 16 | "b9c015918bdaba24b4ff057a92a3873d6eb201be": "1606938044258990275541962092341162602522202993782792835301376", 17 | "6c386a4b26f73c802f34673f7248bb118f97424a": "1606938044258990275541962092341162602522202993782792835301376", 18 | "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": "1606938044258990275541962092341162602522202993782792835301376", 19 | "2ef47100e0787b915105fd5e3f4ff6752079d5cb": "1606938044258990275541962092341162602522202993782792835301376", 20 | "e6716f9544a56c530d868e4bfbacb172315bdead": "1606938044258990275541962092341162602522202993782792835301376", 21 | "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": "1606938044258990275541962092341162602522202993782792835301376", 22 | "b0afc46d9ce366d06ab4952ca27db1d9557ae9fd": "154162184000000000000000", 23 | "f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0": "102774789000000000000000", 24 | "cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0": "51387394000000000000000", 25 | "b7576e9d314df41ec5506494293afb1bd5d3f65d": "69423399000000000000000" 26 | } 27 | 28 | var settings = { 29 | 'network': false, 30 | 'path': './db/', 31 | 'db': { 32 | 'port': 30305 33 | }, 34 | 'dbServer': true, 35 | 'upnp': false, 36 | 'rpc': { 37 | 'ws': { 38 | 'port': 40401 39 | } 40 | } 41 | }; 42 | 43 | var ws; 44 | var mulAddress; 45 | var privateKey; 46 | var address; 47 | var filterID; 48 | var accountAddress; 49 | 50 | describe('basic app functions', function() { 51 | 52 | it('should start', function(done) { 53 | app = new App(settings); 54 | app.start(done); 55 | }); 56 | 57 | it('should generate genesis', function(done) { 58 | // app.vm.db = db; 59 | app.vm.on('afterTx', function() { 60 | var root = app.vm.trie.root 61 | var block = new Block() 62 | block.header.stateRoot = root 63 | block.header.parentHash = app.blockchain.head.hash() 64 | app.blockchain.addBlock(block, function() { 65 | console.log('added block to blockchain') 66 | }) 67 | }) 68 | 69 | app.vm.generateGenesis(allotment, function() { 70 | var block = new Block(); 71 | block.header.stateRoot = app.vm.trie.root; 72 | done(); 73 | }); 74 | 75 | }); 76 | 77 | it('should connect to the ws rpc', function(done) { 78 | ws = new Ws('ws://localhost:' + settings.rpc.ws.port); 79 | ws.on('open', function open() { 80 | done(); 81 | }); 82 | }); 83 | 84 | it('it should get the peer count', function(done) { 85 | var cmd = { 86 | 'method': 'net_peerCount', 87 | 'params': [], 88 | 'jsonrpc': '2.0', 89 | 'id': 0 90 | }; 91 | ws.send(JSON.stringify(cmd)); 92 | ws.once('message', function(msg) { 93 | msg = JSON.parse(msg); 94 | assert.equal(msg.id, 0); 95 | assert.equal(msg.result, 0); 96 | done(); 97 | }); 98 | }); 99 | 100 | it('it check if it is listening', function(done) { 101 | var cmd = { 102 | 'method': 'net_listening', 103 | 'params': [], 104 | 'jsonrpc': '2.0', 105 | 'id': 20 106 | }; 107 | ws.send(JSON.stringify(cmd)); 108 | ws.once('message', function(msg) { 109 | msg = JSON.parse(msg); 110 | assert.equal(msg.id, 20); 111 | assert.equal(msg.result, false); 112 | done(); 113 | }); 114 | }); 115 | 116 | it('getBalance', function(done) { 117 | var cmd = { 118 | 'method': 'eth_getBalance', 119 | 'params': ['e6716f9544a56c530d868e4bfbacb172315bdead'], 120 | 'jsonrpc': '2.0', 121 | 'id': 1 122 | }; 123 | ws.send(JSON.stringify(cmd)); 124 | ws.once('message', function(msg) { 125 | msg = JSON.parse(msg); 126 | assert.equal(msg.id, 1); 127 | assert.equal(msg.result, '0x0100000000000000000000000000000000000000000000000000'); 128 | done(); 129 | }); 130 | }); 131 | 132 | it('shoud send a transation', function(done) { 133 | 134 | privateKey = crypto.randomBytes(32); 135 | address = ethUtil.pubToAddress(ecdsa.createPublicKey(privateKey)); 136 | var mulContract = '602b80600b60003960365660003560001a60008114156029576001356040526021356060526060516040510260805260206080f35b505b6000f3'; 137 | mulAddress = ethUtil.generateAddress(address, new Buffer([1])); 138 | 139 | function populateTrie(cb) { 140 | var account = new Account(); 141 | account.balance = '0xffffffffffffffffff'; 142 | app.vm.trie.put(address, account.serialize(), cb); 143 | } 144 | 145 | function sendTx() { 146 | var tx = new Tx({ 147 | data: mulContract, 148 | gasLimit: 50000000, 149 | gasPrice: 1, 150 | nonce: 0 151 | }) 152 | 153 | tx.sign(privateKey); 154 | 155 | cmd = { 156 | 'method': 'eth_signedTransact', 157 | 'params': [tx.serialize().toString('hex')], 158 | 'jsonrpc': '2.0', 159 | 'id': 2 160 | } 161 | ws.send(JSON.stringify(cmd)); 162 | } 163 | 164 | populateTrie(sendTx); 165 | 166 | ws.once('message', function(msg) { 167 | msg = JSON.parse(msg); 168 | assert(msg.result === '0x' + mulAddress.toString('hex')) 169 | //check something? 170 | done(); 171 | }); 172 | }); 173 | 174 | it('should make a call', function(done) { 175 | var data = '00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003'; 176 | var cmd = { 177 | 'method': 'eth_call', 178 | 'params': [{ 179 | from: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826', 180 | to: mulAddress.toString('hex'), 181 | data: data, 182 | gas: 99999999999, 183 | gasPrice: 1 184 | }], 185 | 'jsonrpc': '2.0', 186 | 'id': 2 187 | }; 188 | 189 | ws.send(JSON.stringify(cmd)); 190 | ws.once('message', function(msg) { 191 | msg = JSON.parse(msg); 192 | assert(msg.result === '0x0000000000000000000000000000000000000000000000000000000000090000'); 193 | done(); 194 | }); 195 | }); 196 | 197 | 198 | it('should subcribe to a topic', function(done) { 199 | cmd = { 200 | 'method': 'eth_newFilter', 201 | 'params': [{ 202 | "topic": ['0x' + ethUtil.pad(address, 32).toString('hex')] 203 | }], 204 | 'jsonrpc': '2.0', 205 | 'id': 3 206 | } 207 | ws.send(JSON.stringify(cmd)); 208 | ws.once('message', function(msg) { 209 | filterID = JSON.parse(msg).result; 210 | assert(filterID !== null); 211 | done(); 212 | }); 213 | }); 214 | 215 | 216 | it('send a tx that causes a log', function(done) { 217 | 218 | accountAddress = ethUtil.pubToAddress(crypto.randomBytes(32)); 219 | 220 | function populateTrie(cb) { 221 | var account = new Account(); 222 | var code = new Buffer('60ff6000533360206000a1', 'hex'); //some code that does some LOGs 223 | account.balance = 'ffffffffffffff'; 224 | account.storeCode(app.vm.trie, code, function() { 225 | app.vm.trie.put(accountAddress, account.serialize(), cb); 226 | }); 227 | } 228 | 229 | function sendTx() { 230 | var tx = new Tx({ 231 | to: accountAddress, 232 | gasLimit: 100000, 233 | gasPrice: 1, 234 | nonce: 1 235 | }) 236 | 237 | tx.sign(privateKey); 238 | 239 | cmd = { 240 | 'method': 'eth_signedTransact', 241 | 'params': [tx.serialize().toString('hex')], 242 | 'jsonrpc': '2.0', 243 | 'id': 4 244 | } 245 | ws.send(JSON.stringify(cmd)); 246 | } 247 | 248 | ws.once('message', function(msg) { 249 | console.log(msg); 250 | msg = JSON.parse(msg); 251 | done(); 252 | }); 253 | 254 | populateTrie(sendTx); 255 | }); 256 | 257 | 258 | it('should return logs after being pulled', function(done) { 259 | var cmd = { 260 | 'method': 'eth_getFilterChanges', 261 | 'jsonrpc': '2.0', 262 | 'params': [filterID], 263 | 'id': 5 264 | }; 265 | 266 | ws.send(JSON.stringify(cmd)); 267 | 268 | ws.once('message', function(msg) { 269 | msg = JSON.parse(msg); 270 | assert.equal(msg.result.length, 1); 271 | assert.equal(msg.result[0].address, accountAddress.toString('hex'), 'should log correct address'); 272 | var data = 'ff00000000000000000000000000000000000000000000000000000000000000'; 273 | assert.equal(msg.result[0].data, data, 'should log correct data'); 274 | done(); 275 | }); 276 | 277 | }); 278 | 279 | it('eth_getCode', function(done) { 280 | 281 | var cmd = { 282 | 'method': 'eth_getCode', 283 | 'params': [accountAddress.toString('hex')], 284 | 'jsonrpc': '2.0', 285 | 'id': 11 286 | }; 287 | 288 | ws.send(JSON.stringify(cmd)); 289 | 290 | ws.once('message', function(msg) { 291 | msg = JSON.parse(msg); 292 | assert.equal(msg.result, '0x60ff6000533360206000a1', 'should have correct code'); 293 | done(); 294 | }); 295 | }); 296 | 297 | //we need blocks in chain to test 298 | it.skip('eth_getBlockByNumber', function(done) { 299 | 300 | var cmd = { 301 | 'method': 'eth_getBlockByNumber', 302 | 'params': [2], 303 | 'jsonrpc': '2.0', 304 | 'id': 11 305 | }; 306 | 307 | ws.send(JSON.stringify(cmd)); 308 | ws.once('message', function(msg) { 309 | msg = JSON.parse(msg); 310 | assert(msg.status !== 'failed'); 311 | assert.equal(msg.result.header.parentHash, '516dccada94c7dd9936747c6819be3d28f9e91a46f18aada525d036ef09867be'); 312 | done(); 313 | }); 314 | }); 315 | 316 | it('eth_blockNumber', function(done) { 317 | 318 | var cmd = { 319 | 'method': 'eth_blockNumber', 320 | 'jsonrpc': '2.0', 321 | 'id': 11 322 | }; 323 | 324 | ws.send(JSON.stringify(cmd)); 325 | 326 | ws.once('message', function(msg) { 327 | msg = JSON.parse(msg); 328 | assert.equal(msg.result, '0x') 329 | done(); 330 | }); 331 | }); 332 | 333 | it('should stop', function(done) { 334 | app.stop(done); 335 | }); 336 | 337 | }); 338 | -------------------------------------------------------------------------------- /test/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rpc": { 3 | "port": 5555 4 | } 5 | } 6 | --------------------------------------------------------------------------------