Merge branch 'develop' into jsonrpc
This commit is contained in:
		
						commit
						d92fde6980
					
				
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -10,3 +10,8 @@ | ||||
| .DS_Store | ||||
| */**/.DS_Store | ||||
| .ethtest | ||||
| 
 | ||||
| #* | ||||
| .#* | ||||
| *# | ||||
| *~ | ||||
|  | ||||
							
								
								
									
										12
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| Jeffrey Wilcke <jeffrey@ethereum.org> | ||||
| Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com> | ||||
| Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com> | ||||
| Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com> | ||||
| 
 | ||||
| Viktor Trón <viktor.tron@gmail.com> | ||||
| 
 | ||||
| Joseph Goulden <joegoulden@gmail.com> | ||||
| 
 | ||||
| Nick Savers <nicksavers@gmail.com> | ||||
| 
 | ||||
| Maran Hidskes <maran.hidskes@gmail.com> | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -61,6 +64,7 @@ var ( | ||||
| 	ImportChain     string | ||||
| 	SHH             bool | ||||
| 	Dial            bool | ||||
| 	PrintVersion    bool | ||||
| ) | ||||
| 
 | ||||
| // flags specific to cli client
 | ||||
| @ -117,6 +121,7 @@ func Init() { | ||||
| 
 | ||||
| 	flag.BoolVar(&StartMining, "mine", false, "start dagger mining") | ||||
| 	flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console") | ||||
| 	flag.BoolVar(&PrintVersion, "version", false, "prints version number") | ||||
| 
 | ||||
| 	flag.Parse() | ||||
| 
 | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -28,6 +31,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/eth" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -49,6 +53,11 @@ func main() { | ||||
| 	// precedence: code-internal flag default < config file < environment variables < command line
 | ||||
| 	Init() // parsing command line
 | ||||
| 
 | ||||
| 	if PrintVersion { | ||||
| 		printVersion() | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") | ||||
| 
 | ||||
| 	ethereum, err := eth.New(ð.Config{ | ||||
| @ -95,7 +104,8 @@ func main() { | ||||
| 		} | ||||
| 
 | ||||
| 		// Leave the Println. This needs clean output for piping
 | ||||
| 		fmt.Printf("%s\n", block.State().Dump()) | ||||
| 		statedb := state.New(block.Root(), ethereum.Db()) | ||||
| 		fmt.Printf("%s\n", statedb.Dump()) | ||||
| 
 | ||||
| 		fmt.Println(block) | ||||
| 
 | ||||
| @ -134,3 +144,13 @@ func main() { | ||||
| 	// this blocks the thread
 | ||||
| 	ethereum.WaitForShutdown() | ||||
| } | ||||
| 
 | ||||
| func printVersion() { | ||||
| 	fmt.Printf(`%v %v | ||||
| PV=%d | ||||
| GOOS=%s | ||||
| GO=%s | ||||
| GOPATH=%s | ||||
| GOROOT=%s | ||||
| `, ClientIdentifier, Version, eth.ProtocolVersion, runtime.GOOS, runtime.Version(), os.Getenv("GOPATH"), runtime.GOROOT()) | ||||
| } | ||||
|  | ||||
| @ -86,6 +86,11 @@ func (self *JSRepl) Stop() { | ||||
| } | ||||
| 
 | ||||
| func (self *JSRepl) parseInput(code string) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			fmt.Println("[native] error", r) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	value, err := self.re.Run(code) | ||||
| 	if err != nil { | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| /** | ||||
|  * @authors: | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  * @date 2014 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package main | ||||
| @ -26,12 +24,15 @@ package main | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 	"github.com/ethereum/go-ethereum/tests/helper" | ||||
| ) | ||||
| @ -43,8 +44,8 @@ type Account struct { | ||||
| 	Storage map[string]string | ||||
| } | ||||
| 
 | ||||
| func StateObjectFromAccount(addr string, account Account) *state.StateObject { | ||||
| 	obj := state.NewStateObject(ethutil.Hex2Bytes(addr)) | ||||
| func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject { | ||||
| 	obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db) | ||||
| 	obj.SetBalance(ethutil.Big(account.Balance)) | ||||
| 
 | ||||
| 	if ethutil.IsHex(account.Code) { | ||||
| @ -66,19 +67,20 @@ type VmTest struct { | ||||
| 	Pre         map[string]Account | ||||
| } | ||||
| 
 | ||||
| func RunVmTest(js string) (failed int) { | ||||
| func RunVmTest(r io.Reader) (failed int) { | ||||
| 	tests := make(map[string]VmTest) | ||||
| 
 | ||||
| 	data, _ := ioutil.ReadAll(strings.NewReader(js)) | ||||
| 	data, _ := ioutil.ReadAll(r) | ||||
| 	err := json.Unmarshal(data, &tests) | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| 	} | ||||
| 
 | ||||
| 	for name, test := range tests { | ||||
| 		state := state.New(helper.NewTrie()) | ||||
| 		db, _ := ethdb.NewMemDatabase() | ||||
| 		state := state.New(nil, db) | ||||
| 		for addr, account := range test.Pre { | ||||
| 			obj := StateObjectFromAccount(addr, account) | ||||
| 			obj := StateObjectFromAccount(db, addr, account) | ||||
| 			state.SetStateObject(obj) | ||||
| 		} | ||||
| 
 | ||||
| @ -118,6 +120,8 @@ func RunVmTest(js string) (failed int) { | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		logger.Flush() | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| @ -125,9 +129,10 @@ func RunVmTest(js string) (failed int) { | ||||
| 
 | ||||
| func main() { | ||||
| 	helper.Logger.SetLogLevel(5) | ||||
| 	if len(os.Args) == 1 { | ||||
| 		log.Fatalln("no json supplied") | ||||
| 	} | ||||
| 
 | ||||
| 	os.Exit(RunVmTest(os.Args[1])) | ||||
| 	if len(os.Args) > 1 { | ||||
| 		os.Exit(RunVmTest(strings.NewReader(os.Args[1]))) | ||||
| 	} else { | ||||
| 		os.Exit(RunVmTest(os.Stdin)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  * @date 2014 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package main | ||||
| @ -37,7 +35,6 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/ptrie" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 	"github.com/ethereum/go-ethereum/vm" | ||||
| ) | ||||
| @ -65,7 +62,7 @@ func main() { | ||||
| 	ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "") | ||||
| 
 | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	statedb := state.New(ptrie.New(nil, db)) | ||||
| 	statedb := state.New(nil, db) | ||||
| 	sender := statedb.NewStateObject([]byte("sender")) | ||||
| 	receiver := statedb.NewStateObject([]byte("receiver")) | ||||
| 	//receiver.SetCode([]byte(*code))
 | ||||
| @ -133,6 +130,12 @@ func (self *VMEnv) Value() *big.Int       { return self.value } | ||||
| func (self *VMEnv) GasLimit() *big.Int    { return big.NewInt(1000000000) } | ||||
| func (self *VMEnv) Depth() int            { return 0 } | ||||
| func (self *VMEnv) SetDepth(i int)        { self.depth = i } | ||||
| func (self *VMEnv) GetHash(n uint64) []byte { | ||||
| 	if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { | ||||
| 		return self.block.Hash() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func (self *VMEnv) AddLog(log state.Log) { | ||||
| 	self.state.AddLog(log) | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ ApplicationWindow { | ||||
|     property alias dataText: rawDataField.text | ||||
| 
 | ||||
|     onClosing: { | ||||
|         dbg.Stop() | ||||
|         //dbg.Stop() | ||||
|     } | ||||
| 
 | ||||
|     menuBar: MenuBar { | ||||
| @ -353,6 +353,7 @@ ApplicationWindow { | ||||
| 
 | ||||
| 
 | ||||
|         ComboBox { | ||||
| 		visible: false | ||||
|             id: snippets | ||||
|             anchors.right: parent.right | ||||
|             model: ListModel { | ||||
|  | ||||
| @ -1,18 +0,0 @@ | ||||
| # Ethereum JavaScript API | ||||
| 
 | ||||
| This is the Ethereum compatible JavaScript API using `Promise`s | ||||
| which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. | ||||
| 
 | ||||
| For an example see `index.html`. | ||||
| 
 | ||||
| **Please note this repo is in it's early stage.** | ||||
| 
 | ||||
| If you'd like to run a WebSocket ethereum node check out | ||||
| [go-ethereum](https://github.com/ethereum/go-ethereum). | ||||
| 
 | ||||
| To install ethereum and spawn a node: | ||||
| 
 | ||||
| ``` | ||||
| go get github.com/ethereum/go-ethereum/ethereum | ||||
| ethereum -ws -loglevel=4 | ||||
| ``` | ||||
| @ -1,70 +0,0 @@ | ||||
| (function () { | ||||
|     var HttpRpcProvider = function (host) { | ||||
|         this.handlers = []; | ||||
|         this.host = host; | ||||
|     }; | ||||
| 
 | ||||
|     function formatJsonRpcObject(object) { | ||||
|         return { | ||||
|             jsonrpc: '2.0', | ||||
|             method: object.call, | ||||
|             params: object.args, | ||||
|             id: object._id | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     function formatJsonRpcMessage(message) {     | ||||
|         var object = JSON.parse(message); | ||||
|         | ||||
|         return { | ||||
|             _id: object.id, | ||||
|             data: object.result | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     HttpRpcProvider.prototype.sendRequest = function (payload, cb) { | ||||
|         var data = formatJsonRpcObject(payload); | ||||
| 
 | ||||
|         var request = new XMLHttpRequest(); | ||||
|         request.open("POST", this.host, true); | ||||
|         request.send(JSON.stringify(data)); | ||||
|         request.onreadystatechange = function () { | ||||
|             if (request.readyState === 4 && cb) { | ||||
|                 cb(request); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     HttpRpcProvider.prototype.send = function (payload) { | ||||
|         var self = this; | ||||
|         this.sendRequest(payload, function (request) { | ||||
|             self.handlers.forEach(function (handler) { | ||||
|                 handler.call(self, formatJsonRpcMessage(request.responseText)); | ||||
|             }); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     HttpRpcProvider.prototype.poll = function (payload, id) { | ||||
|         var self = this; | ||||
|         this.sendRequest(payload, function (request) { | ||||
|             var parsed = JSON.parse(request.responseText); | ||||
|             if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) { | ||||
|                 return; | ||||
|             } | ||||
|             self.handlers.forEach(function (handler) { | ||||
|                 handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); | ||||
|             }); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { | ||||
|         set: function (handler) { | ||||
|             this.handlers.push(handler); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     if (typeof(web3) !== "undefined" && web3.providers !== undefined) { | ||||
|         web3.providers.HttpRpcProvider = HttpRpcProvider; | ||||
|     } | ||||
| })(); | ||||
| 
 | ||||
| @ -1,33 +0,0 @@ | ||||
| <!doctype> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
| <script type="text/javascript" src="main.js"></script> | ||||
| <script type="text/javascript" src="websocket.js"></script> | ||||
| <script type="text/javascript" src="qt.js"></script> | ||||
| <script type="text/javascript" src="httprpc.js"></script> | ||||
| <script type="text/javascript"> | ||||
| function registerName() { | ||||
|     var name = document.querySelector("#name").value; | ||||
|     name = web3.fromAscii(name); | ||||
| 
 | ||||
|     var eth = web3.eth; | ||||
|     eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) { | ||||
|         document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through."; | ||||
|     }, function(err) { | ||||
|         console.log(err);  | ||||
|     }); | ||||
| } | ||||
| </script> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
| 
 | ||||
| <h1>std::name_reg</h1> | ||||
| <input type="text" id="name"></input> | ||||
| <input type="submit" onClick="registerName();"></input> | ||||
| <div id="result"></div> | ||||
| 
 | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
| @ -1,432 +0,0 @@ | ||||
| (function(window) { | ||||
|     function isPromise(o) { | ||||
|         return o instanceof Promise | ||||
|     } | ||||
| 
 | ||||
|     function flattenPromise (obj) { | ||||
|         if (obj instanceof Promise) { | ||||
|             return Promise.resolve(obj); | ||||
|         } | ||||
| 
 | ||||
|         if (obj instanceof Array) { | ||||
|             return new Promise(function (resolve) { | ||||
|                 var promises = obj.map(function (o) { | ||||
|                     return flattenPromise(o); | ||||
|                 }); | ||||
| 
 | ||||
|                 return Promise.all(promises).then(function (res) { | ||||
|                     for (var i = 0; i < obj.length; i++) { | ||||
|                         obj[i] = res[i]; | ||||
|                     } | ||||
|                     resolve(obj); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|          | ||||
|         if (obj instanceof Object) { | ||||
|             return new Promise(function (resolve) { | ||||
|                 var keys = Object.keys(obj); | ||||
|                 var promises = keys.map(function (key) { | ||||
|                     return flattenPromise(obj[key]); | ||||
|                 }); | ||||
| 
 | ||||
|                 return Promise.all(promises).then(function (res) { | ||||
|                     for (var i = 0; i < keys.length; i++) { | ||||
|                         obj[keys[i]] = res[i]; | ||||
|                     } | ||||
|                     resolve(obj); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return Promise.resolve(obj); | ||||
|     }; | ||||
| 
 | ||||
|     var ethMethods = function () { | ||||
|         var blockCall = function (args) { | ||||
|             return typeof args[0] === "string" ? "blockByHash" : "blockByNumber"; | ||||
|         }; | ||||
| 
 | ||||
|         var transactionCall = function (args) { | ||||
|             return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';    | ||||
|         }; | ||||
| 
 | ||||
|         var uncleCall = function (args) { | ||||
|             return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';        | ||||
|         }; | ||||
| 
 | ||||
|         var methods = [ | ||||
|         { name: 'balanceAt', call: 'balanceAt' }, | ||||
|         { name: 'stateAt', call: 'stateAt' }, | ||||
|         { name: 'countAt', call: 'countAt'}, | ||||
|         { name: 'codeAt', call: 'codeAt' }, | ||||
|         { name: 'transact', call: 'transact' }, | ||||
|         { name: 'call', call: 'call' }, | ||||
|         { name: 'block', call: blockCall }, | ||||
|         { name: 'transaction', call: transactionCall }, | ||||
|         { name: 'uncle', call: uncleCall }, | ||||
|         { name: 'compile', call: 'compile' } | ||||
|         ]; | ||||
|         return methods; | ||||
|     }; | ||||
| 
 | ||||
|     var ethProperties = function () { | ||||
|         return [ | ||||
|         { name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' }, | ||||
|         { name: 'listening', getter: 'listening', setter: 'setListening' }, | ||||
|         { name: 'mining', getter: 'mining', setter: 'setMining' }, | ||||
|         { name: 'gasPrice', getter: 'gasPrice' }, | ||||
|         { name: 'account', getter: 'account' }, | ||||
|         { name: 'accounts', getter: 'accounts' }, | ||||
|         { name: 'peerCount', getter: 'peerCount' }, | ||||
|         { name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' }, | ||||
|         { name: 'number', getter: 'number'} | ||||
|         ]; | ||||
|     }; | ||||
| 
 | ||||
|     var dbMethods = function () { | ||||
|         return [ | ||||
|         { name: 'put', call: 'put' }, | ||||
|         { name: 'get', call: 'get' }, | ||||
|         { name: 'putString', call: 'putString' }, | ||||
|         { name: 'getString', call: 'getString' } | ||||
|         ]; | ||||
|     }; | ||||
| 
 | ||||
|     var shhMethods = function () { | ||||
|         return [ | ||||
|         { name: 'post', call: 'post' }, | ||||
|         { name: 'newIdentity', call: 'newIdentity' }, | ||||
|         { name: 'haveIdentity', call: 'haveIdentity' }, | ||||
|         { name: 'newGroup', call: 'newGroup' }, | ||||
|         { name: 'addToGroup', call: 'addToGroup' } | ||||
|         ]; | ||||
|     }; | ||||
| 
 | ||||
|     var ethWatchMethods = function () { | ||||
|         var newFilter = function (args) { | ||||
|             return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter'; | ||||
|         }; | ||||
| 
 | ||||
|         return [ | ||||
|         { name: 'newFilter', call: newFilter }, | ||||
|         { name: 'uninstallFilter', call: 'uninstallFilter' }, | ||||
|         { name: 'getMessages', call: 'getMessages' } | ||||
|         ]; | ||||
|     }; | ||||
| 
 | ||||
|     var shhWatchMethods = function () { | ||||
|         return [ | ||||
|         { name: 'newFilter', call: 'shhNewFilter' }, | ||||
|         { name: 'uninstallFilter', call: 'shhUninstallFilter' }, | ||||
|         { name: 'getMessage', call: 'shhGetMessages' } | ||||
|         ]; | ||||
|     }; | ||||
| 
 | ||||
|     var setupMethods = function (obj, methods) { | ||||
|         methods.forEach(function (method) { | ||||
|             obj[method.name] = function () { | ||||
|                 return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { | ||||
|                     var call = typeof method.call === "function" ? method.call(args) : method.call;  | ||||
|                     return {call: call, args: args}; | ||||
|                 }).then(function (request) { | ||||
|                     return new Promise(function (resolve, reject) { | ||||
|                         web3.provider.send(request, function (result) { | ||||
|                             //if (result || typeof result === "boolean") {
 | ||||
|                                 resolve(result); | ||||
|                                 return; | ||||
|                             //} 
 | ||||
|                             //reject(result);
 | ||||
|                         }); | ||||
|                     }); | ||||
|                 }).catch(function( err) { | ||||
|                     console.error(err); | ||||
|                 }); | ||||
|             }; | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     var setupProperties = function (obj, properties) { | ||||
|         properties.forEach(function (property) { | ||||
|             var proto = {}; | ||||
|             proto.get = function () { | ||||
|                 return new Promise(function(resolve, reject) { | ||||
|                     web3.provider.send({call: property.getter}, function(result) { | ||||
|                         resolve(result); | ||||
|                     }); | ||||
|                 }); | ||||
|             }; | ||||
|             if (property.setter) { | ||||
|                 proto.set = function (val) { | ||||
|                     return flattenPromise([val]).then(function (args) { | ||||
|                         return new Promise(function (resolve) { | ||||
|                             web3.provider.send({call: property.setter, args: args}, function (result) { | ||||
|                                 resolve(result); | ||||
|                             }); | ||||
|                         }); | ||||
|                     }).catch(function (err) { | ||||
|                         console.error(err); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             Object.defineProperty(obj, property.name, proto); | ||||
|         }); | ||||
|     }; | ||||
|      | ||||
|     var web3 = { | ||||
|         _callbacks: {}, | ||||
|         _events: {}, | ||||
|         providers: {}, | ||||
|         toHex: function(str) { | ||||
|             var hex = ""; | ||||
|             for(var i = 0; i < str.length; i++) { | ||||
|                 var n = str.charCodeAt(i).toString(16); | ||||
|                 hex += n.length < 2 ? '0' + n : n; | ||||
|             } | ||||
| 
 | ||||
|             return hex; | ||||
|         }, | ||||
| 
 | ||||
|         toAscii: function(hex) { | ||||
|             // Find termination
 | ||||
|             var str = ""; | ||||
|             var i = 0, l = hex.length; | ||||
|             for(; i < l; i+=2) { | ||||
|                 var code = hex.charCodeAt(i) | ||||
|                 if(code == 0) { | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); | ||||
|             } | ||||
| 
 | ||||
|             return str; | ||||
|         }, | ||||
| 
 | ||||
|         toDecimal: function (val) { | ||||
|             return parseInt(val, 16);          | ||||
|         }, | ||||
| 
 | ||||
|         fromAscii: function(str, pad) { | ||||
|             pad = pad === undefined ? 32 : pad; | ||||
|             var hex = this.toHex(str); | ||||
|             while(hex.length < pad*2) | ||||
|                 hex += "00"; | ||||
|             return hex | ||||
|         }, | ||||
| 
 | ||||
|         eth: { | ||||
|             prototype: Object(), | ||||
|             watch: function (params) { | ||||
|                 return new Filter(params, ethWatch); | ||||
|             }, | ||||
|         }, | ||||
| 
 | ||||
|         db: { | ||||
|             prototype: Object() | ||||
|         }, | ||||
| 
 | ||||
|         shh: { | ||||
|             prototype: Object(), | ||||
|             watch: function (params) { | ||||
|                 return new Filter(params, shhWatch); | ||||
|             } | ||||
|         }, | ||||
| 
 | ||||
|         on: function(event, id, cb) { | ||||
|             if(web3._events[event] === undefined) { | ||||
|                 web3._events[event] = {}; | ||||
|             } | ||||
| 
 | ||||
|             web3._events[event][id] = cb; | ||||
|             return this | ||||
|         }, | ||||
| 
 | ||||
|         off: function(event, id) { | ||||
|             if(web3._events[event] !== undefined) { | ||||
|                 delete web3._events[event][id]; | ||||
|             } | ||||
| 
 | ||||
|             return this | ||||
|         }, | ||||
| 
 | ||||
|         trigger: function(event, id, data) { | ||||
|             var callbacks = web3._events[event]; | ||||
|             if (!callbacks || !callbacks[id]) { | ||||
|                 return; | ||||
|             } | ||||
|             var cb = callbacks[id]; | ||||
|             cb(data); | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     var eth = web3.eth; | ||||
|     setupMethods(eth, ethMethods()); | ||||
|     setupProperties(eth, ethProperties()); | ||||
|     setupMethods(web3.db, dbMethods()); | ||||
|     setupMethods(web3.shh, shhMethods()); | ||||
| 
 | ||||
|     var ethWatch = { | ||||
|         changed: 'changed' | ||||
|     }; | ||||
|     setupMethods(ethWatch, ethWatchMethods()); | ||||
|     var shhWatch = { | ||||
|         changed: 'shhChanged' | ||||
|     }; | ||||
|     setupMethods(shhWatch, shhWatchMethods()); | ||||
| 
 | ||||
|     var ProviderManager = function() { | ||||
|         this.queued = []; | ||||
|         this.polls = []; | ||||
|         this.ready = false; | ||||
|         this.provider = undefined; | ||||
|         this.id = 1; | ||||
| 
 | ||||
|         var self = this; | ||||
|         var poll = function () { | ||||
|             if (self.provider && self.provider.poll) { | ||||
|                 self.polls.forEach(function (data) { | ||||
|                     data.data._id = self.id;  | ||||
|                     self.id++; | ||||
|                     self.provider.poll(data.data, data.id); | ||||
|                 }); | ||||
|             } | ||||
|             setTimeout(poll, 12000); | ||||
|         }; | ||||
|         poll(); | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.send = function(data, cb) { | ||||
|         data._id = this.id; | ||||
|         if (cb) { | ||||
|             web3._callbacks[data._id] = cb; | ||||
|         } | ||||
| 
 | ||||
|         data.args = data.args || []; | ||||
|         this.id++; | ||||
| 
 | ||||
|         if(this.provider !== undefined) { | ||||
|             this.provider.send(data); | ||||
|         } else { | ||||
|             console.warn("provider is not set"); | ||||
|             this.queued.push(data); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.set = function(provider) { | ||||
|         if(this.provider !== undefined && this.provider.unload !== undefined) { | ||||
|             this.provider.unload(); | ||||
|         } | ||||
| 
 | ||||
|         this.provider = provider; | ||||
|         this.ready = true; | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.sendQueued = function() { | ||||
|         for(var i = 0; this.queued.length; i++) { | ||||
|             // Resend
 | ||||
|             this.send(this.queued[i]); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.installed = function() { | ||||
|         return this.provider !== undefined; | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.startPolling = function (data, pollId) { | ||||
|         if (!this.provider || !this.provider.poll) { | ||||
|             return; | ||||
|         } | ||||
|         this.polls.push({data: data, id: pollId}); | ||||
|     }; | ||||
| 
 | ||||
|     ProviderManager.prototype.stopPolling = function (pollId) { | ||||
|         for (var i = this.polls.length; i--;) { | ||||
|             var poll = this.polls[i]; | ||||
|             if (poll.id === pollId) { | ||||
|                 this.polls.splice(i, 1); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     web3.provider = new ProviderManager(); | ||||
| 
 | ||||
|     web3.setProvider = function(provider) { | ||||
|         provider.onmessage = messageHandler; | ||||
|         web3.provider.set(provider); | ||||
|         web3.provider.sendQueued(); | ||||
|     }; | ||||
| 
 | ||||
|     var Filter = function(options, impl) { | ||||
|         this.impl = impl; | ||||
|         this.callbacks = []; | ||||
| 
 | ||||
|         var self = this;  | ||||
|         this.promise = impl.newFilter(options);  | ||||
|         this.promise.then(function (id) { | ||||
|             self.id = id; | ||||
|             web3.on(impl.changed, id, self.trigger.bind(self)); | ||||
|             web3.provider.startPolling({call: impl.changed, args: [id]}, id); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     Filter.prototype.arrived = function(callback) { | ||||
|         this.changed(callback); | ||||
|     } | ||||
| 
 | ||||
|     Filter.prototype.changed = function(callback) { | ||||
|         var self = this; | ||||
|         this.promise.then(function(id) { | ||||
|             self.callbacks.push(callback); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     Filter.prototype.trigger = function(messages) { | ||||
|         for(var i = 0; i < this.callbacks.length; i++) { | ||||
|             this.callbacks[i].call(this, messages); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     Filter.prototype.uninstall = function() { | ||||
|         var self = this; | ||||
|         this.promise.then(function (id) { | ||||
|             self.impl.uninstallFilter(id); | ||||
|             web3.provider.stopPolling(id); | ||||
|             web3.off(impl.changed, id); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     Filter.prototype.messages = function() { | ||||
|         var self = this;     | ||||
|         return this.promise.then(function (id) { | ||||
|             return self.impl.getMessages(id); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     function messageHandler(data) { | ||||
|         if(data._event !== undefined) { | ||||
|             web3.trigger(data._event, data._id, data.data); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         if(data._id) { | ||||
|             var cb = web3._callbacks[data._id]; | ||||
|             if (cb) { | ||||
|                 cb.call(this, data.data) | ||||
|                 delete web3._callbacks[data._id]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|     // Install default provider
 | ||||
|     if(!web3.provider.installed()) { | ||||
|         var sock = new web3.WebSocket("ws://localhost:40404/eth"); | ||||
| 
 | ||||
|         web3.setProvider(sock); | ||||
|     } | ||||
|     */ | ||||
| 
 | ||||
|     window.web3 = web3; | ||||
| 
 | ||||
| })(this); | ||||
| @ -1,27 +0,0 @@ | ||||
| (function() { | ||||
|     var QtProvider = function() { | ||||
|         this.handlers = []; | ||||
|          | ||||
|         var self = this; | ||||
|         navigator.qt.onmessage = function (message) { | ||||
|             self.handlers.forEach(function (handler) { | ||||
|                 handler.call(self, JSON.parse(message.data)); | ||||
|             }); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     QtProvider.prototype.send = function(payload) { | ||||
|         navigator.qt.postMessage(JSON.stringify(payload)); | ||||
|     }; | ||||
| 
 | ||||
|     Object.defineProperty(QtProvider.prototype, "onmessage", { | ||||
|         set: function(handler) { | ||||
|             this.handlers.push(handler); | ||||
|         }, | ||||
|     });  | ||||
| 
 | ||||
|     if(typeof(web3) !== "undefined" && web3.providers !== undefined) { | ||||
|         web3.providers.QtProvider = QtProvider; | ||||
|     } | ||||
| })(); | ||||
| 
 | ||||
| @ -1,51 +0,0 @@ | ||||
| (function() { | ||||
|     var WebSocketProvider = function(host) { | ||||
|         // onmessage handlers
 | ||||
|         this.handlers = []; | ||||
|         // queue will be filled with messages if send is invoked before the ws is ready
 | ||||
|         this.queued = []; | ||||
|         this.ready = false; | ||||
| 
 | ||||
|         this.ws = new WebSocket(host); | ||||
| 
 | ||||
|         var self = this; | ||||
|         this.ws.onmessage = function(event) { | ||||
|             for(var i = 0; i < self.handlers.length; i++) { | ||||
|                 self.handlers[i].call(self, JSON.parse(event.data), event) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         this.ws.onopen = function() { | ||||
|             self.ready = true; | ||||
| 
 | ||||
|             for(var i = 0; i < self.queued.length; i++) { | ||||
|                 // Resend
 | ||||
|                 self.send(self.queued[i]); | ||||
|             } | ||||
|         }; | ||||
|     }; | ||||
|     WebSocketProvider.prototype.send = function(payload) { | ||||
|         if(this.ready) { | ||||
|             var data = JSON.stringify(payload); | ||||
| 
 | ||||
|             this.ws.send(data); | ||||
|         } else { | ||||
|             this.queued.push(payload); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     WebSocketProvider.prototype.onMessage = function(handler) { | ||||
|         this.handlers.push(handler); | ||||
|     }; | ||||
| 
 | ||||
|     WebSocketProvider.prototype.unload = function() { | ||||
|         this.ws.close(); | ||||
|     }; | ||||
|     Object.defineProperty(WebSocketProvider.prototype, "onmessage", { | ||||
|         set: function(provider) { this.onMessage(provider); } | ||||
|     }); | ||||
| 
 | ||||
|     if(typeof(web3) !== "undefined" && web3.providers !== undefined) { | ||||
|         web3.providers.WebSocketProvider = WebSocketProvider; | ||||
|     } | ||||
| })(); | ||||
| @ -1,312 +0,0 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| 
 | ||||
| // Main Ethereum library
 | ||||
| window.eth = { | ||||
| 	prototype: Object(), | ||||
| 	_callbacks: {}, | ||||
| 	_onCallbacks: {}, | ||||
| 
 | ||||
|     test: function() { | ||||
|         var t = undefined; | ||||
| 	postData({call: "test"}) | ||||
|         navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; } | ||||
|         for(;;) { | ||||
|             if(t !== undefined) { | ||||
|                 return t | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
| 	mutan: function(code, cb) { | ||||
| 		postData({call: "mutan", args: [code]}, cb) | ||||
| 	}, | ||||
| 
 | ||||
| 	toHex: function(str) { | ||||
| 		var hex = ""; | ||||
| 		for(var i = 0; i < str.length; i++) { | ||||
| 			var n = str.charCodeAt(i).toString(16); | ||||
| 			hex += n.length < 2 ? '0' + n : n; | ||||
| 		} | ||||
| 
 | ||||
| 		return hex; | ||||
| 	}, | ||||
| 
 | ||||
| 	toAscii: function(hex) { | ||||
| 		// Find termination
 | ||||
| 		var str = ""; | ||||
| 		var i = 0, l = hex.length; | ||||
| 		for(; i < l; i+=2) { | ||||
| 			var code = hex.charCodeAt(i) | ||||
| 			if(code == 0) { | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); | ||||
| 		} | ||||
| 
 | ||||
| 		return str; | ||||
| 	}, | ||||
| 
 | ||||
| 	fromAscii: function(str, pad) { | ||||
| 		if(pad === undefined) { | ||||
| 			pad = 32 | ||||
| 		} | ||||
| 
 | ||||
| 		var hex = this.toHex(str); | ||||
| 
 | ||||
| 		while(hex.length < pad*2) | ||||
| 			hex += "00"; | ||||
| 
 | ||||
| 		return hex | ||||
| 	}, | ||||
| 
 | ||||
| 
 | ||||
| 	// Retrieve block
 | ||||
| 	//
 | ||||
| 	// Either supply a number or a string. Type is determent for the lookup method
 | ||||
| 	// string - Retrieves the block by looking up the hash
 | ||||
| 	// number - Retrieves the block by looking up the block number
 | ||||
| 	getBlock: function(numberOrHash, cb) { | ||||
| 		var func; | ||||
| 		if(typeof numberOrHash == "string") { | ||||
| 			func =  "getBlockByHash"; | ||||
| 		} else { | ||||
| 			func =  "getBlockByNumber"; | ||||
| 		} | ||||
| 		postData({call: func, args: [numberOrHash]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	// Create transaction
 | ||||
| 	//
 | ||||
| 	// Transact between two state objects
 | ||||
| 	transact: function(params, cb) { | ||||
| 		if(params === undefined) { | ||||
| 			params = {}; | ||||
| 		} | ||||
| 
 | ||||
| 		if(params.endowment !== undefined) | ||||
| 			params.value = params.endowment; | ||||
| 		if(params.code !== undefined) | ||||
| 			params.data = params.code; | ||||
| 
 | ||||
| 		// Make sure everything is string
 | ||||
| 		var fields = ["to", "from", "value", "gas", "gasPrice"]; | ||||
| 		for(var i = 0; i < fields.length; i++) { | ||||
| 			if(params[fields[i]] === undefined) { | ||||
| 				params[fields[i]] = ""; | ||||
| 			} | ||||
| 			params[fields[i]] = params[fields[i]].toString(); | ||||
| 		} | ||||
| 
 | ||||
| 		var data; | ||||
| 		if(typeof params.data === "object") { | ||||
| 			data = ""; | ||||
| 			for(var i = 0; i < params.data.length; i++) { | ||||
| 				data += params.data[i] | ||||
| 			} | ||||
| 		} else { | ||||
| 			data = params.data; | ||||
| 		} | ||||
| 
 | ||||
| 		postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getMessages: function(filter, cb) { | ||||
| 		postData({call: "messages", args: [filter]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getStorageAt: function(address, storageAddress, cb) { | ||||
| 		postData({call: "getStorage", args: [address, storageAddress]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getEachStorageAt: function(address, cb){ | ||||
| 		postData({call: "getEachStorage", args: [address]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getKey: function(cb) { | ||||
| 		postData({call: "getKey"}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getTxCountAt: function(address, cb) { | ||||
| 		postData({call: "getTxCountAt", args: [address]}, cb); | ||||
| 	}, | ||||
| 	getIsMining: function(cb){ | ||||
| 		postData({call: "getIsMining"}, cb) | ||||
| 	}, | ||||
| 	getIsListening: function(cb){ | ||||
| 		postData({call: "getIsListening"}, cb) | ||||
| 	}, | ||||
| 	getCoinBase: function(cb){ | ||||
| 		postData({call: "getCoinBase"}, cb); | ||||
| 	}, | ||||
| 	getPeerCount: function(cb){ | ||||
| 		postData({call: "getPeerCount"}, cb); | ||||
| 	}, | ||||
| 	getBalanceAt: function(address, cb) { | ||||
| 		postData({call: "getBalance", args: [address]}, cb); | ||||
| 	}, | ||||
| 	getTransactionsFor: function(address, cb) { | ||||
| 		postData({call: "getTransactionsFor", args: [address]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	getSecretToAddress: function(sec, cb) { | ||||
| 		postData({call: "getSecretToAddress", args: [sec]}, cb); | ||||
| 	}, | ||||
| 
 | ||||
| 	/* | ||||
| 	watch: function(address, storageAddrOrCb, cb) { | ||||
| 		var ev; | ||||
| 		if(cb === undefined) { | ||||
| 			cb = storageAddrOrCb; | ||||
| 			storageAddrOrCb = ""; | ||||
| 			ev = "object:"+address; | ||||
| 		} else { | ||||
| 			ev = "storage:"+address+":"+storageAddrOrCb; | ||||
| 		} | ||||
| 
 | ||||
| 		eth.on(ev, cb) | ||||
| 
 | ||||
| 		postData({call: "watch", args: [address, storageAddrOrCb]}); | ||||
| 	}, | ||||
| 
 | ||||
| 	disconnect: function(address, storageAddrOrCb, cb) { | ||||
| 		var ev; | ||||
| 		if(cb === undefined) { | ||||
| 			cb = storageAddrOrCb; | ||||
| 			storageAddrOrCb = ""; | ||||
| 			ev = "object:"+address; | ||||
| 		} else { | ||||
| 			ev = "storage:"+address+":"+storageAddrOrCb; | ||||
| 		} | ||||
| 
 | ||||
| 		eth.off(ev, cb) | ||||
| 
 | ||||
| 		postData({call: "disconnect", args: [address, storageAddrOrCb]}); | ||||
| 	}, | ||||
| 	*/ | ||||
| 
 | ||||
|        watch: function(options) { | ||||
| 	       var filter = new Filter(options); | ||||
| 	       filter.number = newWatchNum().toString() | ||||
| 
 | ||||
| 	       postData({call: "watch", args: [options, filter.number]}) | ||||
| 
 | ||||
| 	       return filter; | ||||
|        }, | ||||
| 
 | ||||
| 	set: function(props) { | ||||
| 		postData({call: "set", args: props}); | ||||
| 	}, | ||||
| 
 | ||||
| 	on: function(event, cb) { | ||||
| 		if(eth._onCallbacks[event] === undefined) { | ||||
| 			eth._onCallbacks[event] = []; | ||||
| 		} | ||||
| 
 | ||||
| 		eth._onCallbacks[event].push(cb); | ||||
| 
 | ||||
| 		return this | ||||
| 	}, | ||||
| 
 | ||||
| 	off: function(event, cb) { | ||||
| 		if(eth._onCallbacks[event] !== undefined) { | ||||
| 			var callbacks = eth._onCallbacks[event]; | ||||
| 			for(var i = 0; i < callbacks.length; i++) { | ||||
| 				if(callbacks[i] === cb) { | ||||
| 					delete callbacks[i]; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return this | ||||
| 	}, | ||||
| 
 | ||||
| 	trigger: function(event, data) { | ||||
| 		var callbacks = eth._onCallbacks[event]; | ||||
| 		if(callbacks !== undefined) { | ||||
| 			for(var i = 0; i < callbacks.length; i++) { | ||||
| 				// Figure out whether the returned data was an array
 | ||||
| 				// array means multiple return arguments (multiple params)
 | ||||
| 				if(data instanceof Array) { | ||||
| 					callbacks[i].apply(this, data); | ||||
| 				} else { | ||||
| 					callbacks[i].call(this, data); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| var Filter = function(options) { | ||||
| 	this.options = options; | ||||
| }; | ||||
| Filter.prototype.changed = function(callback) { | ||||
| 	// Register the watched:<number>. Qml will call the appropriate event if anything
 | ||||
| 	// interesting happens in the land of Go.
 | ||||
| 	eth.on("watched:"+this.number, callback) | ||||
| } | ||||
| Filter.prototype.getMessages = function(cb) { | ||||
| 	return eth.getMessages(this.options, cb) | ||||
| } | ||||
| 
 | ||||
| var watchNum = 0; | ||||
| function newWatchNum() { | ||||
| 	return watchNum++; | ||||
| } | ||||
| 
 | ||||
| function postData(data, cb) { | ||||
| 	data._seed = Math.floor(Math.random() * 1000000) | ||||
| 	if(cb) { | ||||
| 		eth._callbacks[data._seed] = cb; | ||||
| 	} | ||||
| 
 | ||||
| 	if(data.args === undefined) { | ||||
| 		data.args = []; | ||||
| 	} | ||||
| 
 | ||||
| 	navigator.qt.postMessage(JSON.stringify(data)); | ||||
| } | ||||
| 
 | ||||
| navigator.qt.onmessage = function(ev) { | ||||
| 	var data = JSON.parse(ev.data) | ||||
| 
 | ||||
| 	if(data._event !== undefined) { | ||||
| 		eth.trigger(data._event, data.data); | ||||
| 	} else { | ||||
| 		if(data._seed) { | ||||
| 			var cb = eth._callbacks[data._seed]; | ||||
| 			if(cb) { | ||||
| 				cb.call(this, data.data) | ||||
| 
 | ||||
| 				// Remove the "trigger" callback
 | ||||
| 				delete eth._callbacks[ev._seed]; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| eth.on("chain:changed", function() { | ||||
| }) | ||||
| 
 | ||||
| eth.on("messages", { /* filters */}, function(messages){ | ||||
| }) | ||||
| 
 | ||||
| eth.on("pending:changed", function() { | ||||
| }) | ||||
| 
 | ||||
							
								
								
									
										5
									
								
								cmd/mist/assets/ext/ethereum.js/.bowerrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cmd/mist/assets/ext/ethereum.js/.bowerrc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| { | ||||
|   "directory": "example/js/", | ||||
|   "cwd": "./", | ||||
|   "analytics": false | ||||
| } | ||||
							
								
								
									
										12
									
								
								cmd/mist/assets/ext/ethereum.js/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								cmd/mist/assets/ext/ethereum.js/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| root = true | ||||
| 
 | ||||
| [*] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| end_of_line = lf | ||||
| charset = utf-8 | ||||
| trim_trailing_whitespace = true | ||||
| insert_final_newline = true | ||||
| 
 | ||||
| [*.md] | ||||
| trim_trailing_whitespace = false | ||||
| @ -4,6 +4,7 @@ | ||||
| # or operating system, you probably want to add a global ignore instead: | ||||
| #   git config --global core.excludesfile ~/.gitignore_global | ||||
| 
 | ||||
| *.swp | ||||
| /tmp | ||||
| */**/*un~ | ||||
| *un~ | ||||
| @ -11,4 +12,7 @@ | ||||
| */**/.DS_Store | ||||
| ethereum/ethereum | ||||
| ethereal/ethereal | ||||
| 
 | ||||
| example/js | ||||
| node_modules | ||||
| bower_components | ||||
| npm-debug.log | ||||
							
								
								
									
										50
									
								
								cmd/mist/assets/ext/ethereum.js/.jshintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								cmd/mist/assets/ext/ethereum.js/.jshintrc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| { | ||||
|     "predef": [ | ||||
|         "console", | ||||
|         "require", | ||||
|         "equal", | ||||
|         "test", | ||||
|         "testBoth", | ||||
|         "testWithDefault", | ||||
|         "raises", | ||||
|         "deepEqual", | ||||
|         "start", | ||||
|         "stop", | ||||
|         "ok", | ||||
|         "strictEqual", | ||||
|         "module", | ||||
|         "expect", | ||||
|         "reject", | ||||
|         "impl" | ||||
|     ], | ||||
| 
 | ||||
|     "esnext": true, | ||||
|     "proto": true, | ||||
|     "node" : true, | ||||
|     "browser" : true, | ||||
|     "browserify" : true, | ||||
| 
 | ||||
|     "boss" : true, | ||||
|     "curly": false, | ||||
|     "debug": true, | ||||
|     "devel": true, | ||||
|     "eqeqeq": true, | ||||
|     "evil": true, | ||||
|     "forin": false, | ||||
|     "immed": false, | ||||
|     "laxbreak": false, | ||||
|     "newcap": true, | ||||
|     "noarg": true, | ||||
|     "noempty": false, | ||||
|     "nonew": false, | ||||
|     "nomen": false, | ||||
|     "onevar": false, | ||||
|     "plusplus": false, | ||||
|     "regexp": false, | ||||
|     "undef": true, | ||||
|     "sub": true, | ||||
|     "strict": false, | ||||
|     "white": false, | ||||
|     "shadow": true, | ||||
|     "eqnull": true | ||||
| } | ||||
							
								
								
									
										9
									
								
								cmd/mist/assets/ext/ethereum.js/.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cmd/mist/assets/ext/ethereum.js/.npmignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| example/js | ||||
| node_modules | ||||
| test | ||||
| .gitignore | ||||
| .editorconfig | ||||
| .travis.yml | ||||
| .npmignore | ||||
| component.json | ||||
| testling.html | ||||
							
								
								
									
										11
									
								
								cmd/mist/assets/ext/ethereum.js/.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								cmd/mist/assets/ext/ethereum.js/.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| language: node_js | ||||
| node_js: | ||||
|   - "0.11" | ||||
|   - "0.10" | ||||
| before_script: | ||||
|   - npm install | ||||
|   - npm install jshint | ||||
| script: | ||||
|    - "jshint *.js lib" | ||||
| after_script: | ||||
|   - npm run-script gulp | ||||
							
								
								
									
										14
									
								
								cmd/mist/assets/ext/ethereum.js/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								cmd/mist/assets/ext/ethereum.js/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| This file is part of ethereum.js. | ||||
| 
 | ||||
| ethereum.js is free software: you can redistribute it and/or modify | ||||
| it under the terms of the GNU Lesser General Public License as published by | ||||
| the Free Software Foundation, either version 3 of the License, or | ||||
| (at your option) any later version. | ||||
| 
 | ||||
| ethereum.js is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| GNU Lesser General Public License for more details. | ||||
| 
 | ||||
| You should have received a copy of the GNU Lesser General Public License | ||||
| along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>. | ||||
							
								
								
									
										79
									
								
								cmd/mist/assets/ext/ethereum.js/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								cmd/mist/assets/ext/ethereum.js/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| # Ethereum JavaScript API | ||||
| 
 | ||||
| This is the Ethereum compatible JavaScript API using `Promise`s | ||||
| which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js | ||||
| 
 | ||||
| [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url] | ||||
| 
 | ||||
| <!-- [](https://ci.testling.com/ethereum/ethereum.js) --> | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| ### Node.js | ||||
| 
 | ||||
|     npm install ethereum.js | ||||
| 
 | ||||
| ### For browser | ||||
| Bower | ||||
| 
 | ||||
| 	bower install ethereum.js | ||||
| 
 | ||||
| Component | ||||
| 
 | ||||
| 	component install ethereum/ethereum.js | ||||
| 
 | ||||
| * Include `ethereum.min.js` in your html file. | ||||
| * Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6. | ||||
| 
 | ||||
| ## Usage | ||||
| Require the library: | ||||
| 
 | ||||
| 	var web3 = require('web3'); | ||||
| 
 | ||||
| Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider) | ||||
| 
 | ||||
| 	var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth')); | ||||
| 
 | ||||
| There you go, now you can use it: | ||||
| 
 | ||||
| ``` | ||||
| web3.eth.coinbase.then(function(result){ | ||||
|   console.log(result); | ||||
|   return web3.eth.balanceAt(result); | ||||
| }).then(function(balance){ | ||||
|   console.log(web3.toDecimal(balance)); | ||||
| }).catch(function(err){ | ||||
|   console.log(err); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| For another example see `example/index.html`. | ||||
| 
 | ||||
| ## Building | ||||
| 
 | ||||
| * `gulp build` | ||||
| 
 | ||||
| 
 | ||||
| ### Testing | ||||
| 
 | ||||
| **Please note this repo is in it's early stage.** | ||||
| 
 | ||||
| If you'd like to run a WebSocket ethereum node check out | ||||
| [go-ethereum](https://github.com/ethereum/go-ethereum). | ||||
| 
 | ||||
| To install ethereum and spawn a node: | ||||
| 
 | ||||
| ``` | ||||
| go get github.com/ethereum/go-ethereum/ethereum | ||||
| ethereum -ws -loglevel=4 | ||||
| ``` | ||||
| 
 | ||||
| [npm-image]: https://badge.fury.io/js/ethereum.js.png | ||||
| [npm-url]: https://npmjs.org/package/ethereum.js | ||||
| [travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg | ||||
| [travis-url]: https://travis-ci.org/ethereum/ethereum.js | ||||
| [dep-image]: https://david-dm.org/ethereum/ethereum.js.svg | ||||
| [dep-url]: https://david-dm.org/ethereum/ethereum.js | ||||
| [dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg | ||||
| [dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies | ||||
							
								
								
									
										51
									
								
								cmd/mist/assets/ext/ethereum.js/bower.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								cmd/mist/assets/ext/ethereum.js/bower.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| { | ||||
|   "name": "ethereum.js", | ||||
|   "namespace": "ethereum", | ||||
|   "version": "0.0.3", | ||||
|   "description": "Ethereum Compatible JavaScript API", | ||||
|   "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], | ||||
|   "dependencies": { | ||||
|     "es6-promise": "#master" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://github.com/ethereum/ethereum.js.git" | ||||
|   }, | ||||
|   "homepage": "https://github.com/ethereum/ethereum.js", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/ethereum/ethereum.js/issues" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "ethereum", | ||||
|     "javascript", | ||||
|     "API" | ||||
|   ], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "Marek Kotewicz", | ||||
|       "email": "marek@ethdev.com", | ||||
|       "homepage": "https://github.com/debris" | ||||
|     }, | ||||
|     { | ||||
|       "name": "Marian Oancea", | ||||
|       "email": "marian@ethdev.com", | ||||
|       "homepage": "https://github.com/cubedro" | ||||
|     } | ||||
|   ], | ||||
|   "license": "LGPL-3.0", | ||||
|   "ignore": [ | ||||
|     "example", | ||||
|     "lib", | ||||
|     "node_modules", | ||||
|     "package.json", | ||||
|     ".bowerrc", | ||||
|     ".editorconfig", | ||||
|     ".gitignore", | ||||
|     ".jshintrc", | ||||
|     ".npmignore", | ||||
|     ".travis.yml", | ||||
|     "gulpfile.js", | ||||
|     "index.js", | ||||
|     "**/*.txt" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										1184
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1184
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										29
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cmd/mist/assets/ext/ethereum.js/dist/ethereum.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										41
									
								
								cmd/mist/assets/ext/ethereum.js/example/balance.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cmd/mist/assets/ext/ethereum.js/example/balance.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| <!doctype> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
| <script type="text/javascript" src="js/es6-promise/promise.min.js"></script> | ||||
| <script type="text/javascript" src="../dist/ethereum.js"></script> | ||||
| <script type="text/javascript"> | ||||
|     | ||||
|     var web3 = require('web3'); | ||||
|     web3.setProvider(new web3.providers.AutoProvider()); | ||||
| 
 | ||||
|     function watchBalance() { | ||||
|         var coinbase = web3.eth.coinbase; | ||||
|         var originalBalance = 0; | ||||
| 
 | ||||
|         web3.eth.balanceAt(coinbase).then(function (balance) { | ||||
|             originalBalance = web3.toDecimal(balance); | ||||
|             document.getElementById('original').innerText = 'original balance: ' + originalBalance + '    watching...'; | ||||
|         }); | ||||
| 
 | ||||
|         web3.eth.watch({altered: coinbase}).changed(function() { | ||||
|             web3.eth.balanceAt(coinbase).then(function (balance) { | ||||
|                 var currentBalance = web3.toDecimal(balance); | ||||
|                 document.getElementById("current").innerText = 'current: ' + currentBalance; | ||||
|                 document.getElementById("diff").innerText = 'diff:    ' + (currentBalance - originalBalance); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| </script> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>coinbase balance</h1> | ||||
|     <button type="button" onClick="watchBalance();">watch balance</button> | ||||
|     <div></div> | ||||
|     <div id="original"></div> | ||||
|     <div id="current"></div> | ||||
|     <div id="diff"></div> | ||||
| </body> | ||||
| </html> | ||||
| 
 | ||||
							
								
								
									
										75
									
								
								cmd/mist/assets/ext/ethereum.js/example/contract.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								cmd/mist/assets/ext/ethereum.js/example/contract.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| <!doctype> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
| <script type="text/javascript" src="js/es6-promise/promise.min.js"></script> | ||||
| <script type="text/javascript" src="../dist/ethereum.js"></script> | ||||
| <script type="text/javascript"> | ||||
| 
 | ||||
|     var web3 = require('web3'); | ||||
|     web3.setProvider(new web3.providers.AutoProvider()); | ||||
| 
 | ||||
|     // solidity source code | ||||
|     var source = "" +  | ||||
|     "contract test {\n" + | ||||
|     "   function multiply(uint a) returns(uint d) {\n" + | ||||
|     "       return a * 7;\n" + | ||||
|     "   }\n" + | ||||
|     "}\n"; | ||||
| 
 | ||||
|     // contract description, this will be autogenerated somehow | ||||
|     var desc =  [{ | ||||
|         "name": "multiply", | ||||
|         "inputs": [ | ||||
|         { | ||||
|             "name": "a", | ||||
|             "type": "uint256" | ||||
|         } | ||||
|         ], | ||||
|         "outputs": [ | ||||
|         { | ||||
|             "name": "d", | ||||
|             "type": "uint256" | ||||
|         } | ||||
|         ] | ||||
|     }]; | ||||
| 
 | ||||
|     var contract; | ||||
| 
 | ||||
|     function createExampleContract() { | ||||
|         // hide create button | ||||
|         document.getElementById('create').style.visibility = 'hidden';  | ||||
|         document.getElementById('source').innerText = source; | ||||
| 
 | ||||
|         // create contract | ||||
|         web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) { | ||||
|             contract = web3.contract(address, desc); | ||||
|             document.getElementById('call').style.visibility = 'visible'; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function callExampleContract() { | ||||
|         // this should be generated by ethereum | ||||
|         var param = parseInt(document.getElementById('value').value); | ||||
| 
 | ||||
|         // call the contract | ||||
|         contract.multiply(param).call().then(function(res) { | ||||
|             document.getElementById('result').innerText = res[0]; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| </script> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>contract</h1> | ||||
|     <div id="source"></div>  | ||||
|     <div id='create'> | ||||
|         <button type="button" onClick="createExampleContract();">create example contract</button> | ||||
|     </div> | ||||
|     <div id='call' style='visibility: hidden;'> | ||||
|         <input type="number" id="value" onkeyup='callExampleContract()'></input> | ||||
|     </div> | ||||
|     <div id="result"></div> | ||||
| </body> | ||||
| </html> | ||||
| 
 | ||||
							
								
								
									
										16
									
								
								cmd/mist/assets/ext/ethereum.js/example/node-app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								cmd/mist/assets/ext/ethereum.js/example/node-app.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 
 | ||||
| require('es6-promise').polyfill(); | ||||
| 
 | ||||
| var web3 = require("../index.js"); | ||||
| 
 | ||||
| web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080')); | ||||
| 
 | ||||
| web3.eth.coinbase.then(function(result){ | ||||
|   console.log(result); | ||||
|   return web3.eth.balanceAt(result); | ||||
| }).then(function(balance){ | ||||
|   console.log(web3.toDecimal(balance)); | ||||
| }).catch(function(err){ | ||||
|   console.log(err); | ||||
| }); | ||||
							
								
								
									
										104
									
								
								cmd/mist/assets/ext/ethereum.js/gulpfile.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								cmd/mist/assets/ext/ethereum.js/gulpfile.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var path = require('path'); | ||||
| 
 | ||||
| var del = require('del'); | ||||
| var gulp = require('gulp'); | ||||
| var browserify = require('browserify'); | ||||
| var jshint = require('gulp-jshint'); | ||||
| var uglify = require('gulp-uglify'); | ||||
| var rename = require('gulp-rename'); | ||||
| var envify = require('envify/custom'); | ||||
| var unreach = require('unreachable-branch-transform'); | ||||
| var source = require('vinyl-source-stream'); | ||||
| var exorcist = require('exorcist'); | ||||
| var bower = require('bower'); | ||||
| 
 | ||||
| var DEST = './dist/'; | ||||
| 
 | ||||
| var build = function(src, dst, ugly) { | ||||
|   var result = browserify({ | ||||
|       debug: true, | ||||
|       insert_global_vars: false, | ||||
|       detectGlobals: false, | ||||
|       bundleExternal: false | ||||
|     }) | ||||
|     .require('./' + src + '.js', {expose: 'web3'}) | ||||
|     .add('./' + src + '.js') | ||||
|     .transform('envify', { | ||||
|       NODE_ENV: 'build' | ||||
|     }) | ||||
|     .transform('unreachable-branch-transform'); | ||||
| 
 | ||||
|     if (ugly) { | ||||
|       result = result.transform('uglifyify', { | ||||
|         mangle: false, | ||||
|         compress: { | ||||
|           dead_code: false, | ||||
|           conditionals: true, | ||||
|           unused: false, | ||||
|           hoist_funs: true, | ||||
|           hoist_vars: true, | ||||
|           negate_iife: false | ||||
|         }, | ||||
|         beautify: true, | ||||
|         warnings: true | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     return result.bundle() | ||||
|     .pipe(exorcist(path.join( DEST, dst + '.js.map'))) | ||||
|     .pipe(source(dst + '.js')) | ||||
|     .pipe(gulp.dest( DEST )); | ||||
| }; | ||||
| 
 | ||||
| var uglifyFile = function(file) { | ||||
|   return gulp.src( DEST + file + '.js') | ||||
|     .pipe(uglify()) | ||||
|     .pipe(rename(file + '.min.js')) | ||||
|     .pipe(gulp.dest( DEST )); | ||||
| }; | ||||
| 
 | ||||
| gulp.task('bower', function(cb){ | ||||
|   bower.commands.install().on('end', function (installed){ | ||||
|     console.log(installed); | ||||
|     cb(); | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('clean', ['lint'], function(cb) { | ||||
|   del([ DEST ], cb); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('lint', function(){ | ||||
|   return gulp.src(['./*.js', './lib/*.js']) | ||||
|     .pipe(jshint()) | ||||
|     .pipe(jshint.reporter('default')); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('build', ['clean'], function () { | ||||
|     return build('index', 'ethereum', true); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('buildDev', ['clean'], function () { | ||||
|     return build('index', 'ethereum', false); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('uglify', ['build'], function(){ | ||||
|     return uglifyFile('ethereum'); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('uglify', ['buildDev'], function(){ | ||||
|     return uglifyFile('ethereum'); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('watch', function() { | ||||
|   gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']); | ||||
| }); | ||||
| 
 | ||||
| gulp.task('release', ['bower', 'lint', 'build', 'uglify']); | ||||
| gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']); | ||||
| gulp.task('default', ['dev']); | ||||
| 
 | ||||
							
								
								
									
										8
									
								
								cmd/mist/assets/ext/ethereum.js/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								cmd/mist/assets/ext/ethereum.js/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| var web3 = require('./lib/web3'); | ||||
| web3.providers.WebSocketProvider = require('./lib/websocket'); | ||||
| web3.providers.HttpRpcProvider = require('./lib/httprpc'); | ||||
| web3.providers.QtProvider = require('./lib/qt'); | ||||
| web3.providers.AutoProvider = require('./lib/autoprovider'); | ||||
| web3.contract = require('./lib/contract'); | ||||
| 
 | ||||
| module.exports = web3; | ||||
							
								
								
									
										267
									
								
								cmd/mist/assets/ext/ethereum.js/lib/abi.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								cmd/mist/assets/ext/ethereum.js/lib/abi.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,267 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file abi.js | ||||
|  * @authors: | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  *   Gav Wood <g@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| // TODO: is these line is supposed to be here? 
 | ||||
| if (process.env.NODE_ENV !== 'build') { | ||||
|     var web3 = require('./web3'); // jshint ignore:line
 | ||||
| } | ||||
| 
 | ||||
| // TODO: make these be actually accurate instead of falling back onto JS's doubles.
 | ||||
| var hexToDec = function (hex) { | ||||
|     return parseInt(hex, 16).toString(); | ||||
| }; | ||||
| 
 | ||||
| var decToHex = function (dec) { | ||||
|     return parseInt(dec).toString(16); | ||||
| }; | ||||
| 
 | ||||
| var findIndex = function (array, callback) { | ||||
|     var end = false; | ||||
|     var i = 0; | ||||
|     for (; i < array.length && !end; i++) { | ||||
|         end = callback(array[i]); | ||||
|     } | ||||
|     return end ? i - 1 : -1; | ||||
| }; | ||||
| 
 | ||||
| var findMethodIndex = function (json, methodName) { | ||||
|     return findIndex(json, function (method) { | ||||
|         return method.name === methodName; | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| var padLeft = function (string, chars) { | ||||
|     return new Array(chars - string.length + 1).join("0") + string; | ||||
| }; | ||||
| 
 | ||||
| var calcBitPadding = function (type, expected) { | ||||
|     var value = type.slice(expected.length); | ||||
|     if (value === "") { | ||||
|         return 32; | ||||
|     } | ||||
|     return parseInt(value) / 8; | ||||
| }; | ||||
| 
 | ||||
| var calcBytePadding = function (type, expected) { | ||||
|     var value = type.slice(expected.length); | ||||
|     if (value === "") { | ||||
|         return 32; | ||||
|     } | ||||
|     return parseInt(value); | ||||
| }; | ||||
| 
 | ||||
| var calcRealPadding = function (type, expected) { | ||||
|     var value = type.slice(expected.length); | ||||
|     if (value === "") { | ||||
|         return 32; | ||||
|     } | ||||
|     var sizes = value.split('x'); | ||||
|     for (var padding = 0, i = 0; i < sizes; i++) { | ||||
|         padding += (sizes[i] / 8); | ||||
|     } | ||||
|     return padding; | ||||
| }; | ||||
| 
 | ||||
| var setupInputTypes = function () { | ||||
|      | ||||
|     var prefixedType = function (prefix, calcPadding) { | ||||
|         return function (type, value) { | ||||
|             var expected = prefix; | ||||
|             if (type.indexOf(expected) !== 0) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             var padding = calcPadding(type, expected); | ||||
|             if (typeof value === "number") | ||||
|                 value = value.toString(16); | ||||
|             else if (typeof value === "string") | ||||
|                 value = web3.toHex(value);  | ||||
|             else if (value.indexOf('0x') === 0) | ||||
|                 value = value.substr(2); | ||||
|             else | ||||
|                 value = (+value).toString(16); | ||||
|             return padLeft(value, padding * 2); | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     var namedType = function (name, padding, formatter) { | ||||
|         return function (type, value) { | ||||
|             if (type !== name) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             return padLeft(formatter ? formatter(value) : value, padding * 2); | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     var formatBool = function (value) { | ||||
|         return value ? '0x1' : '0x0'; | ||||
|     }; | ||||
| 
 | ||||
|     return [ | ||||
|         prefixedType('uint', calcBitPadding), | ||||
|         prefixedType('int', calcBitPadding), | ||||
|         prefixedType('hash', calcBitPadding), | ||||
|         prefixedType('string', calcBytePadding), | ||||
|         prefixedType('real', calcRealPadding), | ||||
|         prefixedType('ureal', calcRealPadding), | ||||
|         namedType('address', 20), | ||||
|         namedType('bool', 1, formatBool), | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var inputTypes = setupInputTypes(); | ||||
| 
 | ||||
| var toAbiInput = function (json, methodName, params) { | ||||
|     var bytes = ""; | ||||
|     var index = findMethodIndex(json, methodName); | ||||
| 
 | ||||
|     if (index === -1) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     bytes = "0x" + padLeft(index.toString(16), 2); | ||||
|     var method = json[index]; | ||||
| 
 | ||||
|     for (var i = 0; i < method.inputs.length; i++) { | ||||
|         var found = false; | ||||
|         for (var j = 0; j < inputTypes.length && !found; j++) { | ||||
|             found = inputTypes[j](method.inputs[i].type, params[i]); | ||||
|         } | ||||
|         if (!found) { | ||||
|             console.error('unsupported json type: ' + method.inputs[i].type); | ||||
|         } | ||||
|         bytes += found; | ||||
|     } | ||||
|     return bytes; | ||||
| }; | ||||
| 
 | ||||
| var setupOutputTypes = function () { | ||||
| 
 | ||||
|     var prefixedType = function (prefix, calcPadding) { | ||||
|         return function (type) { | ||||
|             var expected = prefix; | ||||
|             if (type.indexOf(expected) !== 0) { | ||||
|                 return -1; | ||||
|             } | ||||
| 
 | ||||
|             var padding = calcPadding(type, expected); | ||||
|             return padding * 2; | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     var namedType = function (name, padding) { | ||||
|         return function (type) { | ||||
|             return name === type ? padding * 2 : -1; | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     var formatInt = function (value) { | ||||
|         return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value); | ||||
|     }; | ||||
| 
 | ||||
|     var formatHash = function (value) { | ||||
|         return "0x" + value; | ||||
|     }; | ||||
| 
 | ||||
|     var formatBool = function (value) { | ||||
|         return value === '1' ? true : false; | ||||
|     }; | ||||
| 
 | ||||
|     var formatString = function (value) { | ||||
|         return web3.toAscii(value); | ||||
|     }; | ||||
| 
 | ||||
|     return [ | ||||
|     { padding: prefixedType('uint', calcBitPadding), format: formatInt }, | ||||
|     { padding: prefixedType('int', calcBitPadding), format: formatInt }, | ||||
|     { padding: prefixedType('hash', calcBitPadding), format: formatHash }, | ||||
|     { padding: prefixedType('string', calcBytePadding), format: formatString }, | ||||
|     { padding: prefixedType('real', calcRealPadding), format: formatInt }, | ||||
|     { padding: prefixedType('ureal', calcRealPadding), format: formatInt }, | ||||
|     { padding: namedType('address', 20) }, | ||||
|     { padding: namedType('bool', 1), format: formatBool } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var outputTypes = setupOutputTypes(); | ||||
| 
 | ||||
| var fromAbiOutput = function (json, methodName, output) { | ||||
|     var index = findMethodIndex(json, methodName); | ||||
| 
 | ||||
|     if (index === -1) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     output = output.slice(2); | ||||
| 
 | ||||
|     var result = []; | ||||
|     var method = json[index]; | ||||
|     for (var i = 0; i < method.outputs.length; i++) { | ||||
|         var padding = -1; | ||||
|         for (var j = 0; j < outputTypes.length && padding === -1; j++) { | ||||
|             padding = outputTypes[j].padding(method.outputs[i].type); | ||||
|         } | ||||
| 
 | ||||
|         if (padding === -1) { | ||||
|             // not found output parsing
 | ||||
|             continue; | ||||
|         } | ||||
|         var res = output.slice(0, padding); | ||||
|         var formatter = outputTypes[j - 1].format; | ||||
|         result.push(formatter ? formatter(res) : ("0x" + res)); | ||||
|         output = output.slice(padding); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| }; | ||||
| 
 | ||||
| var inputParser = function (json) { | ||||
|     var parser = {}; | ||||
|     json.forEach(function (method) { | ||||
|         parser[method.name] = function () { | ||||
|             var params = Array.prototype.slice.call(arguments); | ||||
|             return toAbiInput(json, method.name, params); | ||||
|         }; | ||||
|     }); | ||||
| 
 | ||||
|     return parser; | ||||
| }; | ||||
| 
 | ||||
| var outputParser = function (json) { | ||||
|     var parser = {}; | ||||
|     json.forEach(function (method) { | ||||
|         parser[method.name] = function (output) { | ||||
|             return fromAbiOutput(json, method.name, output); | ||||
|         }; | ||||
|     }); | ||||
| 
 | ||||
|     return parser; | ||||
| }; | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") { | ||||
| 	module.exports = { | ||||
| 		inputParser: inputParser, | ||||
| 		outputParser: outputParser | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										103
									
								
								cmd/mist/assets/ext/ethereum.js/lib/autoprovider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								cmd/mist/assets/ext/ethereum.js/lib/autoprovider.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file autoprovider.js | ||||
|  * @authors: | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  *   Marian Oancea <marian@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * @brief if qt object is available, uses QtProvider, | ||||
|  * if not tries to connect over websockets | ||||
|  * if it fails, it uses HttpRpcProvider | ||||
|  */ | ||||
| 
 | ||||
| // TODO: is these line is supposed to be here? 
 | ||||
| if (process.env.NODE_ENV !== 'build') { | ||||
|     var WebSocket = require('ws'); // jshint ignore:line
 | ||||
|     var web3 = require('./web3'); // jshint ignore:line
 | ||||
| } | ||||
| 
 | ||||
| var AutoProvider = function (userOptions) { | ||||
|     if (web3.haveProvider()) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // before we determine what provider we are, we have to cache request
 | ||||
|     this.sendQueue = []; | ||||
|     this.onmessageQueue = []; | ||||
| 
 | ||||
|     if (navigator.qt) { | ||||
|         this.provider = new web3.providers.QtProvider(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     userOptions = userOptions || {}; | ||||
|     var options = { | ||||
|         httprpc: userOptions.httprpc || 'http://localhost:8080', | ||||
|         websockets: userOptions.websockets || 'ws://localhost:40404/eth' | ||||
|     }; | ||||
| 
 | ||||
|     var self = this; | ||||
|     var closeWithSuccess = function (success) { | ||||
|         ws.close(); | ||||
|         if (success) { | ||||
|             self.provider = new web3.providers.WebSocketProvider(options.websockets); | ||||
|         } else { | ||||
|             self.provider = new web3.providers.HttpRpcProvider(options.httprpc); | ||||
|             self.poll = self.provider.poll.bind(self.provider); | ||||
|         } | ||||
|         self.sendQueue.forEach(function (payload) { | ||||
|             self.provider(payload); | ||||
|         }); | ||||
|         self.onmessageQueue.forEach(function (handler) { | ||||
|             self.provider.onmessage = handler; | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     var ws = new WebSocket(options.websockets); | ||||
| 
 | ||||
|     ws.onopen = function() { | ||||
|         closeWithSuccess(true); | ||||
|     }; | ||||
| 
 | ||||
|     ws.onerror = function() { | ||||
|         closeWithSuccess(false); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| AutoProvider.prototype.send = function (payload) { | ||||
|     if (this.provider) { | ||||
|         this.provider.send(payload); | ||||
|         return; | ||||
|     } | ||||
|     this.sendQueue.push(payload); | ||||
| }; | ||||
| 
 | ||||
| Object.defineProperty(AutoProvider.prototype, 'onmessage', { | ||||
|     set: function (handler) { | ||||
|         if (this.provider) { | ||||
|             this.provider.onmessage = handler; | ||||
|             return; | ||||
|         } | ||||
|         this.onmessageQueue.push(handler); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
| 	module.exports = AutoProvider; | ||||
							
								
								
									
										66
									
								
								cmd/mist/assets/ext/ethereum.js/lib/contract.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								cmd/mist/assets/ext/ethereum.js/lib/contract.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file contract.js | ||||
|  * @authors: | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| // TODO: is these line is supposed to be here? 
 | ||||
| if (process.env.NODE_ENV !== 'build') { | ||||
|     var web3 = require('./web3'); // jshint ignore:line
 | ||||
| } | ||||
| 
 | ||||
| var abi = require('./abi'); | ||||
| 
 | ||||
| var contract = function (address, desc) { | ||||
|     var inputParser = abi.inputParser(desc); | ||||
|     var outputParser = abi.outputParser(desc); | ||||
| 
 | ||||
|     var contract = {}; | ||||
| 
 | ||||
|     desc.forEach(function (method) { | ||||
|         contract[method.name] = function () { | ||||
|             var params = Array.prototype.slice.call(arguments); | ||||
|             var parsed = inputParser[method.name].apply(null, params); | ||||
| 
 | ||||
|             var onSuccess = function (result) { | ||||
|                 return outputParser[method.name](result); | ||||
|             }; | ||||
| 
 | ||||
|             return { | ||||
|                 call: function (extra) { | ||||
|                     extra = extra || {}; | ||||
|                     extra.to = address; | ||||
|                     extra.data = parsed; | ||||
|                     return web3.eth.call(extra).then(onSuccess); | ||||
|                 }, | ||||
|                 transact: function (extra) { | ||||
|                     extra = extra || {}; | ||||
|                     extra.to = address; | ||||
|                     extra.data = parsed; | ||||
|                     return web3.eth.transact(extra).then(onSuccess); | ||||
|                 } | ||||
|             }; | ||||
|         }; | ||||
|     }); | ||||
| 
 | ||||
|     return contract; | ||||
| }; | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
| 	module.exports = contract; | ||||
							
								
								
									
										95
									
								
								cmd/mist/assets/ext/ethereum.js/lib/httprpc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								cmd/mist/assets/ext/ethereum.js/lib/httprpc.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file httprpc.js | ||||
|  * @authors: | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  *   Marian Oancea <marian@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| // TODO: is these line is supposed to be here? 
 | ||||
| if (process.env.NODE_ENV !== 'build') { | ||||
|     var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
 | ||||
| } | ||||
| 
 | ||||
| var HttpRpcProvider = function (host) { | ||||
|     this.handlers = []; | ||||
|     this.host = host; | ||||
| }; | ||||
| 
 | ||||
| function formatJsonRpcObject(object) { | ||||
|     return { | ||||
|         jsonrpc: '2.0', | ||||
|         method: object.call, | ||||
|         params: object.args, | ||||
|         id: object._id | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| function formatJsonRpcMessage(message) { | ||||
|     var object = JSON.parse(message); | ||||
| 
 | ||||
|     return { | ||||
|         _id: object.id, | ||||
|         data: object.result, | ||||
|         error: object.error | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| HttpRpcProvider.prototype.sendRequest = function (payload, cb) { | ||||
|     var data = formatJsonRpcObject(payload); | ||||
| 
 | ||||
|     var request = new XMLHttpRequest(); | ||||
|     request.open("POST", this.host, true); | ||||
|     request.send(JSON.stringify(data)); | ||||
|     request.onreadystatechange = function () { | ||||
|         if (request.readyState === 4 && cb) { | ||||
|             cb(request); | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| HttpRpcProvider.prototype.send = function (payload) { | ||||
|     var self = this; | ||||
|     this.sendRequest(payload, function (request) { | ||||
|         self.handlers.forEach(function (handler) { | ||||
|             handler.call(self, formatJsonRpcMessage(request.responseText)); | ||||
|         }); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| HttpRpcProvider.prototype.poll = function (payload, id) { | ||||
|     var self = this; | ||||
|     this.sendRequest(payload, function (request) { | ||||
|         var parsed = JSON.parse(request.responseText); | ||||
|         if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { | ||||
|             return; | ||||
|         } | ||||
|         self.handlers.forEach(function (handler) { | ||||
|             handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); | ||||
|         }); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { | ||||
|     set: function (handler) { | ||||
|         this.handlers.push(handler); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
| 	module.exports = HttpRpcProvider; | ||||
							
								
								
									
										46
									
								
								cmd/mist/assets/ext/ethereum.js/lib/qt.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								cmd/mist/assets/ext/ethereum.js/lib/qt.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file qt.js | ||||
|  * @authors: | ||||
|  *   Jeffrey Wilcke <jeff@ethdev.com> | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| var QtProvider = function() { | ||||
|     this.handlers = []; | ||||
| 
 | ||||
|     var self = this; | ||||
|     navigator.qt.onmessage = function (message) { | ||||
|         self.handlers.forEach(function (handler) { | ||||
|             handler.call(self, JSON.parse(message.data)); | ||||
|         }); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| QtProvider.prototype.send = function(payload) { | ||||
|     navigator.qt.postMessage(JSON.stringify(payload)); | ||||
| }; | ||||
| 
 | ||||
| Object.defineProperty(QtProvider.prototype, "onmessage", { | ||||
|     set: function(handler) { | ||||
|         this.handlers.push(handler); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
| 	module.exports = QtProvider; | ||||
							
								
								
									
										509
									
								
								cmd/mist/assets/ext/ethereum.js/lib/web3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										509
									
								
								cmd/mist/assets/ext/ethereum.js/lib/web3.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,509 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file main.js | ||||
|  * @authors: | ||||
|  *   Jeffrey Wilcke <jeff@ethdev.com> | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  *   Marian Oancea <marian@ethdev.com> | ||||
|  *   Gav Wood <g@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| function flattenPromise (obj) { | ||||
|     if (obj instanceof Promise) { | ||||
|         return Promise.resolve(obj); | ||||
|     } | ||||
| 
 | ||||
|     if (obj instanceof Array) { | ||||
|         return new Promise(function (resolve) { | ||||
|             var promises = obj.map(function (o) { | ||||
|                 return flattenPromise(o); | ||||
|             }); | ||||
| 
 | ||||
|             return Promise.all(promises).then(function (res) { | ||||
|                 for (var i = 0; i < obj.length; i++) { | ||||
|                     obj[i] = res[i]; | ||||
|                 } | ||||
|                 resolve(obj); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     if (obj instanceof Object) { | ||||
|         return new Promise(function (resolve) { | ||||
|             var keys = Object.keys(obj); | ||||
|             var promises = keys.map(function (key) { | ||||
|                 return flattenPromise(obj[key]); | ||||
|             }); | ||||
| 
 | ||||
|             return Promise.all(promises).then(function (res) { | ||||
|                 for (var i = 0; i < keys.length; i++) { | ||||
|                     obj[keys[i]] = res[i]; | ||||
|                 } | ||||
|                 resolve(obj); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     return Promise.resolve(obj); | ||||
| } | ||||
| 
 | ||||
| var web3Methods = function () { | ||||
|     return [ | ||||
|     { name: 'sha3', call: 'web3_sha3' } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var ethMethods = function () { | ||||
|     var blockCall = function (args) { | ||||
|         return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; | ||||
|     }; | ||||
| 
 | ||||
|     var transactionCall = function (args) { | ||||
|         return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber'; | ||||
|     }; | ||||
| 
 | ||||
|     var uncleCall = function (args) { | ||||
|         return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber'; | ||||
|     }; | ||||
| 
 | ||||
|     var methods = [ | ||||
|     { name: 'balanceAt', call: 'eth_balanceAt' }, | ||||
|     { name: 'stateAt', call: 'eth_stateAt' }, | ||||
|     { name: 'storageAt', call: 'eth_storageAt' }, | ||||
|     { name: 'countAt', call: 'eth_countAt'}, | ||||
|     { name: 'codeAt', call: 'eth_codeAt' }, | ||||
|     { name: 'transact', call: 'eth_transact' }, | ||||
|     { name: 'call', call: 'eth_call' }, | ||||
|     { name: 'block', call: blockCall }, | ||||
|     { name: 'transaction', call: transactionCall }, | ||||
|     { name: 'uncle', call: uncleCall }, | ||||
|     { name: 'compilers', call: 'eth_compilers' }, | ||||
|     { name: 'lll', call: 'eth_lll' }, | ||||
|     { name: 'solidity', call: 'eth_solidity' }, | ||||
|     { name: 'serpent', call: 'eth_serpent' }, | ||||
|     { name: 'logs', call: 'eth_logs' } | ||||
|     ]; | ||||
|     return methods; | ||||
| }; | ||||
| 
 | ||||
| var ethProperties = function () { | ||||
|     return [ | ||||
|     { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, | ||||
|     { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' }, | ||||
|     { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' }, | ||||
|     { name: 'gasPrice', getter: 'eth_gasPrice' }, | ||||
|     { name: 'account', getter: 'eth_account' }, | ||||
|     { name: 'accounts', getter: 'eth_accounts' }, | ||||
|     { name: 'peerCount', getter: 'eth_peerCount' }, | ||||
|     { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' }, | ||||
|     { name: 'number', getter: 'eth_number'} | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var dbMethods = function () { | ||||
|     return [ | ||||
|     { name: 'put', call: 'db_put' }, | ||||
|     { name: 'get', call: 'db_get' }, | ||||
|     { name: 'putString', call: 'db_putString' }, | ||||
|     { name: 'getString', call: 'db_getString' } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var shhMethods = function () { | ||||
|     return [ | ||||
|     { name: 'post', call: 'shh_post' }, | ||||
|     { name: 'newIdentity', call: 'shh_newIdentity' }, | ||||
|     { name: 'haveIdentity', call: 'shh_haveIdentity' }, | ||||
|     { name: 'newGroup', call: 'shh_newGroup' }, | ||||
|     { name: 'addToGroup', call: 'shh_addToGroup' } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var ethWatchMethods = function () { | ||||
|     var newFilter = function (args) { | ||||
|         return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; | ||||
|     }; | ||||
| 
 | ||||
|     return [ | ||||
|     { name: 'newFilter', call: newFilter }, | ||||
|     { name: 'uninstallFilter', call: 'eth_uninstallFilter' }, | ||||
|     { name: 'getMessages', call: 'eth_filterLogs' } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var shhWatchMethods = function () { | ||||
|     return [ | ||||
|     { name: 'newFilter', call: 'shh_newFilter' }, | ||||
|     { name: 'uninstallFilter', call: 'shh_uninstallFilter' }, | ||||
|     { name: 'getMessages', call: 'shh_getMessages' } | ||||
|     ]; | ||||
| }; | ||||
| 
 | ||||
| var setupMethods = function (obj, methods) { | ||||
|     methods.forEach(function (method) { | ||||
|         obj[method.name] = function () { | ||||
|             return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { | ||||
|                 var call = typeof method.call === "function" ? method.call(args) : method.call; | ||||
|                 return {call: call, args: args}; | ||||
|             }).then(function (request) { | ||||
|                 return new Promise(function (resolve, reject) { | ||||
|                     web3.provider.send(request, function (err, result) { | ||||
|                         if (!err) { | ||||
|                             resolve(result); | ||||
|                             return; | ||||
|                         } | ||||
|                         reject(err); | ||||
|                     }); | ||||
|                 }); | ||||
|             }).catch(function(err) { | ||||
|                 console.error(err); | ||||
|             }); | ||||
|         }; | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| var setupProperties = function (obj, properties) { | ||||
|     properties.forEach(function (property) { | ||||
|         var proto = {}; | ||||
|         proto.get = function () { | ||||
|             return new Promise(function(resolve, reject) { | ||||
|                 web3.provider.send({call: property.getter}, function(err, result) { | ||||
|                     if (!err) { | ||||
|                         resolve(result); | ||||
|                         return; | ||||
|                     } | ||||
|                     reject(err); | ||||
|                 }); | ||||
|             }); | ||||
|         }; | ||||
|         if (property.setter) { | ||||
|             proto.set = function (val) { | ||||
|                 return flattenPromise([val]).then(function (args) { | ||||
|                     return new Promise(function (resolve) { | ||||
|                         web3.provider.send({call: property.setter, args: args}, function (err, result) { | ||||
|                             if (!err) { | ||||
|                                 resolve(result); | ||||
|                                 return; | ||||
|                             } | ||||
|                             reject(err); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }).catch(function (err) { | ||||
|                     console.error(err); | ||||
|                 }); | ||||
|             }; | ||||
|         } | ||||
|         Object.defineProperty(obj, property.name, proto); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| // TODO: import from a dependency, don't duplicate.
 | ||||
| var hexToDec = function (hex) { | ||||
|     return parseInt(hex, 16).toString(); | ||||
| }; | ||||
| 
 | ||||
| var decToHex = function (dec) { | ||||
|     return parseInt(dec).toString(16); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| var web3 = { | ||||
|     _callbacks: {}, | ||||
|     _events: {}, | ||||
|     providers: {}, | ||||
| 
 | ||||
|     toHex: function(str) { | ||||
|         var hex = ""; | ||||
|         for(var i = 0; i < str.length; i++) { | ||||
|             var n = str.charCodeAt(i).toString(16); | ||||
|             hex += n.length < 2 ? '0' + n : n; | ||||
|         } | ||||
| 
 | ||||
|         return hex; | ||||
|     }, | ||||
| 
 | ||||
|     toAscii: function(hex) { | ||||
|         // Find termination
 | ||||
|         var str = ""; | ||||
|         var i = 0, l = hex.length; | ||||
|         if (hex.substring(0, 2) === '0x') | ||||
|             i = 2; | ||||
|         for(; i < l; i+=2) { | ||||
|             var code = hex.charCodeAt(i); | ||||
|             if(code === 0) { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); | ||||
|         } | ||||
| 
 | ||||
|         return str; | ||||
|     }, | ||||
| 
 | ||||
|     fromAscii: function(str, pad) { | ||||
|         pad = pad === undefined ? 32 : pad; | ||||
|         var hex = this.toHex(str); | ||||
|         while(hex.length < pad*2) | ||||
|             hex += "00"; | ||||
|         return "0x" + hex; | ||||
|     }, | ||||
| 
 | ||||
|     toDecimal: function (val) { | ||||
|         return hexToDec(val.substring(2)); | ||||
|     }, | ||||
| 
 | ||||
|     fromDecimal: function (val) { | ||||
|         return "0x" + decToHex(val); | ||||
|     }, | ||||
| 
 | ||||
|     toEth: function(str) { | ||||
|         var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; | ||||
|         var unit = 0; | ||||
|         var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ]; | ||||
|         while (val > 3000 && unit < units.length - 1) | ||||
|         { | ||||
|             val /= 1000; | ||||
|             unit++; | ||||
|         } | ||||
|         var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); | ||||
|         var replaceFunction = function($0, $1, $2) { | ||||
|             return $1 + ',' + $2; | ||||
|         }; | ||||
| 
 | ||||
|         while (true) { | ||||
|             var o = s; | ||||
|             s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); | ||||
|             if (o === s) | ||||
|                 break; | ||||
|         } | ||||
|         return s + ' ' + units[unit]; | ||||
|     }, | ||||
| 
 | ||||
|     eth: { | ||||
|         prototype: Object(), // jshint ignore:line
 | ||||
|         watch: function (params) { | ||||
|             return new Filter(params, ethWatch); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     db: { | ||||
|         prototype: Object() // jshint ignore:line
 | ||||
|     }, | ||||
| 
 | ||||
|     shh: { | ||||
|         prototype: Object(), // jshint ignore:line
 | ||||
|         watch: function (params) { | ||||
|             return new Filter(params, shhWatch); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     on: function(event, id, cb) { | ||||
|         if(web3._events[event] === undefined) { | ||||
|             web3._events[event] = {}; | ||||
|         } | ||||
| 
 | ||||
|         web3._events[event][id] = cb; | ||||
|         return this; | ||||
|     }, | ||||
| 
 | ||||
|     off: function(event, id) { | ||||
|         if(web3._events[event] !== undefined) { | ||||
|             delete web3._events[event][id]; | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     }, | ||||
| 
 | ||||
|     trigger: function(event, id, data) { | ||||
|         var callbacks = web3._events[event]; | ||||
|         if (!callbacks || !callbacks[id]) { | ||||
|             return; | ||||
|         } | ||||
|         var cb = callbacks[id]; | ||||
|         cb(data); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| setupMethods(web3, web3Methods()); | ||||
| setupMethods(web3.eth, ethMethods()); | ||||
| setupProperties(web3.eth, ethProperties()); | ||||
| setupMethods(web3.db, dbMethods()); | ||||
| setupMethods(web3.shh, shhMethods()); | ||||
| 
 | ||||
| var ethWatch = { | ||||
|     changed: 'eth_changed' | ||||
| }; | ||||
| setupMethods(ethWatch, ethWatchMethods()); | ||||
| var shhWatch = { | ||||
|     changed: 'shh_changed' | ||||
| }; | ||||
| setupMethods(shhWatch, shhWatchMethods()); | ||||
| 
 | ||||
| var ProviderManager = function() { | ||||
|     this.queued = []; | ||||
|     this.polls = []; | ||||
|     this.ready = false; | ||||
|     this.provider = undefined; | ||||
|     this.id = 1; | ||||
| 
 | ||||
|     var self = this; | ||||
|     var poll = function () { | ||||
|         if (self.provider && self.provider.poll) { | ||||
|             self.polls.forEach(function (data) { | ||||
|                 data.data._id = self.id; | ||||
|                 self.id++; | ||||
|                 self.provider.poll(data.data, data.id); | ||||
|             }); | ||||
|         } | ||||
|         setTimeout(poll, 12000); | ||||
|     }; | ||||
|     poll(); | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.send = function(data, cb) { | ||||
|     data._id = this.id; | ||||
|     if (cb) { | ||||
|         web3._callbacks[data._id] = cb; | ||||
|     } | ||||
| 
 | ||||
|     data.args = data.args || []; | ||||
|     this.id++; | ||||
| 
 | ||||
|     if(this.provider !== undefined) { | ||||
|         this.provider.send(data); | ||||
|     } else { | ||||
|         console.warn("provider is not set"); | ||||
|         this.queued.push(data); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.set = function(provider) { | ||||
|     if(this.provider !== undefined && this.provider.unload !== undefined) { | ||||
|         this.provider.unload(); | ||||
|     } | ||||
| 
 | ||||
|     this.provider = provider; | ||||
|     this.ready = true; | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.sendQueued = function() { | ||||
|     for(var i = 0; this.queued.length; i++) { | ||||
|         // Resend
 | ||||
|         this.send(this.queued[i]); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.installed = function() { | ||||
|     return this.provider !== undefined; | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.startPolling = function (data, pollId) { | ||||
|     if (!this.provider || !this.provider.poll) { | ||||
|         return; | ||||
|     } | ||||
|     this.polls.push({data: data, id: pollId}); | ||||
| }; | ||||
| 
 | ||||
| ProviderManager.prototype.stopPolling = function (pollId) { | ||||
|     for (var i = this.polls.length; i--;) { | ||||
|         var poll = this.polls[i]; | ||||
|         if (poll.id === pollId) { | ||||
|             this.polls.splice(i, 1); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| web3.provider = new ProviderManager(); | ||||
| 
 | ||||
| web3.setProvider = function(provider) { | ||||
|     provider.onmessage = messageHandler; | ||||
|     web3.provider.set(provider); | ||||
|     web3.provider.sendQueued(); | ||||
| }; | ||||
| 
 | ||||
| web3.haveProvider = function() { | ||||
|     return !!web3.provider.provider; | ||||
| }; | ||||
| 
 | ||||
| var Filter = function(options, impl) { | ||||
|     this.impl = impl; | ||||
|     this.callbacks = []; | ||||
| 
 | ||||
|     var self = this; | ||||
|     this.promise = impl.newFilter(options); | ||||
|     this.promise.then(function (id) { | ||||
|         self.id = id; | ||||
|         web3.on(impl.changed, id, self.trigger.bind(self)); | ||||
|         web3.provider.startPolling({call: impl.changed, args: [id]}, id); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.arrived = function(callback) { | ||||
|     this.changed(callback); | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.changed = function(callback) { | ||||
|     var self = this; | ||||
|     this.promise.then(function(id) { | ||||
|         self.callbacks.push(callback); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.trigger = function(messages) { | ||||
|     for(var i = 0; i < this.callbacks.length; i++) { | ||||
|         this.callbacks[i].call(this, messages); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.uninstall = function() { | ||||
|     var self = this; | ||||
|     this.promise.then(function (id) { | ||||
|         self.impl.uninstallFilter(id); | ||||
|         web3.provider.stopPolling(id); | ||||
|         web3.off(impl.changed, id); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.messages = function() { | ||||
|     var self = this; | ||||
|     return this.promise.then(function (id) { | ||||
|         return self.impl.getMessages(id); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| Filter.prototype.logs = function () { | ||||
|     return this.messages(); | ||||
| }; | ||||
| 
 | ||||
| function messageHandler(data) { | ||||
|     if(data._event !== undefined) { | ||||
|         web3.trigger(data._event, data._id, data.data); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if(data._id) { | ||||
|         var cb = web3._callbacks[data._id]; | ||||
|         if (cb) { | ||||
|             cb.call(this, data.error, data.data); | ||||
|             delete web3._callbacks[data._id]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
|     module.exports = web3; | ||||
							
								
								
									
										78
									
								
								cmd/mist/assets/ext/ethereum.js/lib/websocket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								cmd/mist/assets/ext/ethereum.js/lib/websocket.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| /* | ||||
|     This file is part of ethereum.js. | ||||
| 
 | ||||
|     ethereum.js is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     ethereum.js is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** @file websocket.js | ||||
|  * @authors: | ||||
|  *   Jeffrey Wilcke <jeff@ethdev.com> | ||||
|  *   Marek Kotewicz <marek@ethdev.com> | ||||
|  *   Marian Oancea <marian@ethdev.com> | ||||
|  * @date 2014 | ||||
|  */ | ||||
| 
 | ||||
| // TODO: is these line is supposed to be here? 
 | ||||
| if (process.env.NODE_ENV !== 'build') { | ||||
|     var WebSocket = require('ws'); // jshint ignore:line
 | ||||
| } | ||||
| 
 | ||||
| var WebSocketProvider = function(host) { | ||||
|     // onmessage handlers
 | ||||
|     this.handlers = []; | ||||
|     // queue will be filled with messages if send is invoked before the ws is ready
 | ||||
|     this.queued = []; | ||||
|     this.ready = false; | ||||
| 
 | ||||
|     this.ws = new WebSocket(host); | ||||
| 
 | ||||
|     var self = this; | ||||
|     this.ws.onmessage = function(event) { | ||||
|         for(var i = 0; i < self.handlers.length; i++) { | ||||
|             self.handlers[i].call(self, JSON.parse(event.data), event); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     this.ws.onopen = function() { | ||||
|         self.ready = true; | ||||
| 
 | ||||
|         for(var i = 0; i < self.queued.length; i++) { | ||||
|             // Resend
 | ||||
|             self.send(self.queued[i]); | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| WebSocketProvider.prototype.send = function(payload) { | ||||
|     if(this.ready) { | ||||
|         var data = JSON.stringify(payload); | ||||
| 
 | ||||
|         this.ws.send(data); | ||||
|     } else { | ||||
|         this.queued.push(payload); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| WebSocketProvider.prototype.onMessage = function(handler) { | ||||
|     this.handlers.push(handler); | ||||
| }; | ||||
| 
 | ||||
| WebSocketProvider.prototype.unload = function() { | ||||
|     this.ws.close(); | ||||
| }; | ||||
| Object.defineProperty(WebSocketProvider.prototype, "onmessage", { | ||||
|     set: function(provider) { this.onMessage(provider); } | ||||
| }); | ||||
| 
 | ||||
| if (typeof(module) !== "undefined") | ||||
|     module.exports = WebSocketProvider; | ||||
							
								
								
									
										67
									
								
								cmd/mist/assets/ext/ethereum.js/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								cmd/mist/assets/ext/ethereum.js/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| { | ||||
|   "name": "ethereum.js", | ||||
|   "namespace": "ethereum", | ||||
|   "version": "0.0.6", | ||||
|   "description": "Ethereum Compatible JavaScript API", | ||||
|   "main": "./index.js", | ||||
|   "directories": { | ||||
|     "lib": "./lib" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "es6-promise": "*", | ||||
|     "ws": "*", | ||||
|     "xmlhttprequest": "*" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "bower": ">=1.3.0", | ||||
|     "browserify": ">=6.0", | ||||
|     "del": ">=0.1.1", | ||||
|     "envify": "^3.0.0", | ||||
|     "exorcist": "^0.1.6", | ||||
|     "gulp": ">=3.4.0", | ||||
|     "gulp-jshint": ">=1.5.0", | ||||
|     "gulp-rename": ">=1.2.0", | ||||
|     "gulp-uglify": ">=1.0.0", | ||||
|     "jshint": ">=2.5.0", | ||||
|     "uglifyify": "^2.6.0", | ||||
|     "unreachable-branch-transform": "^0.1.0", | ||||
|     "vinyl-source-stream": "^1.0.0" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "build": "gulp", | ||||
|     "watch": "gulp watch", | ||||
|     "lint": "gulp lint" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://github.com/ethereum/ethereum.js.git" | ||||
|   }, | ||||
|   "homepage": "https://github.com/ethereum/ethereum.js", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/ethereum/ethereum.js/issues" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "ethereum", | ||||
|     "javascript", | ||||
|     "API" | ||||
|   ], | ||||
|   "author": "ethdev.com", | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "Jeffery Wilcke", | ||||
|       "email": "jeff@ethdev.com", | ||||
|       "url": "https://github.com/obscuren" | ||||
|     }, | ||||
|     { | ||||
|       "name": "Marek Kotewicz", | ||||
|       "email": "marek@ethdev.com", | ||||
|       "url": "https://github.com/debris" | ||||
|     }, | ||||
|     { | ||||
|       "name": "Marian Oancea", | ||||
|       "email": "marian@ethdev.com", | ||||
|       "url": "https://github.com/cubedro" | ||||
|     } | ||||
|   ], | ||||
|   "license": "LGPL-3.0" | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| import QtQuick 2.0 | ||||
| import QtQuick 2.1 | ||||
| import QtWebKit 3.0 | ||||
| import QtWebKit.experimental 1.0 | ||||
| import QtQuick.Controls 1.0; | ||||
| @ -8,437 +8,484 @@ import QtQuick.Window 2.1; | ||||
| import Ethereum 1.0 | ||||
| 
 | ||||
| Rectangle { | ||||
|     id: window | ||||
|     property var title: "Browser" | ||||
|     property var iconSource: "../browser.png" | ||||
|     property var menuItem | ||||
| 	id: window | ||||
|     objectName: "browserView" | ||||
|     anchors.fill: parent | ||||
|     color: "#00000000" | ||||
| 
 | ||||
|     property alias url: webview.url | ||||
|     property alias webView: webview | ||||
| 	property var title: "Browser" | ||||
| 	property var iconSource: "../browser.png" | ||||
| 	property var menuItem | ||||
| 
 | ||||
|     property var cleanPath: false | ||||
|     property var open: function(url) { | ||||
|         if(!window.cleanPath) { | ||||
|             var uri = url; | ||||
|             if(!/.*\:\/\/.*/.test(uri)) { | ||||
|                 uri = "http://" + uri; | ||||
|             } | ||||
| 	property alias url: webview.url | ||||
| 	property alias webView: webview | ||||
| 
 | ||||
|             var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ | ||||
| 	property var cleanPath: false | ||||
| 	property var open: function(url) { | ||||
| 		if(!window.cleanPath) { | ||||
| 			var uri = url; | ||||
| 			if(!/.*\:\/\/.*/.test(uri)) { | ||||
| 				uri = "http://" + uri; | ||||
| 			} | ||||
| 
 | ||||
|             if(reg.test(uri)) { | ||||
|                 uri.replace(reg, function(match, pre, domain, path) { | ||||
|                     uri = pre; | ||||
| 			var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ | ||||
| 
 | ||||
|                     var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); | ||||
|                     var ip = []; | ||||
|                     for(var i = 0, l = lookup.length; i < l; i++) { | ||||
|                         ip.push(lookup.charCodeAt(i)) | ||||
|                     } | ||||
| 			if(reg.test(uri)) { | ||||
| 				uri.replace(reg, function(match, pre, domain, path) { | ||||
| 					uri = pre; | ||||
| 
 | ||||
|                     if(ip.length != 0) { | ||||
|                         uri += lookup; | ||||
|                     } else { | ||||
|                         uri += domain; | ||||
|                     } | ||||
| 					var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); | ||||
| 					var ip = []; | ||||
| 					for(var i = 0, l = lookup.length; i < l; i++) { | ||||
| 						ip.push(lookup.charCodeAt(i)) | ||||
| 					} | ||||
| 
 | ||||
|                     uri += path; | ||||
|                 }); | ||||
|             } | ||||
| 					if(ip.length != 0) { | ||||
| 						uri += lookup; | ||||
| 					} else { | ||||
| 						uri += domain; | ||||
| 					} | ||||
| 
 | ||||
|             window.cleanPath = true; | ||||
| 					uri += path; | ||||
| 				}); | ||||
| 			} | ||||
| 
 | ||||
|             webview.url = uri; | ||||
| 			window.cleanPath = true; | ||||
| 
 | ||||
|             //uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>"); | ||||
|             uriNav.text = uri; | ||||
|         } else { | ||||
|             // Prevent inf loop. | ||||
|             window.cleanPath = false; | ||||
|         } | ||||
|     } | ||||
| 			webview.url = uri; | ||||
| 
 | ||||
|     Component.onCompleted: { | ||||
|         webview.url = "http://etherian.io" | ||||
|     } | ||||
| 			//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>"); | ||||
| 			uriNav.text = uri; | ||||
| 		} else { | ||||
| 			// Prevent inf loop. | ||||
| 			window.cleanPath = false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|     signal messages(var messages, int id); | ||||
|     onMessages: { | ||||
|         // Bit of a cheat to get proper JSON | ||||
|         var m = JSON.parse(JSON.parse(JSON.stringify(messages))) | ||||
|         webview.postEvent("messages", id, m); | ||||
|     } | ||||
| 	Component.onCompleted: { | ||||
| 		webview.url = "http://etherian.io" | ||||
| 	} | ||||
| 
 | ||||
|     function onShhMessage(message, id) { | ||||
| 	    webview.postEvent("shhChanged", id, message) | ||||
|     } | ||||
| 	signal messages(var messages, int id); | ||||
| 	onMessages: { | ||||
| 		// Bit of a cheat to get proper JSON | ||||
| 		var m = JSON.parse(JSON.parse(JSON.stringify(messages))) | ||||
| 		webview.postEvent("eth_changed", id, m); | ||||
| 	} | ||||
| 
 | ||||
|     Item { | ||||
|         objectName: "root" | ||||
|         id: root | ||||
|         anchors.fill: parent | ||||
|         state: "inspectorShown" | ||||
| 	function onShhMessage(message, id) { | ||||
| 		webview.postEvent("shh_changed", id, message) | ||||
| 	} | ||||
| 
 | ||||
|         RowLayout { | ||||
|             id: navBar | ||||
|             height: 40 | ||||
| 	Item { | ||||
| 		objectName: "root" | ||||
| 		id: root | ||||
| 		anchors.fill: parent | ||||
| 		state: "inspectorShown" | ||||
| 
 | ||||
| 		RowLayout { | ||||
| 			id: navBar | ||||
| 			height: 40 | ||||
| 			anchors { | ||||
| 				left: parent.left | ||||
| 				right: parent.right | ||||
| 				leftMargin: 7 | ||||
| 			} | ||||
| 
 | ||||
| 			Button { | ||||
| 				id: back | ||||
| 				onClicked: { | ||||
| 					webview.goBack() | ||||
| 				} | ||||
| 				style: ButtonStyle { | ||||
| 					background: Image { | ||||
| 						source: "../back.png" | ||||
| 						width: 30 | ||||
| 						height: 30 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			TextField { | ||||
| 				anchors { | ||||
| 					left: back.right | ||||
| 					right: toggleInspector.left | ||||
| 					leftMargin: 10 | ||||
| 					rightMargin: 10 | ||||
| 				} | ||||
| 				text: webview.url; | ||||
| 				id: uriNav | ||||
| 				y: parent.height / 2 - this.height / 2 | ||||
| 
 | ||||
| 				Keys.onReturnPressed: { | ||||
| 					webview.url = this.text; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			Button { | ||||
| 				id: toggleInspector | ||||
| 				anchors { | ||||
| 					right: parent.right | ||||
| 				} | ||||
| 				iconSource: "../bug.png" | ||||
| 				onClicked: { | ||||
| 					if(inspector.visible == true){ | ||||
| 						inspector.visible = false | ||||
| 					}else{ | ||||
| 						inspector.visible = true | ||||
| 						inspector.url = webview.experimental.remoteInspectorUrl | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|         // Border | ||||
|         Rectangle { | ||||
|             id: divider | ||||
|             anchors { | ||||
|                 left: parent.left | ||||
|                 right: parent.right | ||||
|                 leftMargin: 7 | ||||
|             } | ||||
| 
 | ||||
|             Button { | ||||
|                 id: back | ||||
|                 onClicked: { | ||||
|                     webview.goBack() | ||||
|                 } | ||||
|                 style: ButtonStyle { | ||||
|                     background: Image { | ||||
|                         source: "../back.png" | ||||
|                         width: 30 | ||||
|                         height: 30 | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             TextField { | ||||
|                 anchors { | ||||
|                     left: back.right | ||||
|                     right: toggleInspector.left | ||||
|                     leftMargin: 5 | ||||
|                     rightMargin: 5 | ||||
|                 } | ||||
|                 text: "http://etherian.io" | ||||
|                 id: uriNav | ||||
|                 y: parent.height / 2 - this.height / 2 | ||||
| 
 | ||||
|                 Keys.onReturnPressed: { | ||||
|                     webview.url = this.text; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             Button { | ||||
|                 id: toggleInspector | ||||
|                 anchors { | ||||
|                     right: parent.right | ||||
|                 } | ||||
|                 iconSource: "../bug.png" | ||||
|                 onClicked: { | ||||
|                     if(inspector.visible == true){ | ||||
|                         inspector.visible = false | ||||
|                     }else{ | ||||
|                         inspector.visible = true | ||||
|                         inspector.url = webview.experimental.remoteInspectorUrl | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         WebView { | ||||
|             objectName: "webView" | ||||
|             id: webview | ||||
|             anchors { | ||||
|                 left: parent.left | ||||
|                 right: parent.right | ||||
|                 bottom: parent.bottom | ||||
|                 top: navBar.bottom | ||||
|             } | ||||
|             z: -1 | ||||
|             height: 1 | ||||
|             color: "#CCCCCC" | ||||
|         } | ||||
| 
 | ||||
|             //property var cleanPath: false | ||||
|             onNavigationRequested: { | ||||
|                 window.open(request.url.toString());					 | ||||
|             } | ||||
| 
 | ||||
|             function sendMessage(data) { | ||||
|                 webview.experimental.postMessage(JSON.stringify(data)) | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             experimental.preferences.javascriptEnabled: true | ||||
|             experimental.preferences.navigatorQtObjectEnabled: true | ||||
|             experimental.preferences.developerExtrasEnabled: true | ||||
|             //experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] | ||||
|             experimental.userScripts: ["../ext/q.js", "../ext/eth.js/main.js", "../ext/eth.js/qt.js", "../ext/setup.js"] | ||||
|             experimental.onMessageReceived: { | ||||
|                 console.log("[onMessageReceived]: ", message.data) | ||||
|                 // TODO move to messaging.js | ||||
|                 var data = JSON.parse(message.data) | ||||
| 
 | ||||
|                 try { | ||||
|                     switch(data.call) { | ||||
|                         case "compile": | ||||
|                         postData(data._id, eth.compile(data.args[0])) | ||||
|                         break | ||||
| 
 | ||||
|                         case "coinbase": | ||||
|                         postData(data._id, eth.coinBase()) | ||||
| 
 | ||||
|                         case "account": | ||||
|                         postData(data._id, eth.key().address); | ||||
| 
 | ||||
|                         case "isListening": | ||||
|                         postData(data._id, eth.isListening()) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "isMining": | ||||
|                         postData(data._id, eth.isMining()) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "peerCount": | ||||
|                         postData(data._id, eth.peerCount()) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "countAt": | ||||
|                         require(1) | ||||
|                         postData(data._id, eth.txCountAt(data.args[0])) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "codeAt": | ||||
|                         require(1) | ||||
|                         var code = eth.codeAt(data.args[0]) | ||||
|                         postData(data._id, code); | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "blockByNumber": | ||||
|                         require(1) | ||||
|                         var block = eth.blockByNumber(data.args[0]) | ||||
|                         postData(data._id, block) | ||||
|                         break | ||||
| 
 | ||||
|                         case "blockByHash": | ||||
|                         require(1) | ||||
|                         var block = eth.blockByHash(data.args[0]) | ||||
|                         postData(data._id, block) | ||||
|                         break | ||||
| 
 | ||||
|                         require(2) | ||||
|                         var block = eth.blockByHash(data.args[0]) | ||||
|                         postData(data._id, block.transactions[data.args[1]]) | ||||
|                         break | ||||
| 
 | ||||
|                         case "transactionByHash": | ||||
|                         case "transactionByNumber": | ||||
|                         require(2) | ||||
| 
 | ||||
|                         var block; | ||||
|                         if (data.call === "transactionByHash") | ||||
|                             block = eth.blockByHash(data.args[0]) | ||||
|                         else | ||||
|                             block = eth.blockByNumber(data.args[0]) | ||||
| 
 | ||||
|                         var tx = block.transactions.get(data.args[1]) | ||||
| 
 | ||||
|                         postData(data._id, tx) | ||||
|                         break | ||||
| 
 | ||||
|                         case "uncleByHash": | ||||
|                         case "uncleByNumber": | ||||
|                         require(2) | ||||
| 
 | ||||
|                         var block; | ||||
|                         if (data.call === "uncleByHash") | ||||
|                             block = eth.blockByHash(data.args[0]) | ||||
|                         else | ||||
|                             block = eth.blockByNumber(data.args[0]) | ||||
| 
 | ||||
|                         var uncle = block.uncles.get(data.args[1]) | ||||
| 
 | ||||
|                         postData(data._id, uncle) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "transact": | ||||
|                         require(5) | ||||
| 
 | ||||
|                         var tx = eth.transact(data.args) | ||||
|                         postData(data._id, tx) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "stateAt": | ||||
|                         require(2); | ||||
| 
 | ||||
|                         var storage = eth.storageAt(data.args[0], data.args[1]); | ||||
|                         postData(data._id, storage) | ||||
| 
 | ||||
|                         break | ||||
| 
 | ||||
|                         case "call": | ||||
|                         require(1); | ||||
|                         var ret = eth.call(data.args) | ||||
|                         postData(data._id, ret) | ||||
|                         break | ||||
| 
 | ||||
|                         case "balanceAt": | ||||
|                         require(1); | ||||
| 
 | ||||
|                         postData(data._id, eth.balanceAt(data.args[0])); | ||||
|                         break | ||||
| 
 | ||||
|                         case "watch": | ||||
|                         require(2) | ||||
|                         eth.watch(data.args[0], data.args[1]) | ||||
| 
 | ||||
|                         case "disconnect": | ||||
|                         require(1) | ||||
|                         postData(data._id, null) | ||||
|                         break; | ||||
| 
 | ||||
|                         case "messages": | ||||
|                         require(1); | ||||
| 
 | ||||
|                         var messages = JSON.parse(eth.getMessages(data.args[0])) | ||||
|                         postData(data._id, messages) | ||||
|                         break | ||||
| 
 | ||||
|                         case "mutan": | ||||
|                         require(1) | ||||
| 
 | ||||
|                         var code = eth.compileMutan(data.args[0]) | ||||
|                         postData(data._id, "0x"+code) | ||||
|                         break; | ||||
| 
 | ||||
|                         case "newFilterString": | ||||
|                         require(1) | ||||
|                         var id = eth.newFilterString(data.args[0]) | ||||
|                         postData(data._id, id); | ||||
|                         break; | ||||
| 
 | ||||
|                         case "newFilter": | ||||
|                         require(1) | ||||
|                         var id = eth.newFilter(data.args[0]) | ||||
| 
 | ||||
|                         postData(data._id, id); | ||||
|                         break; | ||||
| 
 | ||||
|                         case "getMessages": | ||||
|                         require(1); | ||||
| 
 | ||||
|                         var messages = eth.messages(data.args[0]); | ||||
|                         var m = JSON.parse(JSON.parse(JSON.stringify(messages))) | ||||
|                         postData(data._id, m); | ||||
| 
 | ||||
|                         break; | ||||
| 
 | ||||
|                         case "deleteFilter": | ||||
|                         require(1); | ||||
|                         eth.uninstallFilter(data.args[0]) | ||||
|                         break; | ||||
| 
 | ||||
| 
 | ||||
| 			case "shhNewFilter": | ||||
| 			require(1); | ||||
| 			var id = shh.watch(data.args[0], window); | ||||
| 			postData(data._id, id); | ||||
| 			break; | ||||
| 
 | ||||
| 			case "newIdentity": | ||||
| 			postData(data._id, shh.newIdentity()) | ||||
| 			break | ||||
| 
 | ||||
| 			case "post": | ||||
| 			require(1); | ||||
| 			var params = data.args[0]; | ||||
| 			var fields = ["payload", "to", "from"]; | ||||
| 			for(var i = 0; i < fields.length; i++) { | ||||
| 				params[fields[i]] = params[fields[i]] || ""; | ||||
| 		WebView { | ||||
| 			objectName: "webView" | ||||
| 			id: webview | ||||
| 			anchors { | ||||
| 				left: parent.left | ||||
| 				right: parent.right | ||||
| 				bottom: parent.bottom | ||||
| 				top: divider.bottom | ||||
| 			} | ||||
| 			if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); } | ||||
| 			params.topics = params.topics || []; | ||||
| 			params.priority = params.priority || 1000; | ||||
| 			params.ttl = params.ttl || 100; | ||||
| 
 | ||||
| 			console.log(JSON.stringify(params)) | ||||
| 			shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl); | ||||
| 			break; | ||||
|                     } | ||||
|                 } catch(e) { | ||||
|                     console.log(data.call + ": " + e) | ||||
| 			function injectJs(js) { | ||||
| 				webview.experimental.navigatorQtObjectEnabled = true; | ||||
| 				webview.experimental.evaluateJavaScript(js) | ||||
| 				webview.experimental.javascriptEnabled = true; | ||||
| 			} | ||||
| 
 | ||||
|                     postData(data._id, null); | ||||
|                 } | ||||
|             } | ||||
| 			function sendMessage(data) { | ||||
| 				webview.experimental.postMessage(JSON.stringify(data)) | ||||
| 			} | ||||
| 
 | ||||
| 
 | ||||
|             function post(seed, data) { | ||||
|                 postData(data._id, data) | ||||
|             } | ||||
| 			experimental.preferences.javascriptEnabled: true | ||||
| 			experimental.preferences.webGLEnabled: true | ||||
| 			experimental.itemSelector:  MouseArea { | ||||
| 				// To avoid conflicting with ListView.model when inside Initiator context. | ||||
| 				property QtObject selectorModel: model | ||||
| 				anchors.fill: parent | ||||
| 				onClicked: selectorModel.reject() | ||||
| 
 | ||||
|             function require(args, num) { | ||||
|                 if(args.length < num) { | ||||
|                     throw("required argument count of "+num+" got "+args.length); | ||||
|                 } | ||||
|             } | ||||
|             function postData(seed, data) { | ||||
|                 webview.experimental.postMessage(JSON.stringify({data: data, _id: seed})) | ||||
|             } | ||||
|             function postEvent(event, id, data) { | ||||
|                 webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event})) | ||||
|             } | ||||
| 				Menu { | ||||
| 					visible: true | ||||
| 					id: itemSelector | ||||
| 
 | ||||
|             function onWatchedCb(data, id) { | ||||
|                 var messages = JSON.parse(data) | ||||
|                 postEvent("watched:"+id, messages) | ||||
|             } | ||||
| 					Instantiator { | ||||
| 						model: selectorModel.items | ||||
| 						delegate: MenuItem { | ||||
| 							text: model.text | ||||
| 							onTriggered: { | ||||
| 								selectorModel.accept(index) | ||||
| 							} | ||||
| 						} | ||||
| 						onObjectAdded: itemSelector.insertItem(index, object) | ||||
| 						onObjectRemoved: itemSelector.removeItem(object) | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
|             function onNewBlockCb(block) { | ||||
|                 postEvent("block:new", block) | ||||
|             } | ||||
|             function onObjectChangeCb(stateObject) { | ||||
|                 postEvent("object:"+stateObject.address(), stateObject) | ||||
|             } | ||||
|             function onStorageChangeCb(storageObject) { | ||||
|                 var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); | ||||
|                 postEvent(ev, [storageObject.address, storageObject.value]) | ||||
|             } | ||||
|         } | ||||
| 				Component.onCompleted: { | ||||
| 					itemSelector.popup() | ||||
| 				} | ||||
| 			} | ||||
| 			experimental.preferences.webAudioEnabled: true | ||||
| 			experimental.preferences.navigatorQtObjectEnabled: true | ||||
| 			experimental.preferences.developerExtrasEnabled: true | ||||
| 			experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"] | ||||
| 			experimental.onMessageReceived: { | ||||
| 				console.log("[onMessageReceived]: ", message.data) | ||||
| 				// TODO move to messaging.js | ||||
| 				var data = JSON.parse(message.data) | ||||
| 
 | ||||
| 				try { | ||||
| 					switch(data.call) { | ||||
| 						case "eth_compile": | ||||
| 						postData(data._id, eth.compile(data.args[0])) | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_coinbase": | ||||
| 						postData(data._id, eth.coinBase()) | ||||
| 
 | ||||
| 						case "eth_account": | ||||
| 						postData(data._id, eth.key().address); | ||||
| 
 | ||||
| 						case "eth_istening": | ||||
| 						postData(data._id, eth.isListening()) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_mining": | ||||
| 						postData(data._id, eth.isMining()) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_peerCount": | ||||
| 						postData(data._id, eth.peerCount()) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_countAt": | ||||
| 						require(1) | ||||
| 						postData(data._id, eth.txCountAt(data.args[0])) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_codeAt": | ||||
| 						require(1) | ||||
| 						var code = eth.codeAt(data.args[0]) | ||||
| 						postData(data._id, code); | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_blockByNumber": | ||||
| 						require(1) | ||||
| 						var block = eth.blockByNumber(data.args[0]) | ||||
| 						postData(data._id, block) | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_blockByHash": | ||||
| 						require(1) | ||||
| 						var block = eth.blockByHash(data.args[0]) | ||||
| 						postData(data._id, block) | ||||
| 						break | ||||
| 
 | ||||
| 						require(2) | ||||
| 						var block = eth.blockByHash(data.args[0]) | ||||
| 						postData(data._id, block.transactions[data.args[1]]) | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_transactionByHash": | ||||
| 						case "eth_transactionByNumber": | ||||
| 						require(2) | ||||
| 
 | ||||
| 						var block; | ||||
| 						if (data.call === "transactionByHash") | ||||
| 						block = eth.blockByHash(data.args[0]) | ||||
| 						else | ||||
| 						block = eth.blockByNumber(data.args[0]) | ||||
| 
 | ||||
| 						var tx = block.transactions.get(data.args[1]) | ||||
| 
 | ||||
| 						postData(data._id, tx) | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_uncleByHash": | ||||
| 						case "eth_uncleByNumber": | ||||
| 						require(2) | ||||
| 
 | ||||
| 						var block; | ||||
| 						if (data.call === "uncleByHash") | ||||
| 						block = eth.blockByHash(data.args[0]) | ||||
| 						else | ||||
| 						block = eth.blockByNumber(data.args[0]) | ||||
| 
 | ||||
| 						var uncle = block.uncles.get(data.args[1]) | ||||
| 
 | ||||
| 						postData(data._id, uncle) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "transact": | ||||
| 						require(5) | ||||
| 
 | ||||
| 						var tx = eth.transact(data.args) | ||||
| 						postData(data._id, tx) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_stateAt": | ||||
| 						require(2); | ||||
| 
 | ||||
| 						var storage = eth.storageAt(data.args[0], data.args[1]); | ||||
| 						postData(data._id, storage) | ||||
| 
 | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_call": | ||||
| 						require(1); | ||||
| 						var ret = eth.call(data.args) | ||||
| 						postData(data._id, ret) | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_balanceAt": | ||||
| 						require(1); | ||||
| 
 | ||||
| 						postData(data._id, eth.balanceAt(data.args[0])); | ||||
| 						break | ||||
| 
 | ||||
| 						case "eth_watch": | ||||
| 						require(2) | ||||
| 						eth.watch(data.args[0], data.args[1]) | ||||
| 
 | ||||
| 						case "eth_disconnect": | ||||
| 						require(1) | ||||
| 						postData(data._id, null) | ||||
| 						break; | ||||
| 
 | ||||
| 						case "eth_newFilterString": | ||||
| 						require(1) | ||||
| 						var id = eth.newFilterString(data.args[0]) | ||||
| 						postData(data._id, id); | ||||
| 						break; | ||||
| 
 | ||||
| 						case "eth_newFilter": | ||||
| 						require(1) | ||||
| 						var id = eth.newFilter(data.args[0]) | ||||
| 
 | ||||
| 						postData(data._id, id); | ||||
| 						break; | ||||
| 
 | ||||
| 						case "eth_filterLogs": | ||||
| 						require(1); | ||||
| 
 | ||||
| 						var messages = eth.messages(data.args[0]); | ||||
| 						var m = JSON.parse(JSON.parse(JSON.stringify(messages))) | ||||
| 						postData(data._id, m); | ||||
| 
 | ||||
| 						break; | ||||
| 
 | ||||
| 						case "eth_deleteFilter": | ||||
| 						require(1); | ||||
| 						eth.uninstallFilter(data.args[0]) | ||||
| 						break; | ||||
| 
 | ||||
| 
 | ||||
|         Rectangle { | ||||
|             id: sizeGrip | ||||
|             color: "gray" | ||||
|             visible: false | ||||
|             height: 10 | ||||
|             anchors { | ||||
|                 left: root.left | ||||
|                 right: root.right | ||||
|             } | ||||
|             y: Math.round(root.height * 2 / 3) | ||||
| 						case "shh_newFilter": | ||||
| 						require(1); | ||||
| 						var id = shh.watch(data.args[0], window); | ||||
| 						postData(data._id, id); | ||||
| 						break; | ||||
| 
 | ||||
|             MouseArea { | ||||
|                 anchors.fill: parent | ||||
|                 drag.target: sizeGrip | ||||
|                 drag.minimumY: 0 | ||||
|                 drag.maximumY: root.height | ||||
|                 drag.axis: Drag.YAxis | ||||
|             } | ||||
|         } | ||||
| 						case "shh_newIdentity": | ||||
| 						var id = shh.newIdentity() | ||||
| 						postData(data._id, id) | ||||
| 
 | ||||
|         WebView { | ||||
|             id: inspector | ||||
|             visible: false | ||||
|             anchors { | ||||
|                 left: root.left | ||||
|                 right: root.right | ||||
|                 top: sizeGrip.bottom | ||||
|                 bottom: root.bottom | ||||
|             } | ||||
|         } | ||||
| 						break | ||||
| 
 | ||||
|         states: [ | ||||
|             State { | ||||
|                 name: "inspectorShown" | ||||
|                 PropertyChanges { | ||||
|                     target: inspector | ||||
|                 } | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| 						case "shh_post": | ||||
| 						require(1); | ||||
| 
 | ||||
| 						var params = data.args[0]; | ||||
| 						var fields = ["payload", "to", "from"]; | ||||
| 						for(var i = 0; i < fields.length; i++) { | ||||
| 							params[fields[i]] = params[fields[i]] || ""; | ||||
| 						} | ||||
| 						if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); } | ||||
| 						params.topics = params.topics || []; | ||||
| 						params.priority = params.priority || 1000; | ||||
| 						params.ttl = params.ttl || 100; | ||||
| 
 | ||||
| 						shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl); | ||||
| 
 | ||||
| 						break; | ||||
| 
 | ||||
| 						case "shh_getMessages": | ||||
| 						require(1); | ||||
| 
 | ||||
| 						var m = shh.messages(data.args[0]); | ||||
| 						var messages = JSON.parse(JSON.parse(JSON.stringify(m))); | ||||
| 						postData(data._id, messages); | ||||
| 
 | ||||
| 						break; | ||||
| 
 | ||||
| 						case "ssh_newGroup": | ||||
| 						postData(data._id, ""); | ||||
| 						break; | ||||
| 					} | ||||
| 				} catch(e) { | ||||
| 					console.log(data.call + ": " + e) | ||||
| 
 | ||||
| 					postData(data._id, null); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 
 | ||||
| 			function post(seed, data) { | ||||
| 				postData(data._id, data) | ||||
| 			} | ||||
| 
 | ||||
| 			function require(args, num) { | ||||
| 				if(args.length < num) { | ||||
| 					throw("required argument count of "+num+" got "+args.length); | ||||
| 				} | ||||
| 			} | ||||
| 			function postData(seed, data) { | ||||
| 				webview.experimental.postMessage(JSON.stringify({data: data, _id: seed})) | ||||
| 			} | ||||
| 			function postEvent(event, id, data) { | ||||
| 				webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event})) | ||||
| 			} | ||||
| 
 | ||||
| 			function onWatchedCb(data, id) { | ||||
| 				var messages = JSON.parse(data) | ||||
| 				postEvent("watched:"+id, messages) | ||||
| 			} | ||||
| 
 | ||||
| 			function onNewBlockCb(block) { | ||||
| 				postEvent("block:new", block) | ||||
| 			} | ||||
| 			function onObjectChangeCb(stateObject) { | ||||
| 				postEvent("object:"+stateObject.address(), stateObject) | ||||
| 			} | ||||
| 			function onStorageChangeCb(storageObject) { | ||||
| 				var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); | ||||
| 				postEvent(ev, [storageObject.address, storageObject.value]) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 		Rectangle { | ||||
| 			id: sizeGrip | ||||
| 			color: "gray" | ||||
| 			visible: false | ||||
| 			height: 10 | ||||
| 			anchors { | ||||
| 				left: root.left | ||||
| 				right: root.right | ||||
| 			} | ||||
| 			y: Math.round(root.height * 2 / 3) | ||||
| 
 | ||||
| 			MouseArea { | ||||
| 				anchors.fill: parent | ||||
| 				drag.target: sizeGrip | ||||
| 				drag.minimumY: 0 | ||||
| 				drag.maximumY: root.height | ||||
| 				drag.axis: Drag.YAxis | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		WebView { | ||||
| 			id: inspector | ||||
| 			visible: false | ||||
| 			anchors { | ||||
| 				left: root.left | ||||
| 				right: root.right | ||||
| 				top: sizeGrip.bottom | ||||
| 				bottom: root.bottom | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		states: [ | ||||
| 			State { | ||||
| 				name: "inspectorShown" | ||||
| 				PropertyChanges { | ||||
| 					target: inspector | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -59,8 +59,19 @@ ApplicationWindow { | ||||
| 
 | ||||
| 		mainSplit.setView(wallet.view, wallet.menuItem); | ||||
| 
 | ||||
| 		// Call the ready handler | ||||
| 		gui.done(); | ||||
| 		// Command setup | ||||
| 		gui.sendCommand(0) | ||||
| 	} | ||||
| 
 | ||||
| 	function activeView(view, menuItem) { | ||||
| 		mainSplit.setView(view, menuItem) | ||||
| 		if (view.objectName === "browserView") { | ||||
| 			urlPane.visible = false; | ||||
| 			mainView.anchors.top = rootView.top | ||||
| 		} else { | ||||
| 			urlPane.visible = true; | ||||
| 			mainView.anchors.top = divider.bottom | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	function addViews(view, path, options) { | ||||
| @ -284,6 +295,7 @@ ApplicationWindow { | ||||
| 		} | ||||
| 
 | ||||
| 		ProgressBar { | ||||
| 			visible: false | ||||
| 			id: downloadIndicator | ||||
| 			value: 0 | ||||
| 			objectName: "downloadIndicator" | ||||
| @ -293,6 +305,7 @@ ApplicationWindow { | ||||
| 		} | ||||
| 
 | ||||
| 		Label { | ||||
| 			visible: false | ||||
| 			objectName: "downloadLabel" | ||||
| 			//y: 7 | ||||
| 			anchors.left: downloadIndicator.right | ||||
| @ -445,7 +458,7 @@ ApplicationWindow { | ||||
| 					 MouseArea { | ||||
| 						 anchors.fill: parent | ||||
| 						 onClicked: { | ||||
| 							 mainSplit.setView(view, menuItem) | ||||
| 							 activeView(view, menuItem); | ||||
| 						 } | ||||
| 					 } | ||||
| 
 | ||||
| @ -512,14 +525,14 @@ ApplicationWindow { | ||||
| 				 var section; | ||||
| 				 switch(options.section) { | ||||
| 					 case "ethereum": | ||||
| 						section = menuDefault; | ||||
| 						break; | ||||
| 					 section = menuDefault; | ||||
| 					 break; | ||||
| 					 case "legacy": | ||||
| 						section = menuLegacy; | ||||
| 						break; | ||||
| 					 section = menuLegacy; | ||||
| 					 break; | ||||
| 					 default: | ||||
| 						section = menuApps; | ||||
| 						break; | ||||
| 					 section = menuApps; | ||||
| 					 break; | ||||
| 				 } | ||||
| 
 | ||||
| 				 var comp = menuItemTemplate.createObject(section) | ||||
| @ -606,6 +619,7 @@ ApplicationWindow { | ||||
| 		  * Main view | ||||
| 		  ********************/ | ||||
| 		  Rectangle { | ||||
| 			  id: rootView | ||||
| 			  anchors.right: parent.right | ||||
| 			  anchors.left: menu.right | ||||
| 			  anchors.bottom: parent.bottom | ||||
| @ -639,8 +653,7 @@ ApplicationWindow { | ||||
| 
 | ||||
| 					  Keys.onReturnPressed: { | ||||
| 						  if(/^https?/.test(this.text)) { | ||||
| 							  root.browser.view.open(this.text); | ||||
| 							  mainSplit.setView(root.browser.view, root.browser.menuItem); | ||||
| 							  activeView(root.browser.view, root.browser.menuItem); | ||||
| 						  } else { | ||||
| 							  addPlugin(this.text, {close: true, section: "apps"}) | ||||
| 						  } | ||||
| @ -864,13 +877,13 @@ ApplicationWindow { | ||||
| 			     Component.onCompleted: { | ||||
| 				     pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"}) | ||||
| 				     /* | ||||
| 				     var ips = eth.pastPeers() | ||||
| 				     for(var i = 0; i < ips.length; i++) { | ||||
| 					     pastPeers.append({text: ips.get(i)}) | ||||
| 				     } | ||||
| 				      var ips = eth.pastPeers() | ||||
| 				      for(var i = 0; i < ips.length; i++) { | ||||
| 					      pastPeers.append({text: ips.get(i)}) | ||||
| 				      } | ||||
| 
 | ||||
| 				     pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) | ||||
| 				     */ | ||||
| 				      pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) | ||||
| 				      */ | ||||
| 			     } | ||||
| 		     } | ||||
| 
 | ||||
|  | ||||
| @ -46,6 +46,7 @@ Rectangle { | ||||
| 						text: "Start" | ||||
| 						onClicked: { | ||||
| 							eth.setGasPrice(minGasPrice.text || "10000000000000"); | ||||
| 							eth.setExtra(blockExtra.text) | ||||
| 							if (eth.toggleMining()) { | ||||
| 								this.text = "Stop"; | ||||
| 							} else { | ||||
| @ -55,6 +56,7 @@ Rectangle { | ||||
| 					} | ||||
| 
 | ||||
| 					Rectangle { | ||||
| 						id: minGasPriceRect | ||||
| 						anchors.top: parent.top | ||||
| 						anchors.topMargin: 2 | ||||
| 						width: 200 | ||||
| @ -65,6 +67,23 @@ Rectangle { | ||||
| 							validator: RegExpValidator { regExp: /\d*/ } | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					Rectangle { | ||||
| 						width: 300 | ||||
| 						anchors { | ||||
| 							left: minGasPriceRect.right | ||||
| 							leftMargin: 5 | ||||
| 							top: parent.top | ||||
| 							topMargin: 2 | ||||
| 						} | ||||
| 
 | ||||
| 						TextField { | ||||
| 							id: blockExtra | ||||
| 							placeholderText: "Extra" | ||||
| 							width: parent.width | ||||
| 							maximumLength: 1024 | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -26,6 +29,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| ) | ||||
| 
 | ||||
| type plugin struct { | ||||
| @ -118,7 +122,7 @@ func (self *Gui) DumpState(hash, path string) { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		stateDump = block.State().Dump() | ||||
| 		stateDump = state.New(block.Root(), self.eth.Db()).Dump() | ||||
| 	} | ||||
| 
 | ||||
| 	file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm) | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -37,7 +40,7 @@ type DebuggerWindow struct { | ||||
| 	engine *qml.Engine | ||||
| 	lib    *UiLib | ||||
| 
 | ||||
| 	vm *vm.DebugVm | ||||
| 	vm *vm.Vm | ||||
| 	Db *Debugger | ||||
| 
 | ||||
| 	state *state.StateDB | ||||
| @ -54,7 +57,7 @@ func NewDebuggerWindow(lib *UiLib) *DebuggerWindow { | ||||
| 
 | ||||
| 	win := component.CreateWindow(nil) | ||||
| 
 | ||||
| 	w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.DebugVm{}} | ||||
| 	w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}} | ||||
| 	w.Db = NewDebugger(w) | ||||
| 
 | ||||
| 	return w | ||||
| @ -264,6 +267,9 @@ type storeVal struct { | ||||
| 	Key, Value string | ||||
| } | ||||
| 
 | ||||
| func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) { | ||||
| } | ||||
| 
 | ||||
| func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool { | ||||
| 	self.main.Logln("break on instr:", pc) | ||||
| 
 | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -89,7 +92,7 @@ func defaultAssetPath() string { | ||||
| } | ||||
| func defaultDataDir() string { | ||||
| 	usr, _ := user.Current() | ||||
| 	return path.Join(usr.HomeDir, ".mist") | ||||
| 	return path.Join(usr.HomeDir, ".ethereum") | ||||
| } | ||||
| 
 | ||||
| var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini") | ||||
|  | ||||
							
								
								
									
										296
									
								
								cmd/mist/gui.go
									
									
									
									
									
								
							
							
						
						
									
										296
									
								
								cmd/mist/gui.go
									
									
									
									
									
								
							| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import "C" | ||||
| @ -23,7 +26,9 @@ import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"math/big" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| @ -45,15 +50,22 @@ import ( | ||||
| 
 | ||||
| var guilogger = logger.NewLogger("GUI") | ||||
| 
 | ||||
| type ServEv byte | ||||
| 
 | ||||
| const ( | ||||
| 	setup ServEv = iota | ||||
| 	update | ||||
| ) | ||||
| 
 | ||||
| type Gui struct { | ||||
| 	// The main application window
 | ||||
| 	win *qml.Window | ||||
| 	// QML Engine
 | ||||
| 	engine    *qml.Engine | ||||
| 	component *qml.Common | ||||
| 	qmlDone   bool | ||||
| 	// The ethereum interface
 | ||||
| 	eth *eth.Ethereum | ||||
| 	eth           *eth.Ethereum | ||||
| 	serviceEvents chan ServEv | ||||
| 
 | ||||
| 	// The public Ethereum library
 | ||||
| 	uiLib   *UiLib | ||||
| @ -83,7 +95,17 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden | ||||
| 	} | ||||
| 
 | ||||
| 	xeth := xeth.NewJSXEth(ethereum) | ||||
| 	gui := &Gui{eth: ethereum, txDb: db, xeth: xeth, logLevel: logger.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)} | ||||
| 	gui := &Gui{eth: ethereum, | ||||
| 		txDb:           db, | ||||
| 		xeth:           xeth, | ||||
| 		logLevel:       logger.LogLevel(logLevel), | ||||
| 		Session:        session, | ||||
| 		open:           false, | ||||
| 		clientIdentity: clientIdentity, | ||||
| 		config:         config, | ||||
| 		plugins:        make(map[string]plugin), | ||||
| 		serviceEvents:  make(chan ServEv, 1), | ||||
| 	} | ||||
| 	data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json")) | ||||
| 	json.Unmarshal([]byte(data), &gui.plugins) | ||||
| 
 | ||||
| @ -95,6 +117,8 @@ func (gui *Gui) Start(assetPath string) { | ||||
| 
 | ||||
| 	guilogger.Infoln("Starting GUI") | ||||
| 
 | ||||
| 	go gui.service() | ||||
| 
 | ||||
| 	// Register ethereum functions
 | ||||
| 	qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ | ||||
| 		Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" }, | ||||
| @ -114,18 +138,7 @@ func (gui *Gui) Start(assetPath string) { | ||||
| 	context.SetVar("eth", gui.uiLib) | ||||
| 	context.SetVar("shh", gui.whisper) | ||||
| 
 | ||||
| 	// Load the main QML interface
 | ||||
| 	data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) | ||||
| 
 | ||||
| 	var win *qml.Window | ||||
| 	var err error | ||||
| 	var addlog = false | ||||
| 	if len(data) == 0 { | ||||
| 		win, err = gui.showKeyImport(context) | ||||
| 	} else { | ||||
| 		win, err = gui.showWallet(context) | ||||
| 		addlog = true | ||||
| 	} | ||||
| 	win, err := gui.showWallet(context) | ||||
| 	if err != nil { | ||||
| 		guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err) | ||||
| 
 | ||||
| @ -136,9 +149,7 @@ func (gui *Gui) Start(assetPath string) { | ||||
| 	win.Show() | ||||
| 
 | ||||
| 	// only add the gui guilogger after window is shown otherwise slider wont be shown
 | ||||
| 	if addlog { | ||||
| 		logger.AddLogSystem(gui) | ||||
| 	} | ||||
| 	logger.AddLogSystem(gui) | ||||
| 	win.Wait() | ||||
| 
 | ||||
| 	// need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
 | ||||
| @ -164,18 +175,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	gui.win = gui.createWindow(component) | ||||
| 
 | ||||
| 	gui.update() | ||||
| 	gui.createWindow(component) | ||||
| 
 | ||||
| 	return gui.win, nil | ||||
| } | ||||
| 
 | ||||
| // The done handler will be called by QML when all views have been loaded
 | ||||
| func (gui *Gui) Done() { | ||||
| 	gui.qmlDone = true | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) ImportKey(filePath string) { | ||||
| } | ||||
| 
 | ||||
| @ -189,10 +193,8 @@ func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) { | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) createWindow(comp qml.Object) *qml.Window { | ||||
| 	win := comp.CreateWindow(nil) | ||||
| 
 | ||||
| 	gui.win = win | ||||
| 	gui.uiLib.win = win | ||||
| 	gui.win = comp.CreateWindow(nil) | ||||
| 	gui.uiLib.win = gui.win | ||||
| 
 | ||||
| 	return gui.win | ||||
| } | ||||
| @ -272,7 +274,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) { | ||||
| 	} | ||||
| 
 | ||||
| 	var ( | ||||
| 		ptx  = xeth.NewJSTx(tx, gui.xeth.World().State()) | ||||
| 		ptx  = xeth.NewJSTx(tx) | ||||
| 		send = nameReg.Storage(tx.From()) | ||||
| 		rec  = nameReg.Storage(tx.To()) | ||||
| 		s, r string | ||||
| @ -345,11 +347,48 @@ func (self *Gui) getObjectByName(objectName string) qml.Object { | ||||
| 	return self.win.Root().ObjectByName(objectName) | ||||
| } | ||||
| 
 | ||||
| // Simple go routine function that updates the list of peers in the GUI
 | ||||
| func (gui *Gui) update() { | ||||
| 	// We have to wait for qml to be done loading all the windows.
 | ||||
| 	for !gui.qmlDone { | ||||
| 		time.Sleep(300 * time.Millisecond) | ||||
| func loadJavascriptAssets(gui *Gui) (jsfiles string) { | ||||
| 	for _, fn := range []string{"ext/q.js", "ext/eth.js/main.js", "ext/eth.js/qt.js", "ext/setup.js"} { | ||||
| 		f, err := os.Open(gui.uiLib.AssetPath(fn)) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		content, err := ioutil.ReadAll(f) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			continue | ||||
| 		} | ||||
| 		jsfiles += string(content) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) SendCommand(cmd ServEv) { | ||||
| 	gui.serviceEvents <- cmd | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) service() { | ||||
| 	for ev := range gui.serviceEvents { | ||||
| 		switch ev { | ||||
| 		case setup: | ||||
| 			go gui.setup() | ||||
| 		case update: | ||||
| 			go gui.update() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) setup() { | ||||
| 	for gui.win == nil { | ||||
| 		time.Sleep(time.Millisecond * 200) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, plugin := range gui.plugins { | ||||
| 		guilogger.Infoln("Loading plugin ", plugin.Name) | ||||
| 		gui.win.Root().Call("addPlugin", plugin.Path, "") | ||||
| 	} | ||||
| 
 | ||||
| 	go func() { | ||||
| @ -359,14 +398,21 @@ func (gui *Gui) update() { | ||||
| 		gui.setPeerInfo() | ||||
| 	}() | ||||
| 
 | ||||
| 	gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView")) | ||||
| 	// Inject javascript files each time navigation is requested.
 | ||||
| 	// Unfortunately webview.experimental.userScripts injects _after_
 | ||||
| 	// the page has loaded which kind of renders it useless...
 | ||||
| 	//jsfiles := loadJavascriptAssets(gui)
 | ||||
| 	gui.getObjectByName("webView").On("navigationRequested", func() { | ||||
| 		//gui.getObjectByName("webView").Call("injectJs", jsfiles)
 | ||||
| 	}) | ||||
| 
 | ||||
| 	for _, plugin := range gui.plugins { | ||||
| 		guilogger.Infoln("Loading plugin ", plugin.Name) | ||||
| 	gui.whisper.SetView(gui.getObjectByName("whisperView")) | ||||
| 
 | ||||
| 		gui.win.Root().Call("addPlugin", plugin.Path, "") | ||||
| 	} | ||||
| 	gui.SendCommand(update) | ||||
| } | ||||
| 
 | ||||
| // Simple go routine function that updates the list of peers in the GUI
 | ||||
| func (gui *Gui) update() { | ||||
| 	peerUpdateTicker := time.NewTicker(5 * time.Second) | ||||
| 	generalUpdateTicker := time.NewTicker(500 * time.Millisecond) | ||||
| 	statsUpdateTicker := time.NewTicker(5 * time.Second) | ||||
| @ -385,77 +431,75 @@ func (gui *Gui) update() { | ||||
| 		core.TxPostEvent{}, | ||||
| 	) | ||||
| 
 | ||||
| 	go func() { | ||||
| 		defer events.Unsubscribe() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case ev, isopen := <-events.Chan(): | ||||
| 				if !isopen { | ||||
| 					return | ||||
| 				} | ||||
| 				switch ev := ev.(type) { | ||||
| 				case core.NewBlockEvent: | ||||
| 					gui.processBlock(ev.Block, false) | ||||
| 					if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 { | ||||
| 						gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil) | ||||
| 					} | ||||
| 
 | ||||
| 				case core.TxPreEvent: | ||||
| 					tx := ev.Tx | ||||
| 
 | ||||
| 					tstate := gui.eth.ChainManager().TransState() | ||||
| 					cstate := gui.eth.ChainManager().State() | ||||
| 
 | ||||
| 					taccount := tstate.GetAccount(gui.address()) | ||||
| 					caccount := cstate.GetAccount(gui.address()) | ||||
| 					unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance()) | ||||
| 
 | ||||
| 					gui.setWalletValue(taccount.Balance(), unconfirmedFunds) | ||||
| 					gui.insertTransaction("pre", tx) | ||||
| 
 | ||||
| 				case core.TxPostEvent: | ||||
| 					tx := ev.Tx | ||||
| 					object := state.GetAccount(gui.address()) | ||||
| 
 | ||||
| 					if bytes.Compare(tx.From(), gui.address()) == 0 { | ||||
| 						object.SubAmount(tx.Value()) | ||||
| 
 | ||||
| 						gui.txDb.Put(tx.Hash(), tx.RlpEncode()) | ||||
| 					} else if bytes.Compare(tx.To(), gui.address()) == 0 { | ||||
| 						object.AddAmount(tx.Value()) | ||||
| 
 | ||||
| 						gui.txDb.Put(tx.Hash(), tx.RlpEncode()) | ||||
| 					} | ||||
| 
 | ||||
| 					gui.setWalletValue(object.Balance(), nil) | ||||
| 					state.UpdateStateObject(object) | ||||
| 				} | ||||
| 
 | ||||
| 			case <-peerUpdateTicker.C: | ||||
| 				gui.setPeerInfo() | ||||
| 			case <-generalUpdateTicker.C: | ||||
| 				statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String() | ||||
| 				lastBlockLabel.Set("text", statusText) | ||||
| 				miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash") | ||||
| 
 | ||||
| 				/* | ||||
| 					blockLength := gui.eth.BlockPool().BlocksProcessed | ||||
| 					chainLength := gui.eth.BlockPool().ChainLength | ||||
| 
 | ||||
| 					var ( | ||||
| 						pct      float64 = 1.0 / float64(chainLength) * float64(blockLength) | ||||
| 						dlWidget         = gui.win.Root().ObjectByName("downloadIndicator") | ||||
| 						dlLabel          = gui.win.Root().ObjectByName("downloadLabel") | ||||
| 					) | ||||
| 					dlWidget.Set("value", pct) | ||||
| 					dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength)) | ||||
| 				*/ | ||||
| 
 | ||||
| 			case <-statsUpdateTicker.C: | ||||
| 				gui.setStatsPane() | ||||
| 	defer events.Unsubscribe() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case ev, isopen := <-events.Chan(): | ||||
| 			if !isopen { | ||||
| 				return | ||||
| 			} | ||||
| 			switch ev := ev.(type) { | ||||
| 			case core.NewBlockEvent: | ||||
| 				gui.processBlock(ev.Block, false) | ||||
| 				if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 { | ||||
| 					gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil) | ||||
| 				} | ||||
| 
 | ||||
| 			case core.TxPreEvent: | ||||
| 				tx := ev.Tx | ||||
| 
 | ||||
| 				tstate := gui.eth.ChainManager().TransState() | ||||
| 				cstate := gui.eth.ChainManager().State() | ||||
| 
 | ||||
| 				taccount := tstate.GetAccount(gui.address()) | ||||
| 				caccount := cstate.GetAccount(gui.address()) | ||||
| 				unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance()) | ||||
| 
 | ||||
| 				gui.setWalletValue(taccount.Balance(), unconfirmedFunds) | ||||
| 				gui.insertTransaction("pre", tx) | ||||
| 
 | ||||
| 			case core.TxPostEvent: | ||||
| 				tx := ev.Tx | ||||
| 				object := state.GetAccount(gui.address()) | ||||
| 
 | ||||
| 				if bytes.Compare(tx.From(), gui.address()) == 0 { | ||||
| 					object.SubAmount(tx.Value()) | ||||
| 
 | ||||
| 					gui.txDb.Put(tx.Hash(), tx.RlpEncode()) | ||||
| 				} else if bytes.Compare(tx.To(), gui.address()) == 0 { | ||||
| 					object.AddAmount(tx.Value()) | ||||
| 
 | ||||
| 					gui.txDb.Put(tx.Hash(), tx.RlpEncode()) | ||||
| 				} | ||||
| 
 | ||||
| 				gui.setWalletValue(object.Balance(), nil) | ||||
| 				state.UpdateStateObject(object) | ||||
| 			} | ||||
| 
 | ||||
| 		case <-peerUpdateTicker.C: | ||||
| 			gui.setPeerInfo() | ||||
| 		case <-generalUpdateTicker.C: | ||||
| 			statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String() | ||||
| 			lastBlockLabel.Set("text", statusText) | ||||
| 			miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash") | ||||
| 
 | ||||
| 			/* | ||||
| 				blockLength := gui.eth.BlockPool().BlocksProcessed | ||||
| 				chainLength := gui.eth.BlockPool().ChainLength | ||||
| 
 | ||||
| 				var ( | ||||
| 					pct      float64 = 1.0 / float64(chainLength) * float64(blockLength) | ||||
| 					dlWidget         = gui.win.Root().ObjectByName("downloadIndicator") | ||||
| 					dlLabel          = gui.win.Root().ObjectByName("downloadLabel") | ||||
| 				) | ||||
| 				dlWidget.Set("value", pct) | ||||
| 				dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength)) | ||||
| 			*/ | ||||
| 
 | ||||
| 		case <-statsUpdateTicker.C: | ||||
| 			gui.setStatsPane() | ||||
| 		} | ||||
| 	}() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (gui *Gui) setStatsPane() { | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,19 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
|  | ||||
| @ -1,20 +1,23 @@ | ||||
| // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
 | ||||
| //
 | ||||
| // This library is free software; you can redistribute it and/or
 | ||||
| // modify it under the terms of the GNU General Public
 | ||||
| // License as published by the Free Software Foundation; either
 | ||||
| // version 2.1 of the License, or (at your option) any later version.
 | ||||
| //
 | ||||
| // This library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||||
| // General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this library; if not, write to the Free Software
 | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | ||||
| // MA 02110-1301  USA
 | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| @ -67,6 +70,7 @@ func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { | ||||
| 	lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
 | ||||
| 	lib.miner = miner.New(eth.KeyManager().Address(), eth) | ||||
| 	lib.filterManager = filter.NewFilterManager(eth.EventMux()) | ||||
| 	go lib.filterManager.Start() | ||||
| 
 | ||||
| 	return lib | ||||
| } | ||||
| @ -208,16 +212,16 @@ func (self *UiLib) StartDbWithContractAndData(contractHash, data string) { | ||||
| 	dbWindow := NewDebuggerWindow(self) | ||||
| 	object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash)) | ||||
| 	if len(object.Code) > 0 { | ||||
| 		dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code)) | ||||
| 		dbWindow.SetCode(ethutil.Bytes2Hex(object.Code)) | ||||
| 	} | ||||
| 	dbWindow.SetData("0x" + data) | ||||
| 	dbWindow.SetData(data) | ||||
| 
 | ||||
| 	dbWindow.Show() | ||||
| } | ||||
| 
 | ||||
| func (self *UiLib) StartDbWithCode(code string) { | ||||
| 	dbWindow := NewDebuggerWindow(self) | ||||
| 	dbWindow.SetCode("0x" + code) | ||||
| 	dbWindow.SetCode(code) | ||||
| 	dbWindow.Show() | ||||
| } | ||||
| 
 | ||||
| @ -279,6 +283,10 @@ func (self *UiLib) SetGasPrice(price string) { | ||||
| 	self.miner.MinAcceptedGasPrice = ethutil.Big(price) | ||||
| } | ||||
| 
 | ||||
| func (self *UiLib) SetExtra(extra string) { | ||||
| 	self.miner.Extra = extra | ||||
| } | ||||
| 
 | ||||
| func (self *UiLib) ToggleMining() bool { | ||||
| 	if !self.miner.Mining() { | ||||
| 		self.miner.Start() | ||||
|  | ||||
| @ -1,10 +1,25 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/elliptic" | ||||
| 	"fmt" | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| @ -12,29 +27,32 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/p2p" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	natType    = flag.String("nat", "", "NAT traversal implementation") | ||||
| 	pmpGateway = flag.String("gateway", "", "gateway address for NAT-PMP") | ||||
| 	listenAddr = flag.String("addr", ":30301", "listen address") | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	nat, err := p2p.ParseNAT(*natType, *pmpGateway) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("invalid nat:", err) | ||||
| 	} | ||||
| 
 | ||||
| 	logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel)) | ||||
| 	key, _ := crypto.GenerateKey() | ||||
| 	marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) | ||||
| 
 | ||||
| 	srv := p2p.Server{ | ||||
| 		MaxPeers:   100, | ||||
| 		Identity:   p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)), | ||||
| 		ListenAddr: ":30301", | ||||
| 		NAT:        p2p.UPNP(), | ||||
| 		Identity:   p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", marshaled), | ||||
| 		ListenAddr: *listenAddr, | ||||
| 		NAT:        nat, | ||||
| 		NoDial:     true, | ||||
| 	} | ||||
| 	if err := srv.Start(); err != nil { | ||||
| 		fmt.Println("could not start server:", err) | ||||
| 		os.Exit(1) | ||||
| 		log.Fatal("could not start server:", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// add seed peers
 | ||||
| 	seed, err := net.ResolveTCPAddr("tcp", "poc-8.ethdev.com:30303") | ||||
| 	if err != nil { | ||||
| 		fmt.Println("couldn't resolve:", err) | ||||
| 	} else { | ||||
| 		srv.SuggestPeer(seed.IP, seed.Port, nil) | ||||
| 	} | ||||
| 
 | ||||
| 	select {} | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,10 @@ | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Felix Lange <felix@ethdev.com> | ||||
|  */ | ||||
| 
 | ||||
| // rlpdump is a pretty-printer for RLP data.
 | ||||
| package main | ||||
| @ -106,8 +110,7 @@ func dump(s *rlp.Stream, depth int) error { | ||||
| 		s.List() | ||||
| 		defer s.ListEnd() | ||||
| 		if size == 0 { | ||||
| 			fmt.Printf(ws(depth) + "[]") | ||||
| 			return nil | ||||
| 			fmt.Print(ws(depth) + "[]") | ||||
| 		} else { | ||||
| 			fmt.Println(ws(depth) + "[") | ||||
| 			for i := 0; ; i++ { | ||||
|  | ||||
| @ -1,3 +1,24 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  * 	Viktor Tron <viktor@ethdev.com> | ||||
|  */ | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| @ -18,6 +39,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/miner" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 	"github.com/ethereum/go-ethereum/xeth" | ||||
| ) | ||||
| 
 | ||||
| @ -238,7 +260,8 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error { | ||||
| 
 | ||||
| 	parent := ethereum.ChainManager().GetBlock(block.ParentHash()) | ||||
| 
 | ||||
| 	_, err := ethereum.BlockProcessor().TransitionState(parent.State(), parent, block) | ||||
| 	statedb := state.New(parent.Root(), ethereum.Db()) | ||||
| 	_, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @ -1,3 +1,23 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
|  | ||||
| @ -1,9 +1,34 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Jeffrey Wilcke <i@jev.io> | ||||
|  */ | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/eth" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/event/filter" | ||||
| 	"github.com/ethereum/go-ethereum/logger" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 	"github.com/ethereum/go-ethereum/ui" | ||||
| 	"github.com/ethereum/go-ethereum/websocket" | ||||
| 	"github.com/ethereum/go-ethereum/xeth" | ||||
| ) | ||||
| @ -15,16 +40,19 @@ func args(v ...interface{}) []interface{} { | ||||
| } | ||||
| 
 | ||||
| type WebSocketServer struct { | ||||
| 	ethereum        *eth.Ethereum | ||||
| 	filterCallbacks map[int][]int | ||||
| 	eth           *eth.Ethereum | ||||
| 	filterManager *filter.FilterManager | ||||
| } | ||||
| 
 | ||||
| func NewWebSocketServer(eth *eth.Ethereum) *WebSocketServer { | ||||
| 	return &WebSocketServer{eth, make(map[int][]int)} | ||||
| 	filterManager := filter.NewFilterManager(eth.EventMux()) | ||||
| 	go filterManager.Start() | ||||
| 
 | ||||
| 	return &WebSocketServer{eth, filterManager} | ||||
| } | ||||
| 
 | ||||
| func (self *WebSocketServer) Serv() { | ||||
| 	pipe := xeth.NewJSXEth(self.ethereum) | ||||
| 	pipe := xeth.NewJSXEth(self.eth) | ||||
| 
 | ||||
| 	wsServ := websocket.NewServer("/eth", ":40404") | ||||
| 	wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) { | ||||
| @ -33,74 +61,91 @@ func (self *WebSocketServer) Serv() { | ||||
| 			data := ethutil.NewValue(msg.Args) | ||||
| 			bcode, err := ethutil.Compile(data.Get(0).Str(), false) | ||||
| 			if err != nil { | ||||
| 				c.Write(args(nil, err.Error()), msg.Seed) | ||||
| 				c.Write(args(nil, err.Error()), msg.Id) | ||||
| 			} | ||||
| 
 | ||||
| 			code := ethutil.Bytes2Hex(bcode) | ||||
| 			c.Write(args(code, nil), msg.Seed) | ||||
| 		case "getBlockByNumber": | ||||
| 			c.Write(args(code, nil), msg.Id) | ||||
| 		case "eth_blockByNumber": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			block := pipe.BlockByNumber(int32(args.Get(0).Uint())) | ||||
| 			c.Write(block, msg.Seed) | ||||
| 			c.Write(block, msg.Id) | ||||
| 
 | ||||
| 		case "getKey": | ||||
| 			c.Write(pipe.Key().PrivateKey, msg.Seed) | ||||
| 		case "transact": | ||||
| 		case "eth_blockByHash": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Id) | ||||
| 
 | ||||
| 		case "eth_transact": | ||||
| 			if mp, ok := msg.Args[0].(map[string]interface{}); ok { | ||||
| 				object := mapToTxParams(mp) | ||||
| 				c.Write( | ||||
| 					args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])), | ||||
| 					msg.Seed, | ||||
| 					args(pipe.Transact(pipe.Key().PrivateKey, object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])), | ||||
| 					msg.Id, | ||||
| 				) | ||||
| 
 | ||||
| 			} | ||||
| 		case "getCoinBase": | ||||
| 			c.Write(pipe.CoinBase(), msg.Seed) | ||||
| 		case "eth_gasPrice": | ||||
| 			c.Write("10000000000000", msg.Id) | ||||
| 		case "eth_coinbase": | ||||
| 			c.Write(pipe.CoinBase(), msg.Id) | ||||
| 
 | ||||
| 		case "getIsListening": | ||||
| 			c.Write(pipe.IsListening(), msg.Seed) | ||||
| 		case "eth_listening": | ||||
| 			c.Write(pipe.IsListening(), msg.Id) | ||||
| 
 | ||||
| 		case "getIsMining": | ||||
| 			c.Write(pipe.IsMining(), msg.Seed) | ||||
| 		case "eth_mining": | ||||
| 			c.Write(pipe.IsMining(), msg.Id) | ||||
| 
 | ||||
| 		case "getPeerCoint": | ||||
| 			c.Write(pipe.PeerCount(), msg.Seed) | ||||
| 		case "eth_peerCount": | ||||
| 			c.Write(pipe.PeerCount(), msg.Id) | ||||
| 
 | ||||
| 		case "getCountAt": | ||||
| 		case "eth_countAt": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Seed) | ||||
| 			c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Id) | ||||
| 
 | ||||
| 		case "getCodeAt": | ||||
| 		case "eth_codeAt": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Seed) | ||||
| 			c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Id) | ||||
| 
 | ||||
| 		case "getBlockByHash": | ||||
| 		case "eth_storageAt": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Seed) | ||||
| 			c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Id) | ||||
| 
 | ||||
| 		case "getStorageAt": | ||||
| 		case "eth_balanceAt": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Seed) | ||||
| 			c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Id) | ||||
| 
 | ||||
| 		case "getBalanceAt": | ||||
| 			args := msg.Arguments() | ||||
| 		case "eth_accounts": | ||||
| 			c.Write(pipe.Accounts(), msg.Id) | ||||
| 
 | ||||
| 			c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed) | ||||
| 
 | ||||
| 		case "getSecretToAddress": | ||||
| 			args := msg.Arguments() | ||||
| 
 | ||||
| 			c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed) | ||||
| 
 | ||||
| 		case "newFilter": | ||||
| 		case "newFilterString": | ||||
| 		case "messages": | ||||
| 			// TODO
 | ||||
| 		case "eth_newFilter": | ||||
| 			if mp, ok := msg.Args[0].(map[string]interface{}); ok { | ||||
| 				var id int | ||||
| 				filter := ui.NewFilterFromMap(mp, self.eth) | ||||
| 				filter.MessageCallback = func(messages state.Messages) { | ||||
| 					c.Event(toMessages(messages), "eth_changed", id) | ||||
| 				} | ||||
| 				id = self.filterManager.InstallFilter(filter) | ||||
| 				c.Write(id, msg.Id) | ||||
| 			} | ||||
| 		case "eth_newFilterString": | ||||
| 			var id int | ||||
| 			filter := core.NewFilter(self.eth) | ||||
| 			filter.BlockCallback = func(block *types.Block) { | ||||
| 				c.Event(nil, "eth_changed", id) | ||||
| 			} | ||||
| 			id = self.filterManager.InstallFilter(filter) | ||||
| 			c.Write(id, msg.Id) | ||||
| 		case "eth_filterLogs": | ||||
| 			filter := self.filterManager.GetFilter(int(msg.Arguments().Get(0).Uint())) | ||||
| 			if filter != nil { | ||||
| 				c.Write(toMessages(filter.Find()), msg.Id) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	}) | ||||
| @ -108,6 +153,15 @@ func (self *WebSocketServer) Serv() { | ||||
| 	wsServ.Listen() | ||||
| } | ||||
| 
 | ||||
| func toMessages(messages state.Messages) (msgs []xeth.JSMessage) { | ||||
| 	msgs = make([]xeth.JSMessage, len(messages)) | ||||
| 	for i, msg := range messages { | ||||
| 		msgs[i] = xeth.NewJSMessage(msg) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func StartWebSockets(eth *eth.Ethereum) { | ||||
| 	wslogger.Infoln("Starting WebSockets") | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,6 @@ package core | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"sync" | ||||
| @ -36,6 +35,7 @@ type EthManager interface { | ||||
| } | ||||
| 
 | ||||
| type BlockProcessor struct { | ||||
| 	db ethutil.Database | ||||
| 	// Mutex for locking the block processor. Blocks can only be handled one at a time
 | ||||
| 	mutex sync.Mutex | ||||
| 	// Canonical block chain
 | ||||
| @ -57,8 +57,9 @@ type BlockProcessor struct { | ||||
| 	eventMux *event.TypeMux | ||||
| } | ||||
| 
 | ||||
| func NewBlockProcessor(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { | ||||
| func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { | ||||
| 	sm := &BlockProcessor{ | ||||
| 		db:       db, | ||||
| 		mem:      make(map[string]*big.Int), | ||||
| 		Pow:      ezp.New(), | ||||
| 		bc:       chainManager, | ||||
| @ -170,7 +171,8 @@ func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, msgs state.M | ||||
| func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) { | ||||
| 	sm.lastAttemptedBlock = block | ||||
| 
 | ||||
| 	state := state.New(parent.Trie().Copy()) | ||||
| 	state := state.New(parent.Root(), sm.db) | ||||
| 	//state := state.New(parent.Trie().Copy())
 | ||||
| 
 | ||||
| 	// Block validation
 | ||||
| 	if err = sm.ValidateBlock(block, parent); err != nil { | ||||
| @ -214,52 +216,33 @@ func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate the new total difficulty and sync back to the db
 | ||||
| 	if td, ok := sm.CalculateTD(block); ok { | ||||
| 		// Sync the current block's state to the database and cancelling out the deferred Undo
 | ||||
| 		state.Sync() | ||||
| 	// Calculate the td for this block
 | ||||
| 	td = CalculateTD(block, parent) | ||||
| 	// Sync the current block's state to the database and cancelling out the deferred Undo
 | ||||
| 	state.Sync() | ||||
| 	// Set the block hashes for the current messages
 | ||||
| 	state.Manifest().SetHash(block.Hash()) | ||||
| 	messages = state.Manifest().Messages | ||||
| 	// Reset the manifest XXX We need this?
 | ||||
| 	state.Manifest().Reset() | ||||
| 	// Remove transactions from the pool
 | ||||
| 	sm.txpool.RemoveSet(block.Transactions()) | ||||
| 
 | ||||
| 		state.Manifest().SetHash(block.Hash()) | ||||
| 	chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4]) | ||||
| 
 | ||||
| 		messages := state.Manifest().Messages | ||||
| 		state.Manifest().Reset() | ||||
| 
 | ||||
| 		chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4]) | ||||
| 
 | ||||
| 		sm.txpool.RemoveSet(block.Transactions()) | ||||
| 
 | ||||
| 		return td, messages, nil | ||||
| 	} else { | ||||
| 		return nil, nil, errors.New("total diff failed") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (sm *BlockProcessor) CalculateTD(block *types.Block) (*big.Int, bool) { | ||||
| 	uncleDiff := new(big.Int) | ||||
| 	for _, uncle := range block.Uncles() { | ||||
| 		uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) | ||||
| 	} | ||||
| 
 | ||||
| 	// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
 | ||||
| 	td := new(big.Int) | ||||
| 	td = td.Add(sm.bc.Td(), uncleDiff) | ||||
| 	td = td.Add(td, block.Header().Difficulty) | ||||
| 
 | ||||
| 	// The new TD will only be accepted if the new difficulty is
 | ||||
| 	// is greater than the previous.
 | ||||
| 	if td.Cmp(sm.bc.Td()) > 0 { | ||||
| 		return td, true | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, false | ||||
| 	return td, messages, nil | ||||
| } | ||||
| 
 | ||||
| // Validates the current block. Returns an error if the block was invalid,
 | ||||
| // an uncle or anything that isn't on the current block chain.
 | ||||
| // Validation validates easy over difficult (dagger takes longer time = difficult)
 | ||||
| func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error { | ||||
| 	if len(block.Header().Extra) > 1024 { | ||||
| 		return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra)) | ||||
| 	} | ||||
| 
 | ||||
| 	expd := CalcDifficulty(block, parent) | ||||
| 	if expd.Cmp(block.Header().Difficulty) < 0 { | ||||
| 	if expd.Cmp(block.Header().Difficulty) != 0 { | ||||
| 		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd) | ||||
| 	} | ||||
| 
 | ||||
| @ -286,32 +269,38 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error { | ||||
| func (sm *BlockProcessor) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error { | ||||
| 	reward := new(big.Int).Set(BlockReward) | ||||
| 
 | ||||
| 	knownUncles := set.New() | ||||
| 	for _, uncle := range parent.Uncles() { | ||||
| 		knownUncles.Add(string(uncle.Hash())) | ||||
| 	ancestors := set.New() | ||||
| 	for _, ancestor := range sm.bc.GetAncestors(block, 7) { | ||||
| 		ancestors.Add(string(ancestor.Hash())) | ||||
| 	} | ||||
| 
 | ||||
| 	nonces := ethutil.NewSet(block.Header().Nonce) | ||||
| 	uncles := set.New() | ||||
| 	uncles.Add(string(block.Hash())) | ||||
| 	for _, uncle := range block.Uncles() { | ||||
| 		if nonces.Include(uncle.Nonce) { | ||||
| 		if uncles.Has(string(uncle.Hash())) { | ||||
| 			// Error not unique
 | ||||
| 			return UncleError("Uncle not unique") | ||||
| 		} | ||||
| 		uncles.Add(string(uncle.Hash())) | ||||
| 
 | ||||
| 		uncleParent := sm.bc.GetBlock(uncle.ParentHash) | ||||
| 		if uncleParent == nil { | ||||
| 		if !ancestors.Has(string(uncle.ParentHash)) { | ||||
| 			return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) | ||||
| 		} | ||||
| 
 | ||||
| 		if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 { | ||||
| 			return UncleError("Uncle too old") | ||||
| 		} | ||||
| 		/* | ||||
| 			uncleParent := sm.bc.GetBlock(uncle.ParentHash) | ||||
| 			if uncleParent == nil { | ||||
| 				return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) | ||||
| 			} | ||||
| 
 | ||||
| 		if knownUncles.Has(string(uncle.Hash())) { | ||||
| 			return UncleError("Uncle in chain") | ||||
| 		} | ||||
| 			if uncleParent.Number().Cmp(new(big.Int).Sub(parent.Number(), big.NewInt(6))) < 0 { | ||||
| 				return UncleError("Uncle too old") | ||||
| 			} | ||||
| 
 | ||||
| 		nonces.Insert(uncle.Nonce) | ||||
| 			if knownUncles.Has(string(uncle.Hash())) { | ||||
| 				return UncleError("Uncle in chain") | ||||
| 			} | ||||
| 		*/ | ||||
| 
 | ||||
| 		r := new(big.Int) | ||||
| 		r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) | ||||
| @ -347,7 +336,8 @@ func (sm *BlockProcessor) GetMessages(block *types.Block) (messages []*state.Mes | ||||
| 
 | ||||
| 	var ( | ||||
| 		parent = sm.bc.GetBlock(block.Header().ParentHash) | ||||
| 		state  = state.New(parent.Trie().Copy()) | ||||
| 		//state  = state.New(parent.Trie().Copy())
 | ||||
| 		state = state.New(parent.Root(), sm.db) | ||||
| 	) | ||||
| 
 | ||||
| 	defer state.Reset() | ||||
|  | ||||
| @ -23,17 +23,30 @@ type StateQuery interface { | ||||
| func CalcDifficulty(block, parent *types.Block) *big.Int { | ||||
| 	diff := new(big.Int) | ||||
| 
 | ||||
| 	bh, ph := block.Header(), parent.Header() | ||||
| 	adjust := new(big.Int).Rsh(ph.Difficulty, 10) | ||||
| 	if bh.Time >= ph.Time+5 { | ||||
| 		diff.Sub(ph.Difficulty, adjust) | ||||
| 	adjust := new(big.Int).Rsh(parent.Difficulty(), 10) | ||||
| 	if block.Time() >= parent.Time()+8 { | ||||
| 		diff.Sub(parent.Difficulty(), adjust) | ||||
| 	} else { | ||||
| 		diff.Add(ph.Difficulty, adjust) | ||||
| 		diff.Add(parent.Difficulty(), adjust) | ||||
| 	} | ||||
| 
 | ||||
| 	return diff | ||||
| } | ||||
| 
 | ||||
| func CalculateTD(block, parent *types.Block) *big.Int { | ||||
| 	uncleDiff := new(big.Int) | ||||
| 	for _, uncle := range block.Uncles() { | ||||
| 		uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) | ||||
| 	} | ||||
| 
 | ||||
| 	// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
 | ||||
| 	td := new(big.Int) | ||||
| 	td = td.Add(parent.Td, uncleDiff) | ||||
| 	td = td.Add(td, block.Header().Difficulty) | ||||
| 
 | ||||
| 	return td | ||||
| } | ||||
| 
 | ||||
| func CalcGasLimit(parent, block *types.Block) *big.Int { | ||||
| 	if block.Number().Cmp(big.NewInt(0)) == 0 { | ||||
| 		return ethutil.BigPow(10, 6) | ||||
| @ -55,6 +68,7 @@ func CalcGasLimit(parent, block *types.Block) *big.Int { | ||||
| 
 | ||||
| type ChainManager struct { | ||||
| 	//eth          EthManager
 | ||||
| 	db           ethutil.Database | ||||
| 	processor    types.BlockProcessor | ||||
| 	eventMux     *event.TypeMux | ||||
| 	genesisBlock *types.Block | ||||
| @ -96,13 +110,9 @@ func (self *ChainManager) CurrentBlock() *types.Block { | ||||
| 	return self.currentBlock | ||||
| } | ||||
| 
 | ||||
| func NewChainManager(mux *event.TypeMux) *ChainManager { | ||||
| 	bc := &ChainManager{} | ||||
| 	bc.genesisBlock = GenesisBlock() | ||||
| 	bc.eventMux = mux | ||||
| 
 | ||||
| func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager { | ||||
| 	bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux} | ||||
| 	bc.setLastBlock() | ||||
| 
 | ||||
| 	bc.transState = bc.State().Copy() | ||||
| 
 | ||||
| 	return bc | ||||
| @ -120,7 +130,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) State() *state.StateDB { | ||||
| 	return state.New(self.CurrentBlock().Trie()) | ||||
| 	return state.New(self.CurrentBlock().Root(), self.db) | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) TransState() *state.StateDB { | ||||
| @ -128,7 +138,7 @@ func (self *ChainManager) TransState() *state.StateDB { | ||||
| } | ||||
| 
 | ||||
| func (bc *ChainManager) setLastBlock() { | ||||
| 	data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) | ||||
| 	data, _ := bc.db.Get([]byte("LastBlock")) | ||||
| 	if len(data) != 0 { | ||||
| 		var block types.Block | ||||
| 		rlp.Decode(bytes.NewReader(data), &block) | ||||
| @ -137,12 +147,12 @@ func (bc *ChainManager) setLastBlock() { | ||||
| 		bc.lastBlockNumber = block.Header().Number.Uint64() | ||||
| 
 | ||||
| 		// Set the last know difficulty (might be 0x0 as initial value, Genesis)
 | ||||
| 		bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) | ||||
| 		bc.td = ethutil.BigD(bc.db.LastKnownTD()) | ||||
| 	} else { | ||||
| 		bc.Reset() | ||||
| 	} | ||||
| 
 | ||||
| 	chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash()) | ||||
| 	chainlogger.Infof("Last block (#%d) %x TD=%v\n", bc.lastBlockNumber, bc.currentBlock.Hash(), bc.td) | ||||
| } | ||||
| 
 | ||||
| // Block creation & chain handling
 | ||||
| @ -183,7 +193,7 @@ func (bc *ChainManager) Reset() { | ||||
| 	defer bc.mu.Unlock() | ||||
| 
 | ||||
| 	for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { | ||||
| 		ethutil.Config.Db.Delete(block.Hash()) | ||||
| 		bc.db.Delete(block.Hash()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Prepare the genesis block
 | ||||
| @ -210,7 +220,7 @@ func (self *ChainManager) Export() []byte { | ||||
| 
 | ||||
| func (bc *ChainManager) insert(block *types.Block) { | ||||
| 	encodedBlock := ethutil.Encode(block) | ||||
| 	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) | ||||
| 	bc.db.Put([]byte("LastBlock"), encodedBlock) | ||||
| 	bc.currentBlock = block | ||||
| 	bc.lastBlockHash = block.Hash() | ||||
| } | ||||
| @ -218,8 +228,8 @@ func (bc *ChainManager) insert(block *types.Block) { | ||||
| func (bc *ChainManager) write(block *types.Block) { | ||||
| 	bc.writeBlockInfo(block) | ||||
| 
 | ||||
| 	encodedBlock := ethutil.Encode(block) | ||||
| 	ethutil.Config.Db.Put(block.Hash(), encodedBlock) | ||||
| 	encodedBlock := ethutil.Encode(block.RlpDataForStorage()) | ||||
| 	bc.db.Put(block.Hash(), encodedBlock) | ||||
| } | ||||
| 
 | ||||
| // Accessors
 | ||||
| @ -229,7 +239,7 @@ func (bc *ChainManager) Genesis() *types.Block { | ||||
| 
 | ||||
| // Block fetching methods
 | ||||
| func (bc *ChainManager) HasBlock(hash []byte) bool { | ||||
| 	data, _ := ethutil.Config.Db.Get(hash) | ||||
| 	data, _ := bc.db.Get(hash) | ||||
| 	return len(data) != 0 | ||||
| } | ||||
| 
 | ||||
| @ -241,20 +251,18 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain | ||||
| 
 | ||||
| 	// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
 | ||||
| 	for i := uint64(0); i < max; i++ { | ||||
| 		block = self.GetBlock(block.Header().ParentHash) | ||||
| 		chain = append(chain, block.Hash()) | ||||
| 
 | ||||
| 		if block.Header().Number.Cmp(ethutil.Big0) <= 0 { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		block = self.GetBlock(block.Header().ParentHash) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) GetBlock(hash []byte) *types.Block { | ||||
| 	data, _ := ethutil.Config.Db.Get(hash) | ||||
| 	data, _ := self.db.Get(hash) | ||||
| 	if len(data) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| @ -267,6 +275,28 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block { | ||||
| 	return &block | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) { | ||||
| 	for i := 0; block != nil && i < length; i++ { | ||||
| 		uncles = append(uncles, block.Uncles()...) | ||||
| 		block = self.GetBlock(block.ParentHash()) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) { | ||||
| 	for i := 0; i < length; i++ { | ||||
| 		block = self.GetBlock(block.ParentHash()) | ||||
| 		if block == nil { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		blocks = append(blocks, block) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { | ||||
| 	self.mu.RLock() | ||||
| 	defer self.mu.RUnlock() | ||||
| @ -286,7 +316,7 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { | ||||
| } | ||||
| 
 | ||||
| func (bc *ChainManager) setTotalDifficulty(td *big.Int) { | ||||
| 	ethutil.Config.Db.Put([]byte("LTD"), td.Bytes()) | ||||
| 	bc.db.Put([]byte("LTD"), td.Bytes()) | ||||
| 	bc.td = td | ||||
| } | ||||
| 
 | ||||
| @ -343,12 +373,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { | ||||
| 			cblock := self.currentBlock | ||||
| 			if td.Cmp(self.td) > 0 { | ||||
| 				if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 { | ||||
| 					chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4]) | ||||
| 					chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td) | ||||
| 				} | ||||
| 
 | ||||
| 				self.setTotalDifficulty(td) | ||||
| 				self.insert(block) | ||||
| 				self.transState = state.New(cblock.Trie().Copy()) | ||||
| 				self.transState = state.New(cblock.Root(), self.db) //state.New(cblock.Trie().Copy())
 | ||||
| 			} | ||||
| 
 | ||||
| 		} | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"reflect" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"testing" | ||||
| @ -21,14 +21,6 @@ func init() { | ||||
| 	ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH") | ||||
| } | ||||
| 
 | ||||
| func reset() { | ||||
| 	db, err := ethdb.NewMemDatabase() | ||||
| 	if err != nil { | ||||
| 		panic("Could not create mem-db, failing") | ||||
| 	} | ||||
| 	ethutil.Config.Db = db | ||||
| } | ||||
| 
 | ||||
| func loadChain(fn string, t *testing.T) (types.Blocks, error) { | ||||
| 	fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm) | ||||
| 	if err != nil { | ||||
| @ -54,7 +46,7 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t * | ||||
| } | ||||
| 
 | ||||
| func TestChainInsertions(t *testing.T) { | ||||
| 	reset() | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 
 | ||||
| 	chain1, err := loadChain("valid1", t) | ||||
| 	if err != nil { | ||||
| @ -69,9 +61,9 @@ func TestChainInsertions(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	var eventMux event.TypeMux | ||||
| 	chainMan := NewChainManager(&eventMux) | ||||
| 	chainMan := NewChainManager(db, &eventMux) | ||||
| 	txPool := NewTxPool(&eventMux) | ||||
| 	blockMan := NewBlockManager(txPool, chainMan, &eventMux) | ||||
| 	blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) | ||||
| 	chainMan.SetProcessor(blockMan) | ||||
| 
 | ||||
| 	const max = 2 | ||||
| @ -84,17 +76,17 @@ func TestChainInsertions(t *testing.T) { | ||||
| 		<-done | ||||
| 	} | ||||
| 
 | ||||
| 	if reflect.DeepEqual(chain2[len(chain2)-1], chainMan.CurrentBlock()) { | ||||
| 	if bytes.Equal(chain2[len(chain2)-1].Hash(), chainMan.CurrentBlock().Hash()) { | ||||
| 		t.Error("chain2 is canonical and shouldn't be") | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(chain1[len(chain1)-1], chainMan.CurrentBlock()) { | ||||
| 	if !bytes.Equal(chain1[len(chain1)-1].Hash(), chainMan.CurrentBlock().Hash()) { | ||||
| 		t.Error("chain1 isn't canonical and should be") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestChainMultipleInsertions(t *testing.T) { | ||||
| 	reset() | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 
 | ||||
| 	const max = 4 | ||||
| 	chains := make([]types.Blocks, max) | ||||
| @ -113,9 +105,9 @@ func TestChainMultipleInsertions(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| 	var eventMux event.TypeMux | ||||
| 	chainMan := NewChainManager(&eventMux) | ||||
| 	chainMan := NewChainManager(db, &eventMux) | ||||
| 	txPool := NewTxPool(&eventMux) | ||||
| 	blockMan := NewBlockManager(txPool, chainMan, &eventMux) | ||||
| 	blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux) | ||||
| 	chainMan.SetProcessor(blockMan) | ||||
| 	done := make(chan bool, max) | ||||
| 	for i, chain := range chains { | ||||
| @ -132,7 +124,25 @@ func TestChainMultipleInsertions(t *testing.T) { | ||||
| 		<-done | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(chains[longest][len(chains[longest])-1], chainMan.CurrentBlock()) { | ||||
| 	if !bytes.Equal(chains[longest][len(chains[longest])-1].Hash(), chainMan.CurrentBlock().Hash()) { | ||||
| 		t.Error("Invalid canonical chain") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestGetAncestors(t *testing.T) { | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	var eventMux event.TypeMux | ||||
| 	chainMan := NewChainManager(db, &eventMux) | ||||
| 	chain, err := loadChain("valid1", t) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| 
 | ||||
| 	for _, block := range chain { | ||||
| 		chainMan.write(block) | ||||
| 	} | ||||
| 
 | ||||
| 	ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4) | ||||
| 	fmt.Println(ancestors) | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,7 @@ import ( | ||||
| 	"math/big" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 	"github.com/ethereum/go-ethereum/vm" | ||||
| ) | ||||
| @ -13,7 +14,6 @@ type Execution struct { | ||||
| 	env               vm.Environment | ||||
| 	address, input    []byte | ||||
| 	Gas, price, value *big.Int | ||||
| 	SkipTransfer      bool | ||||
| } | ||||
| 
 | ||||
| func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution { | ||||
| @ -33,7 +33,7 @@ func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, erro | ||||
| 
 | ||||
| func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) { | ||||
| 	env := self.env | ||||
| 	evm := vm.New(env, vm.DebugVmTy) | ||||
| 	evm := vm.New(env) | ||||
| 
 | ||||
| 	if env.Depth() == vm.MaxCallDepth { | ||||
| 		caller.ReturnGas(self.Gas, self.price) | ||||
| @ -41,16 +41,22 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret | ||||
| 		return nil, vm.DepthError{} | ||||
| 	} | ||||
| 
 | ||||
| 	from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address) | ||||
| 	// Skipping transfer is used on testing for the initial call
 | ||||
| 	if !self.SkipTransfer { | ||||
| 		err = env.Transfer(from, to, self.value) | ||||
| 		if err != nil { | ||||
| 			caller.ReturnGas(self.Gas, self.price) | ||||
| 	vsnapshot := env.State().Copy() | ||||
| 	if len(self.address) == 0 { | ||||
| 		// Generate a new address
 | ||||
| 		nonce := env.State().GetNonce(caller.Address()) | ||||
| 		self.address = crypto.CreateAddress(caller.Address(), nonce) | ||||
| 		env.State().SetNonce(caller.Address(), nonce+1) | ||||
| 	} | ||||
| 
 | ||||
| 			err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance) | ||||
| 			return | ||||
| 		} | ||||
| 	from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address) | ||||
| 	err = env.Transfer(from, to, self.value) | ||||
| 	if err != nil { | ||||
| 		env.State().Set(vsnapshot) | ||||
| 
 | ||||
| 		caller.ReturnGas(self.Gas, self.price) | ||||
| 
 | ||||
| 		return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance()) | ||||
| 	} | ||||
| 
 | ||||
| 	snapshot := env.State().Copy() | ||||
|  | ||||
| @ -19,18 +19,20 @@ var ZeroHash512 = make([]byte, 64) | ||||
| var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{})) | ||||
| var EmptyListRoot = crypto.Sha3(ethutil.Encode("")) | ||||
| 
 | ||||
| func GenesisBlock() *types.Block { | ||||
| func GenesisBlock(db ethutil.Database) *types.Block { | ||||
| 	genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "") | ||||
| 	genesis.Header().Number = ethutil.Big0 | ||||
| 	genesis.Header().GasLimit = big.NewInt(1000000) | ||||
| 	genesis.Header().GasUsed = ethutil.Big0 | ||||
| 	genesis.Header().Time = 0 | ||||
| 	genesis.Td = ethutil.Big0 | ||||
| 
 | ||||
| 	genesis.SetUncles([]*types.Header{}) | ||||
| 	genesis.SetTransactions(types.Transactions{}) | ||||
| 	genesis.SetReceipts(types.Receipts{}) | ||||
| 
 | ||||
| 	statedb := state.New(genesis.Trie()) | ||||
| 	statedb := state.New(genesis.Root(), db) | ||||
| 	//statedb := state.New(genesis.Trie())
 | ||||
| 	for _, addr := range []string{ | ||||
| 		"51ba59315b3a95761d0863b05ccc7a7f54703d99", | ||||
| 		"e4157b34ea9615cfbde6b4fda419828124b70c78", | ||||
|  | ||||
| @ -77,7 +77,6 @@ func NewTestManager() *TestManager { | ||||
| 		fmt.Println("Could not create mem-db, failing") | ||||
| 		return nil | ||||
| 	} | ||||
| 	ethutil.Config.Db = db | ||||
| 
 | ||||
| 	testManager := &TestManager{} | ||||
| 	testManager.eventMux = new(event.TypeMux) | ||||
|  | ||||
| @ -192,8 +192,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) { | ||||
| 		if err == nil { | ||||
| 			dataGas := big.NewInt(int64(len(ret))) | ||||
| 			dataGas.Mul(dataGas, vm.GasCreateByte) | ||||
| 			if err = self.UseGas(dataGas); err == nil { | ||||
| 				//self.state.SetCode(ref.Address(), ret)
 | ||||
| 			if err := self.UseGas(dataGas); err == nil { | ||||
| 				ref.SetCode(ret) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -56,11 +56,6 @@ func NewTxPool(eventMux *event.TypeMux) *TxPool { | ||||
| } | ||||
| 
 | ||||
| func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { | ||||
| 	hash := tx.Hash() | ||||
| 	if pool.txs[string(hash)] != nil { | ||||
| 		return fmt.Errorf("Known transaction (%x)", hash[0:4]) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tx.To()) != 0 && len(tx.To()) != 20 { | ||||
| 		return fmt.Errorf("Invalid recipient. len = %d", len(tx.To())) | ||||
| 	} | ||||
| @ -97,6 +92,10 @@ func (self *TxPool) addTx(tx *types.Transaction) { | ||||
| } | ||||
| 
 | ||||
| func (self *TxPool) Add(tx *types.Transaction) error { | ||||
| 	if self.txs[string(tx.Hash())] != nil { | ||||
| 		return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4]) | ||||
| 	} | ||||
| 
 | ||||
| 	err := self.ValidateTransaction(tx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @ -149,6 +148,7 @@ func (pool *TxPool) RemoveInvalid(query StateQuery) { | ||||
| 	for _, tx := range pool.txs { | ||||
| 		sender := query.GetAccount(tx.From()) | ||||
| 		err := pool.ValidateTransaction(tx) | ||||
| 		fmt.Println(err, sender.Nonce, tx.Nonce()) | ||||
| 		if err != nil || sender.Nonce >= tx.Nonce() { | ||||
| 			removedTxs = append(removedTxs, tx) | ||||
| 		} | ||||
|  | ||||
| @ -6,16 +6,22 @@ import ( | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| ) | ||||
| 
 | ||||
| // State query interface
 | ||||
| type stateQuery struct{} | ||||
| type stateQuery struct{ db ethutil.Database } | ||||
| 
 | ||||
| func SQ() stateQuery { | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	return stateQuery{db: db} | ||||
| } | ||||
| 
 | ||||
| func (self stateQuery) GetAccount(addr []byte) *state.StateObject { | ||||
| 	return state.NewStateObject(addr) | ||||
| 	return state.NewStateObject(addr, self.db) | ||||
| } | ||||
| 
 | ||||
| func transaction() *types.Transaction { | ||||
| @ -55,7 +61,7 @@ func TestAddInvalidTx(t *testing.T) { | ||||
| func TestRemoveSet(t *testing.T) { | ||||
| 	pool, _ := setup() | ||||
| 	tx1 := transaction() | ||||
| 	pool.pool.Add(tx1) | ||||
| 	pool.addTx(tx1) | ||||
| 	pool.RemoveSet(types.Transactions{tx1}) | ||||
| 	if pool.Size() > 0 { | ||||
| 		t.Error("expected pool size to be 0") | ||||
| @ -65,16 +71,16 @@ func TestRemoveSet(t *testing.T) { | ||||
| func TestRemoveInvalid(t *testing.T) { | ||||
| 	pool, key := setup() | ||||
| 	tx1 := transaction() | ||||
| 	pool.pool.Add(tx1) | ||||
| 	pool.RemoveInvalid(stateQuery{}) | ||||
| 	pool.addTx(tx1) | ||||
| 	pool.RemoveInvalid(SQ()) | ||||
| 	if pool.Size() > 0 { | ||||
| 		t.Error("expected pool size to be 0") | ||||
| 	} | ||||
| 
 | ||||
| 	tx1.SetNonce(1) | ||||
| 	tx1.SignECDSA(key) | ||||
| 	pool.pool.Add(tx1) | ||||
| 	pool.RemoveInvalid(stateQuery{}) | ||||
| 	pool.addTx(tx1) | ||||
| 	pool.RemoveInvalid(SQ()) | ||||
| 	if pool.Size() != 1 { | ||||
| 		t.Error("expected pool size to be 1, is", pool.Size()) | ||||
| 	} | ||||
|  | ||||
| @ -9,9 +9,7 @@ import ( | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/ptrie" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| ) | ||||
| 
 | ||||
| type Header struct { | ||||
| @ -168,16 +166,18 @@ func (self *Block) RlpDataForStorage() interface{} { | ||||
| } | ||||
| 
 | ||||
| // Header accessors (add as you need them)
 | ||||
| func (self *Block) Number() *big.Int          { return self.header.Number } | ||||
| func (self *Block) NumberU64() uint64         { return self.header.Number.Uint64() } | ||||
| func (self *Block) Bloom() []byte             { return self.header.Bloom } | ||||
| func (self *Block) Coinbase() []byte          { return self.header.Coinbase } | ||||
| func (self *Block) Time() int64               { return int64(self.header.Time) } | ||||
| func (self *Block) GasLimit() *big.Int        { return self.header.GasLimit } | ||||
| func (self *Block) GasUsed() *big.Int         { return self.header.GasUsed } | ||||
| func (self *Block) Trie() *ptrie.Trie         { return ptrie.New(self.header.Root, ethutil.Config.Db) } | ||||
| func (self *Block) Number() *big.Int   { return self.header.Number } | ||||
| func (self *Block) NumberU64() uint64  { return self.header.Number.Uint64() } | ||||
| func (self *Block) Bloom() []byte      { return self.header.Bloom } | ||||
| func (self *Block) Coinbase() []byte   { return self.header.Coinbase } | ||||
| func (self *Block) Time() int64        { return int64(self.header.Time) } | ||||
| func (self *Block) GasLimit() *big.Int { return self.header.GasLimit } | ||||
| func (self *Block) GasUsed() *big.Int  { return self.header.GasUsed } | ||||
| 
 | ||||
| //func (self *Block) Trie() *ptrie.Trie         { return ptrie.New(self.header.Root, ethutil.Config.Db) }
 | ||||
| //func (self *Block) State() *state.StateDB     { return state.New(self.Trie()) }
 | ||||
| func (self *Block) Root() []byte              { return self.header.Root } | ||||
| func (self *Block) SetRoot(root []byte)       { self.header.Root = root } | ||||
| func (self *Block) State() *state.StateDB     { return state.New(self.Trie()) } | ||||
| func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) } | ||||
| 
 | ||||
| // Implement pow.Block
 | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| package types | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/ptrie" | ||||
| 	"github.com/ethereum/go-ethereum/trie" | ||||
| ) | ||||
| 
 | ||||
| type DerivableList interface { | ||||
| @ -11,7 +12,8 @@ type DerivableList interface { | ||||
| } | ||||
| 
 | ||||
| func DeriveSha(list DerivableList) []byte { | ||||
| 	trie := ptrie.New(nil, ethutil.Config.Db) | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	trie := trie.New(nil, db) | ||||
| 	for i := 0; i < list.Len(); i++ { | ||||
| 		trie.Update(ethutil.Encode(i), list.GetRlp(i)) | ||||
| 	} | ||||
|  | ||||
| @ -3,7 +3,12 @@ package crypto | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/obscuren/secp256k1-go" | ||||
| ) | ||||
| 
 | ||||
| // These tests are sanity checks.
 | ||||
| @ -34,3 +39,24 @@ func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte | ||||
| 		t.Errorf("hash %s returned wrong result.\ngot:  %x\nwant: %x", name, sum, exp) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func BenchmarkSha3(b *testing.B) { | ||||
| 	a := []byte("hello world") | ||||
| 	amount := 1000000 | ||||
| 	start := time.Now() | ||||
| 	for i := 0; i < amount; i++ { | ||||
| 		Sha3(a) | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Println(amount, ":", time.Since(start)) | ||||
| } | ||||
| 
 | ||||
| func Test0Key(t *testing.T) { | ||||
| 
 | ||||
| 	key := ethutil.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111") | ||||
| 
 | ||||
| 	p, err := secp256k1.GeneratePubKey(key) | ||||
| 	addr := Sha3(p[1:])[12:] | ||||
| 	fmt.Printf("%x\n", p) | ||||
| 	fmt.Printf("%v %x\n", err, addr) | ||||
| } | ||||
|  | ||||
							
								
								
									
										107
									
								
								crypto/key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								crypto/key.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU Lesser General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU Lesser General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Gustav Simonsson <gustav.simonsson@gmail.com> | ||||
|  * @date 2015 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package crypto | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"code.google.com/p/go-uuid/uuid" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| type Key struct { | ||||
| 	Id *uuid.UUID // Version 4 "random" for unique id not derived from key data
 | ||||
| 	// we only store privkey as pubkey/address can be derived from it
 | ||||
| 	// privkey in this struct is always in plaintext
 | ||||
| 	PrivateKey *ecdsa.PrivateKey | ||||
| } | ||||
| 
 | ||||
| type plainKeyJSON struct { | ||||
| 	Id         []byte | ||||
| 	PrivateKey []byte | ||||
| } | ||||
| 
 | ||||
| type cipherJSON struct { | ||||
| 	Salt       []byte | ||||
| 	IV         []byte | ||||
| 	CipherText []byte | ||||
| } | ||||
| 
 | ||||
| type encryptedKeyJSON struct { | ||||
| 	Id     []byte | ||||
| 	Crypto cipherJSON | ||||
| } | ||||
| 
 | ||||
| func (k *Key) Address() []byte { | ||||
| 	pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey) | ||||
| 	return Sha3(pubBytes)[12:] | ||||
| } | ||||
| 
 | ||||
| func (k *Key) MarshalJSON() (j []byte, err error) { | ||||
| 	jStruct := plainKeyJSON{ | ||||
| 		*k.Id, | ||||
| 		FromECDSA(k.PrivateKey), | ||||
| 	} | ||||
| 	j, err = json.Marshal(jStruct) | ||||
| 	return j, err | ||||
| } | ||||
| 
 | ||||
| func (k *Key) UnmarshalJSON(j []byte) (err error) { | ||||
| 	keyJSON := new(plainKeyJSON) | ||||
| 	err = json.Unmarshal(j, &keyJSON) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	u := new(uuid.UUID) | ||||
| 	*u = keyJSON.Id | ||||
| 	k.Id = u | ||||
| 
 | ||||
| 	k.PrivateKey = ToECDSA(keyJSON.PrivateKey) | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func NewKey(rand io.Reader) *Key { | ||||
| 	randBytes := make([]byte, 32) | ||||
| 	_, err := rand.Read(randBytes) | ||||
| 	if err != nil { | ||||
| 		panic("key generation: could not read from random source: " + err.Error()) | ||||
| 	} | ||||
| 	reader := bytes.NewReader(randBytes) | ||||
| 	_, x, y, err := elliptic.GenerateKey(S256(), reader) | ||||
| 	if err != nil { | ||||
| 		panic("key generation: elliptic.GenerateKey failed: " + err.Error()) | ||||
| 	} | ||||
| 	privateKeyMarshalled := elliptic.Marshal(S256(), x, y) | ||||
| 	privateKeyECDSA := ToECDSA(privateKeyMarshalled) | ||||
| 
 | ||||
| 	key := new(Key) | ||||
| 	id := uuid.NewRandom() | ||||
| 	key.Id = &id | ||||
| 	key.PrivateKey = privateKeyECDSA | ||||
| 	return key | ||||
| } | ||||
							
								
								
									
										245
									
								
								crypto/key_store_passphrase.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								crypto/key_store_passphrase.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,245 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU Lesser General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU Lesser General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Gustav Simonsson <gustav.simonsson@gmail.com> | ||||
|  * @date 2015 | ||||
|  * | ||||
|  */ | ||||
| /* | ||||
| 
 | ||||
| This key store behaves as KeyStorePlain with the difference that | ||||
| the private key is encrypted and on disk uses another JSON encoding. | ||||
| 
 | ||||
| Cryptography: | ||||
| 
 | ||||
| 1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters | ||||
|    (work factors) [1][2] are defined as constants below. | ||||
| 2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. | ||||
| 3. Checksum is SHA3 of the private key bytes. | ||||
| 4. Plaintext is concatenation of private key bytes and checksum. | ||||
| 5. Encryption algo is AES 256 CBC [3][4] | ||||
| 6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. | ||||
| 7. Plaintext padding is PKCS #7 [5][6] | ||||
| 
 | ||||
| Encoding: | ||||
| 
 | ||||
| 1. On disk, ciphertext, salt and IV are encoded in a nested JSON object. | ||||
|    cat a key file to see the structure. | ||||
| 2. byte arrays are base64 JSON strings. | ||||
| 3. The EC private key bytes are in uncompressed form [7]. | ||||
|    They are a big-endian byte slice of the absolute value of D [8][9]. | ||||
| 4. The checksum is the last 32 bytes of the plaintext byte array and the | ||||
|    private key is the preceeding bytes. | ||||
| 
 | ||||
| References: | ||||
| 
 | ||||
| 1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf
 | ||||
| 2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors
 | ||||
| 3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
 | ||||
| 4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
 | ||||
| 5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
 | ||||
| 6. http://tools.ietf.org/html/rfc2315
 | ||||
| 7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key
 | ||||
| 8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey
 | ||||
| 9. https://golang.org/pkg/math/big/#Int.Bytes
 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| package crypto | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"code.google.com/p/go-uuid/uuid" | ||||
| 	"code.google.com/p/go.crypto/scrypt" | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	crand "crypto/rand" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
 | ||||
| 	scryptN     = 1 << 18 | ||||
| 	scryptr     = 8 | ||||
| 	scryptp     = 1 | ||||
| 	scryptdkLen = 32 | ||||
| ) | ||||
| 
 | ||||
| type keyStorePassphrase struct { | ||||
| 	keysDirPath string | ||||
| } | ||||
| 
 | ||||
| func NewKeyStorePassphrase(path string) KeyStore2 { | ||||
| 	return &keyStorePassphrase{path} | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { | ||||
| 	return GenerateNewKeyDefault(ks, rand, auth) | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { | ||||
| 	keyBytes, err := DecryptKey(ks, keyId, auth) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	key = &Key{ | ||||
| 		Id:         keyId, | ||||
| 		PrivateKey: ToECDSA(keyBytes), | ||||
| 	} | ||||
| 	return key, err | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { | ||||
| 	authArray := []byte(auth) | ||||
| 	salt := getEntropyCSPRNG(32) | ||||
| 	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	keyBytes := FromECDSA(key.PrivateKey) | ||||
| 	keyBytesHash := Sha3(keyBytes) | ||||
| 	toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...)) | ||||
| 
 | ||||
| 	AES256Block, err := aes.NewCipher(derivedKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	iv := getEntropyCSPRNG(aes.BlockSize) // 16
 | ||||
| 	AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) | ||||
| 	cipherText := make([]byte, len(toEncrypt)) | ||||
| 	AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) | ||||
| 
 | ||||
| 	cipherStruct := cipherJSON{ | ||||
| 		salt, | ||||
| 		iv, | ||||
| 		cipherText, | ||||
| 	} | ||||
| 	keyStruct := encryptedKeyJSON{ | ||||
| 		*key.Id, | ||||
| 		cipherStruct, | ||||
| 	} | ||||
| 	keyJSON, err := json.Marshal(keyStruct) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) { | ||||
| 	// only delete if correct passphrase is given
 | ||||
| 	_, err = DecryptKey(ks, keyId, auth) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	keyDirPath := path.Join(ks.keysDirPath, keyId.String()) | ||||
| 	return os.RemoveAll(keyDirPath) | ||||
| } | ||||
| 
 | ||||
| func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) { | ||||
| 	fileContent, err := GetKeyFile(ks.keysDirPath, keyId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	keyProtected := new(encryptedKeyJSON) | ||||
| 	err = json.Unmarshal(fileContent, keyProtected) | ||||
| 
 | ||||
| 	salt := keyProtected.Crypto.Salt | ||||
| 
 | ||||
| 	iv := keyProtected.Crypto.IV | ||||
| 
 | ||||
| 	cipherText := keyProtected.Crypto.CipherText | ||||
| 
 | ||||
| 	authArray := []byte(auth) | ||||
| 	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	AES256Block, err := aes.NewCipher(derivedKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv) | ||||
| 	paddedPlainText := make([]byte, len(cipherText)) | ||||
| 	AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText) | ||||
| 
 | ||||
| 	plainText := PKCS7Unpad(paddedPlainText) | ||||
| 	if plainText == nil { | ||||
| 		err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	keyBytes = plainText[:len(plainText)-32] | ||||
| 	keyBytesHash := plainText[len(plainText)-32:] | ||||
| 	if !bytes.Equal(Sha3(keyBytes), keyBytesHash) { | ||||
| 		err = errors.New("Decryption failed: checksum mismatch") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return keyBytes, err | ||||
| } | ||||
| 
 | ||||
| func getEntropyCSPRNG(n int) []byte { | ||||
| 	mainBuff := make([]byte, n) | ||||
| 	_, err := io.ReadFull(crand.Reader, mainBuff) | ||||
| 	if err != nil { | ||||
| 		panic("key generation: reading from crypto/rand failed: " + err.Error()) | ||||
| 	} | ||||
| 	return mainBuff | ||||
| } | ||||
| 
 | ||||
| // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
 | ||||
| func PKCS7Pad(in []byte) []byte { | ||||
| 	padding := 16 - (len(in) % 16) | ||||
| 	if padding == 0 { | ||||
| 		padding = 16 | ||||
| 	} | ||||
| 	for i := 0; i < padding; i++ { | ||||
| 		in = append(in, byte(padding)) | ||||
| 	} | ||||
| 	return in | ||||
| } | ||||
| 
 | ||||
| func PKCS7Unpad(in []byte) []byte { | ||||
| 	if len(in) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	padding := in[len(in)-1] | ||||
| 	if int(padding) > len(in) || padding > aes.BlockSize { | ||||
| 		return nil | ||||
| 	} else if padding == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { | ||||
| 		if in[i] != padding { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return in[:len(in)-int(padding)] | ||||
| } | ||||
							
								
								
									
										114
									
								
								crypto/key_store_plain.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								crypto/key_store_plain.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| /* | ||||
| 	This file is part of go-ethereum | ||||
| 
 | ||||
| 	go-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU Lesser General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	go-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU Lesser General Public License | ||||
| 	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /** | ||||
|  * @authors | ||||
|  * 	Gustav Simonsson <gustav.simonsson@gmail.com> | ||||
|  * @date 2015 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package crypto | ||||
| 
 | ||||
| import ( | ||||
| 	"code.google.com/p/go-uuid/uuid" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"os/user" | ||||
| 	"path" | ||||
| ) | ||||
| 
 | ||||
| // TODO: rename to KeyStore when replacing existing KeyStore
 | ||||
| type KeyStore2 interface { | ||||
| 	// create new key using io.Reader entropy source and optionally using auth string
 | ||||
| 	GenerateNewKey(io.Reader, string) (*Key, error) | ||||
| 	GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string
 | ||||
| 	StoreKey(*Key, string) error             // store key optionally using auth string
 | ||||
| 	DeleteKey(*uuid.UUID, string) error      // delete key by id and auth string
 | ||||
| } | ||||
| 
 | ||||
| type keyStorePlain struct { | ||||
| 	keysDirPath string | ||||
| } | ||||
| 
 | ||||
| // TODO: copied from cmd/ethereum/flags.go
 | ||||
| func DefaultDataDir() string { | ||||
| 	usr, _ := user.Current() | ||||
| 	return path.Join(usr.HomeDir, ".ethereum") | ||||
| } | ||||
| 
 | ||||
| func NewKeyStorePlain(path string) KeyStore2 { | ||||
| 	return &keyStorePlain{path} | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { | ||||
| 	return GenerateNewKeyDefault(ks, rand, auth) | ||||
| } | ||||
| 
 | ||||
| func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key, err error) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			err = fmt.Errorf("GenerateNewKey error: %v", r) | ||||
| 		} | ||||
| 	}() | ||||
| 	key = NewKey(rand) | ||||
| 	err = ks.StoreKey(key, auth) | ||||
| 	return key, err | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) { | ||||
| 	fileContent, err := GetKeyFile(ks.keysDirPath, keyId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	key = new(Key) | ||||
| 	err = json.Unmarshal(fileContent, key) | ||||
| 	return key, err | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) { | ||||
| 	keyJSON, err := json.Marshal(key) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) { | ||||
| 	keyDirPath := path.Join(ks.keysDirPath, keyId.String()) | ||||
| 	err = os.RemoveAll(keyDirPath) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) { | ||||
| 	id := keyId.String() | ||||
| 	return ioutil.ReadFile(path.Join(keysDirPath, id, id)) | ||||
| } | ||||
| 
 | ||||
| func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) { | ||||
| 	keyDirPath := path.Join(keysDirPath, id) | ||||
| 	keyFilePath := path.Join(keyDirPath, id) | ||||
| 	err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
 | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
 | ||||
| } | ||||
							
								
								
									
										85
									
								
								crypto/key_store_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								crypto/key_store_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| package crypto | ||||
| 
 | ||||
| import ( | ||||
| 	crand "crypto/rand" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestKeyStorePlain(t *testing.T) { | ||||
| 	ks := NewKeyStorePlain(DefaultDataDir()) | ||||
| 	pass := "" // not used but required by API
 | ||||
| 	k1, err := ks.GenerateNewKey(crand.Reader, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	k2 := new(Key) | ||||
| 	k2, err = ks.GetKey(k1.Id, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(k1.Id, k2.Id) { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = ks.DeleteKey(k2.Id, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestKeyStorePassphrase(t *testing.T) { | ||||
| 	ks := NewKeyStorePassphrase(DefaultDataDir()) | ||||
| 	pass := "foo" | ||||
| 	k1, err := ks.GenerateNewKey(crand.Reader, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	k2 := new(Key) | ||||
| 	k2, err = ks.GetKey(k1.Id, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(k1.Id, k2.Id) { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = ks.DeleteKey(k2.Id, pass) // also to clean up created files
 | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestKeyStorePassphraseDecryptionFail(t *testing.T) { | ||||
| 	ks := NewKeyStorePassphrase(DefaultDataDir()) | ||||
| 	pass := "foo" | ||||
| 	k1, err := ks.GenerateNewKey(crand.Reader, pass) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase
 | ||||
| 	if err == nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase
 | ||||
| 	if err == nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = ks.DeleteKey(k1.Id, pass) // to clean up
 | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| @ -18,7 +18,7 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	seedNodeAddress = "poc-7.ethdev.com:30300" | ||||
| 	seedNodeAddress = "poc-8.ethdev.com:30303" | ||||
| ) | ||||
| 
 | ||||
| type Config struct { | ||||
| @ -81,7 +81,7 @@ type Ethereum struct { | ||||
| func New(config *Config) (*Ethereum, error) { | ||||
| 	// Boostrap database
 | ||||
| 	logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel) | ||||
| 	db, err := ethdb.NewLDBDatabase("database") | ||||
| 	db, err := ethdb.NewLDBDatabase("blockchain") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -110,7 +110,7 @@ func New(config *Config) (*Ethereum, error) { | ||||
| 	clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey()) | ||||
| 
 | ||||
| 	saveProtocolVersion(db) | ||||
| 	ethutil.Config.Db = db | ||||
| 	//ethutil.Config.Db = db
 | ||||
| 
 | ||||
| 	eth := &Ethereum{ | ||||
| 		shutdownChan:   make(chan bool), | ||||
| @ -123,9 +123,9 @@ func New(config *Config) (*Ethereum, error) { | ||||
| 		logger:         logger, | ||||
| 	} | ||||
| 
 | ||||
| 	eth.chainManager = core.NewChainManager(eth.EventMux()) | ||||
| 	eth.chainManager = core.NewChainManager(db, eth.EventMux()) | ||||
| 	eth.txPool = core.NewTxPool(eth.EventMux()) | ||||
| 	eth.blockProcessor = core.NewBlockProcessor(eth.txPool, eth.chainManager, eth.EventMux()) | ||||
| 	eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux()) | ||||
| 	eth.chainManager.SetProcessor(eth.blockProcessor) | ||||
| 	eth.whisper = whisper.New() | ||||
| 
 | ||||
| @ -134,24 +134,20 @@ func New(config *Config) (*Ethereum, error) { | ||||
| 	eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify) | ||||
| 
 | ||||
| 	ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool) | ||||
| 	protocols := []p2p.Protocol{ethProto} | ||||
| 
 | ||||
| 	if config.Shh { | ||||
| 		eth.whisper = whisper.New() | ||||
| 		protocols = append(protocols, eth.whisper.Protocol()) | ||||
| 	} | ||||
| 	protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()} | ||||
| 
 | ||||
| 	nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	fmt.Println(nat) | ||||
| 
 | ||||
| 	eth.net = &p2p.Server{ | ||||
| 		Identity:  clientId, | ||||
| 		MaxPeers:  config.MaxPeers, | ||||
| 		Protocols: protocols, | ||||
| 		Blacklist: eth.blacklist, | ||||
| 		NAT:       nat, | ||||
| 		NAT:       p2p.UPNP(), | ||||
| 		NoDial:    !config.Dial, | ||||
| 	} | ||||
| 
 | ||||
| @ -249,7 +245,7 @@ func (s *Ethereum) Start(seed bool) error { | ||||
| 	if seed { | ||||
| 		logger.Infof("Connect to seed node %v", seedNodeAddress) | ||||
| 		if err := s.SuggestPeer(seedNodeAddress); err != nil { | ||||
| 			return err | ||||
| 			logger.Infoln(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package eth | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"math/big" | ||||
| @ -24,8 +25,8 @@ const ( | ||||
| 	blocksRequestRepetition    = 1 | ||||
| 	blockHashesRequestInterval = 500 // ms
 | ||||
| 	blocksRequestMaxIdleRounds = 100 | ||||
| 	cacheTimeout               = 3 // minutes
 | ||||
| 	blockTimeout               = 5 // minutes
 | ||||
| 	blockHashesTimeout         = 60  // seconds
 | ||||
| 	blocksTimeout              = 120 // seconds
 | ||||
| ) | ||||
| 
 | ||||
| type poolNode struct { | ||||
| @ -70,9 +71,14 @@ type BlockPool struct { | ||||
| type peerInfo struct { | ||||
| 	lock sync.RWMutex | ||||
| 
 | ||||
| 	td           *big.Int | ||||
| 	currentBlock []byte | ||||
| 	id           string | ||||
| 	td               *big.Int | ||||
| 	currentBlockHash []byte | ||||
| 	currentBlock     *types.Block | ||||
| 	currentBlockC    chan *types.Block | ||||
| 	parentHash       []byte | ||||
| 	headSection      *section | ||||
| 	headSectionC     chan *section | ||||
| 	id               string | ||||
| 
 | ||||
| 	requestBlockHashes func([]byte) error | ||||
| 	requestBlocks      func([][]byte) error | ||||
| @ -203,30 +209,39 @@ func (self *BlockPool) Wait(t time.Duration) { | ||||
| // AddPeer is called by the eth protocol instance running on the peer after
 | ||||
| // the status message has been received with total difficulty and current block hash
 | ||||
| // AddPeer can only be used once, RemovePeer needs to be called when the peer disconnects
 | ||||
| func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) bool { | ||||
| func (self *BlockPool) AddPeer(td *big.Int, currentBlockHash []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool) { | ||||
| 
 | ||||
| 	self.peersLock.Lock() | ||||
| 	defer self.peersLock.Unlock() | ||||
| 	peer, ok := self.peers[peerId] | ||||
| 	if ok { | ||||
| 		poolLogger.Debugf("Update peer %v with td %v and current block %x", peerId, td, currentBlock[:4]) | ||||
| 		peer.td = td | ||||
| 		peer.currentBlock = currentBlock | ||||
| 		if bytes.Compare(peer.currentBlockHash, currentBlockHash) != 0 { | ||||
| 			poolLogger.Debugf("Update peer %v with td %v and current block %s", peerId, td, name(currentBlockHash)) | ||||
| 			peer.lock.Lock() | ||||
| 			peer.td = td | ||||
| 			peer.currentBlockHash = currentBlockHash | ||||
| 			peer.currentBlock = nil | ||||
| 			peer.parentHash = nil | ||||
| 			peer.headSection = nil | ||||
| 			peer.lock.Unlock() | ||||
| 		} | ||||
| 	} else { | ||||
| 		peer = &peerInfo{ | ||||
| 			td:                 td, | ||||
| 			currentBlock:       currentBlock, | ||||
| 			currentBlockHash:   currentBlockHash, | ||||
| 			id:                 peerId, //peer.Identity().Pubkey()
 | ||||
| 			requestBlockHashes: requestBlockHashes, | ||||
| 			requestBlocks:      requestBlocks, | ||||
| 			peerError:          peerError, | ||||
| 			sections:           make(map[string]*section), | ||||
| 			currentBlockC:      make(chan *types.Block), | ||||
| 			headSectionC:       make(chan *section), | ||||
| 		} | ||||
| 		self.peers[peerId] = peer | ||||
| 		poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlock[:4]) | ||||
| 		poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlockHash[:4]) | ||||
| 	} | ||||
| 	// check peer current head
 | ||||
| 	if self.hasBlock(currentBlock) { | ||||
| 	if self.hasBlock(currentBlockHash) { | ||||
| 		// peer not ahead
 | ||||
| 		return false | ||||
| 	} | ||||
| @ -234,22 +249,135 @@ func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, | ||||
| 	if self.peer == peer { | ||||
| 		// new block update
 | ||||
| 		// peer is already active best peer, request hashes
 | ||||
| 		poolLogger.Debugf("[%s] already the best peer. request hashes from %s", peerId, name(currentBlock)) | ||||
| 		peer.requestBlockHashes(currentBlock) | ||||
| 		return true | ||||
| 		poolLogger.Debugf("[%s] already the best peer. Request new head section info from %s", peerId, name(currentBlockHash)) | ||||
| 		peer.headSectionC <- nil | ||||
| 		best = true | ||||
| 	} else { | ||||
| 		currentTD := ethutil.Big0 | ||||
| 		if self.peer != nil { | ||||
| 			currentTD = self.peer.td | ||||
| 		} | ||||
| 		if td.Cmp(currentTD) > 0 { | ||||
| 			poolLogger.Debugf("peer %v promoted best peer", peerId) | ||||
| 			self.switchPeer(self.peer, peer) | ||||
| 			self.peer = peer | ||||
| 			best = true | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| 	currentTD := ethutil.Big0 | ||||
| 	if self.peer != nil { | ||||
| 		currentTD = self.peer.td | ||||
| 	} | ||||
| 	if td.Cmp(currentTD) > 0 { | ||||
| 		poolLogger.Debugf("peer %v promoted best peer", peerId) | ||||
| 		self.switchPeer(self.peer, peer) | ||||
| 		self.peer = peer | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| func (self *BlockPool) requestHeadSection(peer *peerInfo) { | ||||
| 	self.wg.Add(1) | ||||
| 	self.procWg.Add(1) | ||||
| 	poolLogger.Debugf("[%s] head section at [%s] requesting info", peer.id, name(peer.currentBlockHash)) | ||||
| 
 | ||||
| 	go func() { | ||||
| 		var idle bool | ||||
| 		peer.lock.RLock() | ||||
| 		quitC := peer.quitC | ||||
| 		currentBlockHash := peer.currentBlockHash | ||||
| 		peer.lock.RUnlock() | ||||
| 		blockHashesRequestTimer := time.NewTimer(0) | ||||
| 		blocksRequestTimer := time.NewTimer(0) | ||||
| 		suicide := time.NewTimer(blockHashesTimeout * time.Second) | ||||
| 		blockHashesRequestTimer.Stop() | ||||
| 		defer blockHashesRequestTimer.Stop() | ||||
| 		defer blocksRequestTimer.Stop() | ||||
| 
 | ||||
| 		entry := self.get(currentBlockHash) | ||||
| 		if entry != nil { | ||||
| 			entry.node.lock.RLock() | ||||
| 			currentBlock := entry.node.block | ||||
| 			entry.node.lock.RUnlock() | ||||
| 			if currentBlock != nil { | ||||
| 				peer.lock.Lock() | ||||
| 				peer.currentBlock = currentBlock | ||||
| 				peer.parentHash = currentBlock.ParentHash() | ||||
| 				poolLogger.Debugf("[%s] head block [%s] found", peer.id, name(currentBlockHash)) | ||||
| 				peer.lock.Unlock() | ||||
| 				blockHashesRequestTimer.Reset(0) | ||||
| 				blocksRequestTimer.Stop() | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	LOOP: | ||||
| 		for { | ||||
| 
 | ||||
| 			select { | ||||
| 			case <-self.quit: | ||||
| 				break LOOP | ||||
| 
 | ||||
| 			case <-quitC: | ||||
| 				poolLogger.Debugf("[%s] head section at [%s] incomplete - quit request loop", peer.id, name(currentBlockHash)) | ||||
| 				break LOOP | ||||
| 
 | ||||
| 			case headSection := <-peer.headSectionC: | ||||
| 				peer.lock.Lock() | ||||
| 				peer.headSection = headSection | ||||
| 				if headSection == nil { | ||||
| 					oldBlockHash := currentBlockHash | ||||
| 					currentBlockHash = peer.currentBlockHash | ||||
| 					poolLogger.Debugf("[%s] head section changed [%s] -> [%s]", peer.id, name(oldBlockHash), name(currentBlockHash)) | ||||
| 					if idle { | ||||
| 						idle = false | ||||
| 						suicide.Reset(blockHashesTimeout * time.Second) | ||||
| 						self.procWg.Add(1) | ||||
| 					} | ||||
| 					blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond) | ||||
| 				} else { | ||||
| 					poolLogger.DebugDetailf("[%s] head section at [%s] created", peer.id, name(currentBlockHash)) | ||||
| 					if !idle { | ||||
| 						idle = true | ||||
| 						suicide.Stop() | ||||
| 						self.procWg.Done() | ||||
| 					} | ||||
| 				} | ||||
| 				peer.lock.Unlock() | ||||
| 				blockHashesRequestTimer.Stop() | ||||
| 
 | ||||
| 			case <-blockHashesRequestTimer.C: | ||||
| 				poolLogger.DebugDetailf("[%s] head section at [%s] not found, requesting block hashes", peer.id, name(currentBlockHash)) | ||||
| 				peer.requestBlockHashes(currentBlockHash) | ||||
| 				blockHashesRequestTimer.Reset(blockHashesRequestInterval * time.Millisecond) | ||||
| 
 | ||||
| 			case currentBlock := <-peer.currentBlockC: | ||||
| 				peer.lock.Lock() | ||||
| 				peer.currentBlock = currentBlock | ||||
| 				peer.parentHash = currentBlock.ParentHash() | ||||
| 				poolLogger.DebugDetailf("[%s] head block [%s] found", peer.id, name(currentBlockHash)) | ||||
| 				peer.lock.Unlock() | ||||
| 				if self.hasBlock(currentBlock.ParentHash()) { | ||||
| 					if err := self.insertChain(types.Blocks([]*types.Block{currentBlock})); err != nil { | ||||
| 						peer.peerError(ErrInvalidBlock, "%v", err) | ||||
| 					} | ||||
| 					if !idle { | ||||
| 						idle = true | ||||
| 						suicide.Stop() | ||||
| 						self.procWg.Done() | ||||
| 					} | ||||
| 				} else { | ||||
| 					blockHashesRequestTimer.Reset(0) | ||||
| 				} | ||||
| 				blocksRequestTimer.Stop() | ||||
| 
 | ||||
| 			case <-blocksRequestTimer.C: | ||||
| 				peer.lock.RLock() | ||||
| 				poolLogger.DebugDetailf("[%s] head block [%s] not found, requesting", peer.id, name(currentBlockHash)) | ||||
| 				peer.requestBlocks([][]byte{peer.currentBlockHash}) | ||||
| 				peer.lock.RUnlock() | ||||
| 				blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond) | ||||
| 
 | ||||
| 			case <-suicide.C: | ||||
| 				peer.peerError(ErrInsufficientChainInfo, "peer failed to provide block hashes or head block for block hash %x", currentBlockHash) | ||||
| 				break LOOP | ||||
| 			} | ||||
| 		} | ||||
| 		self.wg.Done() | ||||
| 		if !idle { | ||||
| 			self.procWg.Done() | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
| 
 | ||||
| // RemovePeer is called by the eth protocol when the peer disconnects
 | ||||
| @ -274,13 +402,13 @@ func (self *BlockPool) RemovePeer(peerId string) { | ||||
| 				newPeer = info | ||||
| 			} | ||||
| 		} | ||||
| 		self.peer = newPeer | ||||
| 		self.switchPeer(peer, newPeer) | ||||
| 		if newPeer != nil { | ||||
| 			poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id, newPeer.td) | ||||
| 		} else { | ||||
| 			poolLogger.Warnln("no peers") | ||||
| 		} | ||||
| 		self.peer = newPeer | ||||
| 		self.switchPeer(peer, newPeer) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -299,25 +427,56 @@ func (self *BlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string) | ||||
| 		return | ||||
| 	} | ||||
| 	// peer is still the best
 | ||||
| 	poolLogger.Debugf("adding hashes for best peer %s", peerId) | ||||
| 
 | ||||
| 	var size, n int | ||||
| 	var hash []byte | ||||
| 	var ok bool | ||||
| 	var section, child, parent *section | ||||
| 	var ok, headSection bool | ||||
| 	var sec, child, parent *section | ||||
| 	var entry *poolEntry | ||||
| 	var nodes []*poolNode | ||||
| 	bestPeer := peer | ||||
| 
 | ||||
| 	hash, ok = next() | ||||
| 	peer.lock.Lock() | ||||
| 	if bytes.Compare(peer.parentHash, hash) == 0 { | ||||
| 		if self.hasBlock(peer.currentBlockHash) { | ||||
| 			return | ||||
| 		} | ||||
| 		poolLogger.Debugf("adding hashes at chain head for best peer %s starting from [%s]", peerId, name(peer.currentBlockHash)) | ||||
| 		headSection = true | ||||
| 
 | ||||
| 		if entry := self.get(peer.currentBlockHash); entry == nil { | ||||
| 			node := &poolNode{ | ||||
| 				hash:    peer.currentBlockHash, | ||||
| 				block:   peer.currentBlock, | ||||
| 				peer:    peerId, | ||||
| 				blockBy: peerId, | ||||
| 			} | ||||
| 			if size == 0 { | ||||
| 				sec = newSection() | ||||
| 			} | ||||
| 			nodes = append(nodes, node) | ||||
| 			size++ | ||||
| 			n++ | ||||
| 		} else { | ||||
| 			child = entry.section | ||||
| 		} | ||||
| 	} else { | ||||
| 		poolLogger.Debugf("adding hashes for best peer %s starting from [%s]", peerId, name(hash)) | ||||
| 	} | ||||
| 	quitC := peer.quitC | ||||
| 	peer.lock.Unlock() | ||||
| 
 | ||||
| LOOP: | ||||
| 	// iterate using next (rlp stream lazy decoder) feeding hashesC
 | ||||
| 	for hash, ok = next(); ok; hash, ok = next() { | ||||
| 	for ; ok; hash, ok = next() { | ||||
| 		n++ | ||||
| 		select { | ||||
| 		case <-self.quit: | ||||
| 			return | ||||
| 		case <-peer.quitC: | ||||
| 		case <-quitC: | ||||
| 			// if the peer is demoted, no more hashes taken
 | ||||
| 			peer = nil | ||||
| 			bestPeer = nil | ||||
| 			break LOOP | ||||
| 		default: | ||||
| 		} | ||||
| @ -325,8 +484,8 @@ LOOP: | ||||
| 			// check if known block connecting the downloaded chain to our blockchain
 | ||||
| 			poolLogger.DebugDetailf("[%s] known block", name(hash)) | ||||
| 			// mark child as absolute pool root with parent known to blockchain
 | ||||
| 			if section != nil { | ||||
| 				self.connectToBlockChain(section) | ||||
| 			if sec != nil { | ||||
| 				self.connectToBlockChain(sec) | ||||
| 			} else { | ||||
| 				if child != nil { | ||||
| 					self.connectToBlockChain(child) | ||||
| @ -340,6 +499,7 @@ LOOP: | ||||
| 			// reached a known chain in the pool
 | ||||
| 			if entry.node == entry.section.bottom && n == 1 { | ||||
| 				// the first block hash received is an orphan in the pool, so rejoice and continue
 | ||||
| 				poolLogger.DebugDetailf("[%s] connecting child section", sectionName(entry.section)) | ||||
| 				child = entry.section | ||||
| 				continue LOOP | ||||
| 			} | ||||
| @ -353,7 +513,7 @@ LOOP: | ||||
| 			peer: peerId, | ||||
| 		} | ||||
| 		if size == 0 { | ||||
| 			section = newSection() | ||||
| 			sec = newSection() | ||||
| 		} | ||||
| 		nodes = append(nodes, node) | ||||
| 		size++ | ||||
| @ -379,10 +539,10 @@ LOOP: | ||||
| 	} | ||||
| 
 | ||||
| 	if size > 0 { | ||||
| 		self.processSection(section, nodes) | ||||
| 		poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(section), size, sectionName(child)) | ||||
| 		self.link(parent, section) | ||||
| 		self.link(section, child) | ||||
| 		self.processSection(sec, nodes) | ||||
| 		poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(sec), size, sectionName(child)) | ||||
| 		self.link(parent, sec) | ||||
| 		self.link(sec, child) | ||||
| 	} else { | ||||
| 		poolLogger.DebugDetailf("[%s]->[%s] connecting known sections", sectionName(parent), sectionName(child)) | ||||
| 		self.link(parent, child) | ||||
| @ -390,15 +550,31 @@ LOOP: | ||||
| 
 | ||||
| 	self.chainLock.Unlock() | ||||
| 
 | ||||
| 	if parent != nil && peer != nil { | ||||
| 	if parent != nil && bestPeer != nil { | ||||
| 		self.activateChain(parent, peer) | ||||
| 		poolLogger.Debugf("[%s] activate parent section [%s]", name(parent.top.hash), sectionName(parent)) | ||||
| 	} | ||||
| 
 | ||||
| 	if section != nil { | ||||
| 		peer.addSection(section.top.hash, section) | ||||
| 		section.controlC <- peer | ||||
| 		poolLogger.Debugf("[%s] activate new section", sectionName(section)) | ||||
| 	if sec != nil { | ||||
| 		peer.addSection(sec.top.hash, sec) | ||||
| 		// request next section here once, only repeat if bottom block arrives,
 | ||||
| 		// otherwise no way to check if it arrived
 | ||||
| 		peer.requestBlockHashes(sec.bottom.hash) | ||||
| 		sec.controlC <- bestPeer | ||||
| 		poolLogger.Debugf("[%s] activate new section", sectionName(sec)) | ||||
| 	} | ||||
| 
 | ||||
| 	if headSection { | ||||
| 		var headSec *section | ||||
| 		switch { | ||||
| 		case sec != nil: | ||||
| 			headSec = sec | ||||
| 		case child != nil: | ||||
| 			headSec = child | ||||
| 		default: | ||||
| 			headSec = parent | ||||
| 		} | ||||
| 		peer.headSectionC <- headSec | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -426,14 +602,21 @@ func sectionName(section *section) (name string) { | ||||
| // only the first PoW-valid block for a hash is considered legit
 | ||||
| func (self *BlockPool) AddBlock(block *types.Block, peerId string) { | ||||
| 	hash := block.Hash() | ||||
| 	if self.hasBlock(hash) { | ||||
| 		poolLogger.DebugDetailf("block [%s] already known", name(hash)) | ||||
| 		return | ||||
| 	} | ||||
| 	self.peersLock.Lock() | ||||
| 	peer := self.peer | ||||
| 	self.peersLock.Unlock() | ||||
| 
 | ||||
| 	entry := self.get(hash) | ||||
| 	if bytes.Compare(hash, peer.currentBlockHash) == 0 { | ||||
| 		poolLogger.Debugf("add head block [%s] for peer %s", name(hash), peerId) | ||||
| 		peer.currentBlockC <- block | ||||
| 	} else { | ||||
| 		if entry == nil { | ||||
| 			poolLogger.Warnf("unrequested block [%s] by peer %s", name(hash), peerId) | ||||
| 			self.peerError(peerId, ErrUnrequestedBlock, "%x", hash) | ||||
| 		} | ||||
| 	} | ||||
| 	if entry == nil { | ||||
| 		poolLogger.Warnf("unrequested block [%x] by peer %s", hash, peerId) | ||||
| 		self.peerError(peerId, ErrUnrequestedBlock, "%x", hash) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| @ -443,17 +626,21 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) { | ||||
| 
 | ||||
| 	// check if block already present
 | ||||
| 	if node.block != nil { | ||||
| 		poolLogger.DebugDetailf("block [%x] already sent by %s", name(hash), node.blockBy) | ||||
| 		poolLogger.DebugDetailf("block [%s] already sent by %s", name(hash), node.blockBy) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// validate block for PoW
 | ||||
| 	if !self.verifyPoW(block) { | ||||
| 		poolLogger.Warnf("invalid pow on block [%x] by peer %s", hash, peerId) | ||||
| 		self.peerError(peerId, ErrInvalidPoW, "%x", hash) | ||||
| 		return | ||||
| 	} | ||||
| 	if self.hasBlock(hash) { | ||||
| 		poolLogger.DebugDetailf("block [%s] already known", name(hash)) | ||||
| 	} else { | ||||
| 
 | ||||
| 		// validate block for PoW
 | ||||
| 		if !self.verifyPoW(block) { | ||||
| 			poolLogger.Warnf("invalid pow on block [%s] by peer %s", name(hash), peerId) | ||||
| 			self.peerError(peerId, ErrInvalidPoW, "%x", hash) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	poolLogger.Debugf("added block [%s] sent by peer %s", name(hash), peerId) | ||||
| 	node.block = block | ||||
| 	node.blockBy = peerId | ||||
| @ -544,23 +731,23 @@ LOOP: | ||||
| // - when turned off (if peer disconnects and new peer connects with alternative chain), no blockrequests are made but absolute expiry timer is ticking
 | ||||
| // - when turned back on it recursively calls itself on the root of the next chain section
 | ||||
| // - when exits, signals to
 | ||||
| func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| func (self *BlockPool) processSection(sec *section, nodes []*poolNode) { | ||||
| 
 | ||||
| 	for i, node := range nodes { | ||||
| 		entry := &poolEntry{node: node, section: section, index: i} | ||||
| 		entry := &poolEntry{node: node, section: sec, index: i} | ||||
| 		self.set(node.hash, entry) | ||||
| 	} | ||||
| 
 | ||||
| 	section.bottom = nodes[len(nodes)-1] | ||||
| 	section.top = nodes[0] | ||||
| 	section.nodes = nodes | ||||
| 	poolLogger.DebugDetailf("[%s] setup section process", sectionName(section)) | ||||
| 	sec.bottom = nodes[len(nodes)-1] | ||||
| 	sec.top = nodes[0] | ||||
| 	sec.nodes = nodes | ||||
| 	poolLogger.DebugDetailf("[%s] setup section process", sectionName(sec)) | ||||
| 
 | ||||
| 	self.wg.Add(1) | ||||
| 	go func() { | ||||
| 
 | ||||
| 		// absolute time after which sub-chain is killed if not complete (some blocks are missing)
 | ||||
| 		suicideTimer := time.After(blockTimeout * time.Minute) | ||||
| 		suicideTimer := time.After(blocksTimeout * time.Second) | ||||
| 
 | ||||
| 		var peer, newPeer *peerInfo | ||||
| 
 | ||||
| @ -580,21 +767,23 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 		var insertChain bool | ||||
| 		var quitC chan bool | ||||
| 
 | ||||
| 		var blockChainC = section.blockChainC | ||||
| 		var blockChainC = sec.blockChainC | ||||
| 
 | ||||
| 		var parentHash []byte | ||||
| 
 | ||||
| 	LOOP: | ||||
| 		for { | ||||
| 
 | ||||
| 			if insertChain { | ||||
| 				insertChain = false | ||||
| 				rest, err := self.addSectionToBlockChain(section) | ||||
| 				rest, err := self.addSectionToBlockChain(sec) | ||||
| 				if err != nil { | ||||
| 					close(section.suicideC) | ||||
| 					close(sec.suicideC) | ||||
| 					continue LOOP | ||||
| 				} | ||||
| 				if rest == 0 { | ||||
| 					blocksRequestsComplete = true | ||||
| 					child := self.getChild(section) | ||||
| 					child := self.getChild(sec) | ||||
| 					if child != nil { | ||||
| 						self.connectToBlockChain(child) | ||||
| 					} | ||||
| @ -603,7 +792,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 
 | ||||
| 			if blockHashesRequestsComplete && blocksRequestsComplete { | ||||
| 				// not waiting for hashes any more
 | ||||
| 				poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(section), depth, blocksRequests, blockHashesRequests) | ||||
| 				poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(sec), depth, blocksRequests, blockHashesRequests) | ||||
| 				break LOOP | ||||
| 			} // otherwise suicide if no hashes coming
 | ||||
| 
 | ||||
| @ -611,11 +800,12 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 				// went through all blocks in section
 | ||||
| 				if missing == 0 { | ||||
| 					// no missing blocks
 | ||||
| 					poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth) | ||||
| 					poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth) | ||||
| 					blocksRequestsComplete = true | ||||
| 					blocksRequestTimer = nil | ||||
| 					blocksRequestTime = false | ||||
| 				} else { | ||||
| 					poolLogger.DebugDetailf("[%s] section checked: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth) | ||||
| 					// some missing blocks
 | ||||
| 					blocksRequests++ | ||||
| 					if len(hashes) > 0 { | ||||
| @ -630,8 +820,8 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 							idle++ | ||||
| 							// too many idle rounds
 | ||||
| 							if idle >= blocksRequestMaxIdleRounds { | ||||
| 								poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(section), idle, blocksRequests, missing, lastMissing, depth) | ||||
| 								close(section.suicideC) | ||||
| 								poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(sec), idle, blocksRequests, missing, lastMissing, depth) | ||||
| 								close(sec.suicideC) | ||||
| 							} | ||||
| 						} else { | ||||
| 							idle = 0 | ||||
| @ -653,22 +843,39 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 			//
 | ||||
| 
 | ||||
| 			if ready && blocksRequestTime && !blocksRequestsComplete { | ||||
| 				poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth) | ||||
| 				poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth) | ||||
| 				blocksRequestTimer = time.After(blocksRequestInterval * time.Millisecond) | ||||
| 				blocksRequestTime = false | ||||
| 				processC = offC | ||||
| 			} | ||||
| 
 | ||||
| 			if blockHashesRequestTime { | ||||
| 				if self.getParent(section) != nil { | ||||
| 				var parentSection = self.getParent(sec) | ||||
| 				if parentSection == nil { | ||||
| 					if parent := self.get(parentHash); parent != nil { | ||||
| 						parentSection = parent.section | ||||
| 						self.chainLock.Lock() | ||||
| 						self.link(parentSection, sec) | ||||
| 						self.chainLock.Unlock() | ||||
| 					} else { | ||||
| 						if self.hasBlock(parentHash) { | ||||
| 							insertChain = true | ||||
| 							blockHashesRequestTime = false | ||||
| 							blockHashesRequestTimer = nil | ||||
| 							blockHashesRequestsComplete = true | ||||
| 							continue LOOP | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if parentSection != nil { | ||||
| 					// if not root of chain, switch off
 | ||||
| 					poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(section), blockHashesRequests) | ||||
| 					poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(sec), blockHashesRequests) | ||||
| 					blockHashesRequestTimer = nil | ||||
| 					blockHashesRequestsComplete = true | ||||
| 				} else { | ||||
| 					blockHashesRequests++ | ||||
| 					poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(section), blockHashesRequests) | ||||
| 					peer.requestBlockHashes(section.bottom.hash) | ||||
| 					poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(sec), blockHashesRequests) | ||||
| 					peer.requestBlockHashes(sec.bottom.hash) | ||||
| 					blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Millisecond) | ||||
| 				} | ||||
| 				blockHashesRequestTime = false | ||||
| @ -682,27 +889,27 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 				// peer quit or demoted, put section in idle mode
 | ||||
| 				quitC = nil | ||||
| 				go func() { | ||||
| 					section.controlC <- nil | ||||
| 					sec.controlC <- nil | ||||
| 				}() | ||||
| 
 | ||||
| 			case <-self.purgeC: | ||||
| 				suicideTimer = time.After(0) | ||||
| 
 | ||||
| 			case <-suicideTimer: | ||||
| 				close(section.suicideC) | ||||
| 				poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth) | ||||
| 				close(sec.suicideC) | ||||
| 				poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth) | ||||
| 
 | ||||
| 			case <-section.suicideC: | ||||
| 				poolLogger.Debugf("[%s] suicide", sectionName(section)) | ||||
| 			case <-sec.suicideC: | ||||
| 				poolLogger.Debugf("[%s] suicide", sectionName(sec)) | ||||
| 
 | ||||
| 				// first delink from child and parent under chainlock
 | ||||
| 				self.chainLock.Lock() | ||||
| 				self.link(nil, section) | ||||
| 				self.link(section, nil) | ||||
| 				self.link(nil, sec) | ||||
| 				self.link(sec, nil) | ||||
| 				self.chainLock.Unlock() | ||||
| 				// delete node entries from pool index under pool lock
 | ||||
| 				self.lock.Lock() | ||||
| 				for _, node := range section.nodes { | ||||
| 				for _, node := range sec.nodes { | ||||
| 					delete(self.pool, string(node.hash)) | ||||
| 				} | ||||
| 				self.lock.Unlock() | ||||
| @ -710,20 +917,20 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 				break LOOP | ||||
| 
 | ||||
| 			case <-blocksRequestTimer: | ||||
| 				poolLogger.DebugDetailf("[%s] block request time", sectionName(section)) | ||||
| 				poolLogger.DebugDetailf("[%s] block request time", sectionName(sec)) | ||||
| 				blocksRequestTime = true | ||||
| 
 | ||||
| 			case <-blockHashesRequestTimer: | ||||
| 				poolLogger.DebugDetailf("[%s] hash request time", sectionName(section)) | ||||
| 				poolLogger.DebugDetailf("[%s] hash request time", sectionName(sec)) | ||||
| 				blockHashesRequestTime = true | ||||
| 
 | ||||
| 			case newPeer = <-section.controlC: | ||||
| 			case newPeer = <-sec.controlC: | ||||
| 
 | ||||
| 				// active -> idle
 | ||||
| 				if peer != nil && newPeer == nil { | ||||
| 					self.procWg.Done() | ||||
| 					if init { | ||||
| 						poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth) | ||||
| 						poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth) | ||||
| 					} | ||||
| 					blocksRequestTime = false | ||||
| 					blocksRequestTimer = nil | ||||
| @ -739,11 +946,11 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 				if peer == nil && newPeer != nil { | ||||
| 					self.procWg.Add(1) | ||||
| 
 | ||||
| 					poolLogger.Debugf("[%s] active mode", sectionName(section)) | ||||
| 					poolLogger.Debugf("[%s] active mode", sectionName(sec)) | ||||
| 					if !blocksRequestsComplete { | ||||
| 						blocksRequestTime = true | ||||
| 					} | ||||
| 					if !blockHashesRequestsComplete { | ||||
| 					if !blockHashesRequestsComplete && parentHash != nil { | ||||
| 						blockHashesRequestTime = true | ||||
| 					} | ||||
| 					if !init { | ||||
| @ -753,13 +960,13 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 						missing = 0 | ||||
| 						self.wg.Add(1) | ||||
| 						self.procWg.Add(1) | ||||
| 						depth = len(section.nodes) | ||||
| 						depth = len(sec.nodes) | ||||
| 						lastMissing = depth | ||||
| 						// if not run at least once fully, launch iterator
 | ||||
| 						go func() { | ||||
| 							var node *poolNode | ||||
| 						IT: | ||||
| 							for _, node = range section.nodes { | ||||
| 							for _, node = range sec.nodes { | ||||
| 								select { | ||||
| 								case processC <- node: | ||||
| 								case <-self.quit: | ||||
| @ -771,7 +978,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 							self.procWg.Done() | ||||
| 						}() | ||||
| 					} else { | ||||
| 						poolLogger.Debugf("[%s] restore earlier state", sectionName(section)) | ||||
| 						poolLogger.Debugf("[%s] restore earlier state", sectionName(sec)) | ||||
| 						processC = offC | ||||
| 					} | ||||
| 				} | ||||
| @ -781,7 +988,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 				} | ||||
| 				peer = newPeer | ||||
| 
 | ||||
| 			case waiter := <-section.forkC: | ||||
| 			case waiter := <-sec.forkC: | ||||
| 				// this case just blocks the process until section is split at the fork
 | ||||
| 				<-waiter | ||||
| 				init = false | ||||
| @ -794,7 +1001,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 					init = true | ||||
| 					done = true | ||||
| 					processC = make(chan *poolNode, missing) | ||||
| 					poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(section), missing, lastMissing, depth) | ||||
| 					poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth) | ||||
| 					continue LOOP | ||||
| 				} | ||||
| 				if ready { | ||||
| @ -811,17 +1018,24 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 					missing++ | ||||
| 					hashes = append(hashes, node.hash) | ||||
| 					if len(hashes) == blockBatchSize { | ||||
| 						poolLogger.Debugf("[%s] request %v missing blocks", sectionName(section), len(hashes)) | ||||
| 						poolLogger.Debugf("[%s] request %v missing blocks", sectionName(sec), len(hashes)) | ||||
| 						self.requestBlocks(blocksRequests, hashes) | ||||
| 						hashes = nil | ||||
| 					} | ||||
| 					missingC <- node | ||||
| 				} else { | ||||
| 					if blockChainC == nil && i == lastMissing { | ||||
| 						insertChain = true | ||||
| 					if i == lastMissing { | ||||
| 						if blockChainC == nil { | ||||
| 							insertChain = true | ||||
| 						} else { | ||||
| 							if parentHash == nil { | ||||
| 								parentHash = block.ParentHash() | ||||
| 								poolLogger.Debugf("[%s] found root block [%s]", sectionName(sec), name(parentHash)) | ||||
| 								blockHashesRequestTime = true | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				poolLogger.Debugf("[%s] %v/%v/%v/%v", sectionName(section), i, missing, lastMissing, depth) | ||||
| 				if i == lastMissing && init { | ||||
| 					done = true | ||||
| 				} | ||||
| @ -829,23 +1043,22 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) { | ||||
| 			case <-blockChainC: | ||||
| 				// closed blockChain channel indicates that the blockpool is reached
 | ||||
| 				// connected to the blockchain, insert the longest chain of blocks
 | ||||
| 				poolLogger.Debugf("[%s] reached blockchain", sectionName(section)) | ||||
| 				poolLogger.Debugf("[%s] reached blockchain", sectionName(sec)) | ||||
| 				blockChainC = nil | ||||
| 				// switch off hash requests in case they were on
 | ||||
| 				blockHashesRequestTime = false | ||||
| 				blockHashesRequestTimer = nil | ||||
| 				blockHashesRequestsComplete = true | ||||
| 				// section root has block
 | ||||
| 				if len(section.nodes) > 0 && section.nodes[len(section.nodes)-1].block != nil { | ||||
| 				if len(sec.nodes) > 0 && sec.nodes[len(sec.nodes)-1].block != nil { | ||||
| 					insertChain = true | ||||
| 				} | ||||
| 				continue LOOP | ||||
| 
 | ||||
| 			} // select
 | ||||
| 		} // for
 | ||||
| 		poolLogger.Debugf("[%s] section complete: %v block hashes requests - %v block requests - missing %v/%v/%v", sectionName(section), blockHashesRequests, blocksRequests, missing, lastMissing, depth) | ||||
| 
 | ||||
| 		close(section.offC) | ||||
| 		close(sec.offC) | ||||
| 
 | ||||
| 		self.wg.Done() | ||||
| 		if peer != nil { | ||||
| @ -917,22 +1130,28 @@ func (self *peerInfo) addSection(hash []byte, section *section) (found *section) | ||||
| 	defer self.lock.Unlock() | ||||
| 	key := string(hash) | ||||
| 	found = self.sections[key] | ||||
| 	poolLogger.DebugDetailf("[%s] section process %s registered", sectionName(section), self.id) | ||||
| 	poolLogger.DebugDetailf("[%s] section process stored for %s", sectionName(section), self.id) | ||||
| 	self.sections[key] = section | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) { | ||||
| 	if newPeer != nil { | ||||
| 		entry := self.get(newPeer.currentBlock) | ||||
| 		if entry == nil { | ||||
| 			poolLogger.Debugf("[%s] head block [%s] not found, requesting hashes", newPeer.id, name(newPeer.currentBlock)) | ||||
| 			newPeer.requestBlockHashes(newPeer.currentBlock) | ||||
| 		} else { | ||||
| 			poolLogger.Debugf("[%s] head block [%s] found, activate chain at section [%s]", newPeer.id, name(newPeer.currentBlock), sectionName(entry.section)) | ||||
| 			self.activateChain(entry.section, newPeer) | ||||
| 		} | ||||
| 		newPeer.quitC = make(chan bool) | ||||
| 		poolLogger.DebugDetailf("[%s] activate section processes", newPeer.id) | ||||
| 		var addSections []*section | ||||
| 		for hash, section := range newPeer.sections { | ||||
| 			// split sections get reorganised here
 | ||||
| 			if string(section.top.hash) != hash { | ||||
| 				addSections = append(addSections, section) | ||||
| 				if entry := self.get([]byte(hash)); entry != nil { | ||||
| 					addSections = append(addSections, entry.section) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		for _, section := range addSections { | ||||
| 			newPeer.sections[string(section.top.hash)] = section | ||||
| 		} | ||||
| 		for hash, section := range newPeer.sections { | ||||
| 			// this will block if section process is waiting for peer lock
 | ||||
| 			select { | ||||
| @ -940,12 +1159,26 @@ func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) { | ||||
| 				poolLogger.DebugDetailf("[%s][%x] section process complete - remove", newPeer.id, hash[:4]) | ||||
| 				delete(newPeer.sections, hash) | ||||
| 			case section.controlC <- newPeer: | ||||
| 				poolLogger.DebugDetailf("[%s][%x] registered peer with section", newPeer.id, hash[:4]) | ||||
| 				poolLogger.DebugDetailf("[%s][%x] activates section [%s]", newPeer.id, hash[:4], sectionName(section)) | ||||
| 			} | ||||
| 		} | ||||
| 		newPeer.quitC = make(chan bool) | ||||
| 		newPeer.lock.Lock() | ||||
| 		headSection := newPeer.headSection | ||||
| 		currentBlockHash := newPeer.currentBlockHash | ||||
| 		newPeer.lock.Unlock() | ||||
| 		if headSection == nil { | ||||
| 			poolLogger.DebugDetailf("[%s] head section for [%s] not created, requesting info", newPeer.id, name(currentBlockHash)) | ||||
| 			self.requestHeadSection(newPeer) | ||||
| 		} else { | ||||
| 			if entry := self.get(currentBlockHash); entry != nil { | ||||
| 				headSection = entry.section | ||||
| 			} | ||||
| 			poolLogger.DebugDetailf("[%s] activate chain at head section [%s] for current head [%s]", newPeer.id, sectionName(headSection), name(currentBlockHash)) | ||||
| 			self.activateChain(headSection, newPeer) | ||||
| 		} | ||||
| 	} | ||||
| 	if oldPeer != nil { | ||||
| 		poolLogger.DebugDetailf("[%s] quit section processes", oldPeer.id) | ||||
| 		close(oldPeer.quitC) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -18,7 +18,7 @@ import ( | ||||
| 
 | ||||
| const waitTimeout = 60 // seconds
 | ||||
| 
 | ||||
| var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugLevel)) | ||||
| var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel)) | ||||
| 
 | ||||
| var ini = false | ||||
| 
 | ||||
| @ -336,12 +336,12 @@ func (self *peerTester) AddPeer() bool { | ||||
| 
 | ||||
| // peer sends blockhashes if and when gets a request
 | ||||
| func (self *peerTester) AddBlockHashes(indexes ...int) { | ||||
| 	i := 0 | ||||
| 	fmt.Printf("ready to add block hashes %v\n", indexes) | ||||
| 
 | ||||
| 	self.waitBlockHashesRequests(indexes[0]) | ||||
| 	fmt.Printf("adding block hashes %v\n", indexes) | ||||
| 	hashes := self.hashPool.indexesToHashes(indexes) | ||||
| 	i := 1 | ||||
| 	next := func() (hash []byte, ok bool) { | ||||
| 		if i < len(hashes) { | ||||
| 			hash = hashes[i] | ||||
| @ -415,7 +415,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.id != "peer0" { | ||||
| 		t.Errorf("peer0 (TD=1) not set as best") | ||||
| 	} | ||||
| 	peer0.checkBlockHashesRequests(0) | ||||
| 	// peer0.checkBlockHashesRequests(0)
 | ||||
| 
 | ||||
| 	best = peer2.AddPeer() | ||||
| 	if !best { | ||||
| @ -424,7 +424,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.id != "peer2" { | ||||
| 		t.Errorf("peer2 (TD=3) not set as best") | ||||
| 	} | ||||
| 	peer2.checkBlockHashesRequests(2) | ||||
| 	peer2.waitBlocksRequests(2) | ||||
| 
 | ||||
| 	best = peer1.AddPeer() | ||||
| 	if best { | ||||
| @ -449,7 +449,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 { | ||||
| 		t.Errorf("peer2 TD not updated") | ||||
| 	} | ||||
| 	peer2.checkBlockHashesRequests(2, 3) | ||||
| 	peer2.waitBlocksRequests(3) | ||||
| 
 | ||||
| 	peer1.td = 3 | ||||
| 	peer1.currentBlock = 2 | ||||
| @ -474,7 +474,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.id != "peer1" { | ||||
| 		t.Errorf("existing peer1 (TD=3) should be set as best peer") | ||||
| 	} | ||||
| 	peer1.checkBlockHashesRequests(2) | ||||
| 	peer1.waitBlocksRequests(2) | ||||
| 
 | ||||
| 	blockPool.RemovePeer("peer1") | ||||
| 	peer, best = blockPool.getPeer("peer1") | ||||
| @ -485,6 +485,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.id != "peer0" { | ||||
| 		t.Errorf("existing peer0 (TD=1) should be set as best peer") | ||||
| 	} | ||||
| 	peer0.waitBlocksRequests(0) | ||||
| 
 | ||||
| 	blockPool.RemovePeer("peer0") | ||||
| 	peer, best = blockPool.getPeer("peer0") | ||||
| @ -502,7 +503,7 @@ func TestAddPeer(t *testing.T) { | ||||
| 	if blockPool.peer.id != "peer0" { | ||||
| 		t.Errorf("peer0 (TD=1) should be set as best") | ||||
| 	} | ||||
| 	peer0.checkBlockHashesRequests(0, 0, 3) | ||||
| 	peer0.waitBlocksRequests(3) | ||||
| 
 | ||||
| 	blockPool.Stop() | ||||
| 
 | ||||
| @ -513,17 +514,36 @@ func TestPeerWithKnownBlock(t *testing.T) { | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.refBlockChain[0] = nil | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| 	// hashPool, blockPool, blockPoolTester := newTestBlockPool()
 | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer0 := blockPoolTester.newPeer("0", 1, 0) | ||||
| 	peer0.AddPeer() | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	// no request on known block
 | ||||
| 	peer0.checkBlockHashesRequests() | ||||
| } | ||||
| 
 | ||||
| func TestPeerWithKnownParentBlock(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.initRefBlockChain(1) | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer0 := blockPoolTester.newPeer("0", 1, 1) | ||||
| 	peer0.AddPeer() | ||||
| 	peer0.AddBlocks(0, 1) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	peer0.checkBlocksRequests([]int{1}) | ||||
| 	peer0.checkBlockHashesRequests() | ||||
| 	blockPoolTester.refBlockChain[1] = []int{} | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestSimpleChain(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| @ -534,8 +554,9 @@ func TestSimpleChain(t *testing.T) { | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 2) | ||||
| 	peer1.AddPeer() | ||||
| 	peer1.AddBlocks(1, 2) | ||||
| 	go peer1.AddBlockHashes(2, 1, 0) | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 	peer1.AddBlocks(0, 1) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| @ -543,6 +564,26 @@ func TestSimpleChain(t *testing.T) { | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestChainConnectingWithParentHash(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| 	blockPoolTester.initRefBlockChain(3) | ||||
| 
 | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 3) | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(2, 3) | ||||
| 	go peer1.AddBlockHashes(3, 2, 1) | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	blockPoolTester.refBlockChain[3] = []int{} | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestInvalidBlock(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| @ -554,8 +595,9 @@ func TestInvalidBlock(t *testing.T) { | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 3) | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(2, 3) | ||||
| 	go peer1.AddBlockHashes(3, 2, 1, 0) | ||||
| 	peer1.AddBlocks(0, 1, 2, 3) | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| @ -566,7 +608,7 @@ func TestInvalidBlock(t *testing.T) { | ||||
| 			t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock) | ||||
| 		} | ||||
| 	} else { | ||||
| 		t.Errorf("expected invalid block error, got nothing") | ||||
| 		t.Errorf("expected invalid block error, got nothing %v", peer1.peerErrors) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -579,7 +621,7 @@ func TestVerifyPoW(t *testing.T) { | ||||
| 	blockPoolTester.blockPool.verifyPoW = func(b pow.Block) bool { | ||||
| 		bb, _ := b.(*types.Block) | ||||
| 		indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()}) | ||||
| 		if indexes[0] == 1 && !first { | ||||
| 		if indexes[0] == 2 && !first { | ||||
| 			first = true | ||||
| 			return false | ||||
| 		} else { | ||||
| @ -590,15 +632,17 @@ func TestVerifyPoW(t *testing.T) { | ||||
| 
 | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 2) | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 3) | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlockHashes(2, 1, 0) | ||||
| 	go peer1.AddBlocks(2, 3) | ||||
| 	go peer1.AddBlockHashes(3, 2, 1, 0) | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 	peer1.AddBlocks(0, 1) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	// blockPool.Wait(waitTimeout * time.Second)
 | ||||
| 	time.Sleep(1 * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	blockPoolTester.refBlockChain[2] = []int{} | ||||
| 	blockPoolTester.refBlockChain[1] = []int{} | ||||
| 	delete(blockPoolTester.refBlockChain, 2) | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| 	if len(peer1.peerErrors) == 1 { | ||||
| 		if peer1.peerErrors[0] != ErrInvalidPoW { | ||||
| @ -620,8 +664,9 @@ func TestMultiSectionChain(t *testing.T) { | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 5) | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(4, 5) | ||||
| 	go peer1.AddBlockHashes(5, 4, 3) | ||||
| 	go peer1.AddBlocks(2, 3, 4, 5) | ||||
| 	go peer1.AddBlocks(2, 3, 4) | ||||
| 	go peer1.AddBlockHashes(3, 2, 1, 0) | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| @ -641,14 +686,17 @@ func TestNewBlocksOnPartialChain(t *testing.T) { | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 5) | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(4, 5) // partially complete section
 | ||||
| 	go peer1.AddBlockHashes(5, 4, 3) | ||||
| 	peer1.AddBlocks(2, 3) // partially complete section
 | ||||
| 	peer1.AddBlocks(3, 4) // partially complete section
 | ||||
| 	// peer1 found new blocks
 | ||||
| 	peer1.td = 2 | ||||
| 	peer1.currentBlock = 7 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(6, 7) | ||||
| 	go peer1.AddBlockHashes(7, 6, 5) | ||||
| 	go peer1.AddBlocks(3, 4, 5, 6, 7) | ||||
| 	go peer1.AddBlocks(2, 3) | ||||
| 	go peer1.AddBlocks(5, 6) | ||||
| 	go peer1.AddBlockHashes(3, 2, 1, 0) // tests that hash request from known chain root is remembered
 | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| @ -658,35 +706,37 @@ func TestNewBlocksOnPartialChain(t *testing.T) { | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestPeerSwitch(t *testing.T) { | ||||
| func TestPeerSwitchUp(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| 	blockPoolTester.initRefBlockChain(6) | ||||
| 	blockPoolTester.initRefBlockChain(7) | ||||
| 
 | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 5) | ||||
| 	peer2 := blockPoolTester.newPeer("peer2", 2, 6) | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 6) | ||||
| 	peer2 := blockPoolTester.newPeer("peer2", 2, 7) | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlockHashes(5, 4, 3) | ||||
| 	go peer1.AddBlocks(5, 6) | ||||
| 	go peer1.AddBlockHashes(6, 5, 4, 3) //
 | ||||
| 	peer1.AddBlocks(2, 3)               // section partially complete, block 3 will be preserved after peer demoted
 | ||||
| 	peer2.AddPeer()                     // peer2 is promoted as best peer, peer1 is demoted
 | ||||
| 	go peer2.AddBlockHashes(6, 5)       //
 | ||||
| 	go peer2.AddBlocks(4, 5, 6)         // tests that block request for earlier section is remembered
 | ||||
| 	go peer2.AddBlocks(6, 7) | ||||
| 	go peer2.AddBlockHashes(7, 6)       //
 | ||||
| 	go peer2.AddBlocks(4, 5)            // tests that block request for earlier section is remembered
 | ||||
| 	go peer1.AddBlocks(3, 4)            // tests that connecting section by demoted peer is remembered and blocks are accepted from demoted peer
 | ||||
| 	go peer2.AddBlockHashes(3, 2, 1, 0) // tests that known chain section is activated, hash requests from 3 is remembered
 | ||||
| 	peer2.AddBlocks(0, 1, 2)            // final blocks linking to blockchain sent
 | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	blockPoolTester.refBlockChain[6] = []int{} | ||||
| 	blockPoolTester.refBlockChain[7] = []int{} | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestPeerDownSwitch(t *testing.T) { | ||||
| func TestPeerSwitchDown(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| @ -698,12 +748,39 @@ func TestPeerDownSwitch(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer2.AddPeer() | ||||
| 	go peer2.AddBlockHashes(6, 5, 4) | ||||
| 	peer2.AddBlocks(5, 6)                  // partially complete, section will be preserved
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4)       //
 | ||||
| 	peer2.AddBlocks(4, 5)                  //
 | ||||
| 	blockPool.RemovePeer("peer2")          // peer2 disconnects
 | ||||
| 	peer1.AddPeer()                        // inferior peer1 is promoted as best peer
 | ||||
| 	go peer1.AddBlockHashes(4, 3, 2, 1, 0) //
 | ||||
| 	go peer1.AddBlocks(3, 4, 5)            // tests that section set by demoted peer is remembered and blocks are accepted
 | ||||
| 	go peer1.AddBlocks(3, 4)               // tests that section set by demoted peer is remembered and blocks are accepted , this connects the chain sections together
 | ||||
| 	peer1.AddBlocks(0, 1, 2, 3) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| 	blockPoolTester.refBlockChain[6] = []int{} | ||||
| 	blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain) | ||||
| } | ||||
| 
 | ||||
| func TestPeerCompleteSectionSwitchDown(t *testing.T) { | ||||
| 	logInit() | ||||
| 	_, blockPool, blockPoolTester := newTestBlockPool(t) | ||||
| 	blockPoolTester.blockChain[0] = nil | ||||
| 	blockPoolTester.initRefBlockChain(6) | ||||
| 	blockPool.Start() | ||||
| 
 | ||||
| 	peer1 := blockPoolTester.newPeer("peer1", 1, 4) | ||||
| 	peer2 := blockPoolTester.newPeer("peer2", 2, 6) | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer2.AddPeer() | ||||
| 	peer2.AddBlocks(5, 6)               // partially complete, section will be preserved
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4)    //
 | ||||
| 	peer2.AddBlocks(3, 4, 5)            // complete section
 | ||||
| 	blockPool.RemovePeer("peer2")       // peer2 disconnects
 | ||||
| 	peer1.AddPeer()                     // inferior peer1 is promoted as best peer
 | ||||
| 	peer1.AddBlockHashes(4, 3, 2, 1, 0) // tests that hash request are directly connecting if the head block exists
 | ||||
| 	peer1.AddBlocks(0, 1, 2, 3) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| @ -725,11 +802,13 @@ func TestPeerSwitchBack(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer2.AddPeer() | ||||
| 	go peer2.AddBlocks(7, 8) | ||||
| 	go peer2.AddBlockHashes(8, 7, 6) | ||||
| 	go peer2.AddBlockHashes(6, 5, 4) | ||||
| 	peer2.AddBlocks(4, 5)                  // section partially complete
 | ||||
| 	peer1.AddPeer()                        // peer1 is promoted as best peer
 | ||||
| 	go peer1.AddBlockHashes(11, 10)        // only gives useless results
 | ||||
| 	go peer1.AddBlocks(10, 11)             //
 | ||||
| 	peer1.AddBlockHashes(11, 10)           // only gives useless results
 | ||||
| 	blockPool.RemovePeer("peer1")          // peer1 disconnects
 | ||||
| 	go peer2.AddBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
 | ||||
| 	go peer2.AddBlocks(3, 4, 5, 6, 7, 8)   // tests that section 4, 5, 6 and 7, 8 are remembered for missing blocks
 | ||||
| @ -756,11 +835,13 @@ func TestForkSimple(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(8, 9) | ||||
| 	go peer1.AddBlockHashes(9, 8, 7, 3, 2) | ||||
| 	peer1.AddBlocks(1, 2, 3, 7, 8, 9) | ||||
| 	peer1.AddBlocks(1, 2, 3, 7, 8) | ||||
| 	peer2.AddPeer()                        // peer2 is promoted as best peer
 | ||||
| 	go peer2.AddBlocks(5, 6)               //
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4, 3, 2) // fork on 3 -> 4 (earlier child: 7)
 | ||||
| 	go peer2.AddBlocks(1, 2, 3, 4, 5, 6) | ||||
| 	go peer2.AddBlocks(1, 2, 3, 4, 5) | ||||
| 	go peer2.AddBlockHashes(2, 1, 0) | ||||
| 	peer2.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| @ -790,23 +871,24 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlockHashes(9, 8, 7, 3, 2) | ||||
| 	peer1.AddBlocks(8, 9)                  // partial section
 | ||||
| 	peer1.AddBlocks(8, 9)                  //
 | ||||
| 	go peer1.AddBlockHashes(9, 8, 7, 3, 2) //
 | ||||
| 	peer1.AddBlocks(7, 8)                  // partial section
 | ||||
| 	peer2.AddPeer()                        //
 | ||||
| 	peer2.AddBlocks(5, 6)                  //
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
 | ||||
| 	peer2.AddBlocks(1, 2, 3, 4, 5, 6)      //
 | ||||
| 	peer2.AddBlocks(1, 2, 3, 4, 5)         //
 | ||||
| 
 | ||||
| 	// peer1 finds new blocks
 | ||||
| 	peer1.td = 3 | ||||
| 	peer1.currentBlock = 11 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(10, 11) | ||||
| 	go peer1.AddBlockHashes(11, 10, 9) | ||||
| 	peer1.AddBlocks(7, 8, 9, 10, 11) | ||||
| 	go peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered
 | ||||
| 	go peer1.AddBlocks(3, 7)      // tests that block requests on earlier fork are remembered
 | ||||
| 	// go peer1.AddBlockHashes(1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
 | ||||
| 	peer1.AddBlocks(9, 10) | ||||
| 	go peer1.AddBlocks(3, 7)         // tests that block requests on earlier fork are remembered
 | ||||
| 	go peer1.AddBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
 | ||||
| 	peer1.AddBlocks(0, 1, 2, 3) | ||||
| 	peer1.AddBlocks(0, 1) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| @ -834,16 +916,18 @@ func TestForkSwitchBackByPeerSwitchBack(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(8, 9) | ||||
| 	go peer1.AddBlockHashes(9, 8, 7, 3, 2) | ||||
| 	peer1.AddBlocks(8, 9) | ||||
| 	peer2.AddPeer()                        //
 | ||||
| 	peer1.AddBlocks(7, 8) | ||||
| 	peer2.AddPeer() | ||||
| 	go peer2.AddBlocks(5, 6)               //
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
 | ||||
| 	peer2.AddBlocks(2, 3, 4, 5, 6)         //
 | ||||
| 	peer2.AddBlocks(2, 3, 4, 5)            //
 | ||||
| 	blockPool.RemovePeer("peer2")          // peer2 disconnects, peer1 is promoted again as best peer
 | ||||
| 	peer1.AddBlockHashes(7, 3)             // tests that hash request from fork root is remembered
 | ||||
| 	go peer1.AddBlocks(3, 7, 8)            // tests that block requests on earlier fork are remembered
 | ||||
| 	go peer1.AddBlocks(3, 7)               // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
 | ||||
| 	go peer1.AddBlocks(1, 2)               //
 | ||||
| 	go peer1.AddBlockHashes(2, 1, 0)       //
 | ||||
| 	peer1.AddBlocks(0, 1, 2, 3) | ||||
| 	peer1.AddBlocks(0, 1) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
| 	blockPool.Stop() | ||||
| @ -871,17 +955,19 @@ func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) { | ||||
| 	peer2.blocksRequestsMap = peer1.blocksRequestsMap | ||||
| 
 | ||||
| 	peer1.AddPeer() | ||||
| 	go peer1.AddBlocks(8, 9) | ||||
| 	go peer1.AddBlockHashes(9, 8, 7) | ||||
| 	peer1.AddBlocks(3, 7, 8, 9) // make sure this section is complete
 | ||||
| 	peer1.AddBlocks(3, 7, 8) // make sure this section is complete
 | ||||
| 	time.Sleep(1 * time.Second) | ||||
| 	go peer1.AddBlockHashes(7, 3, 2)       // block 3/7 is section boundary
 | ||||
| 	peer1.AddBlocks(2, 3)                  // partially complete sections
 | ||||
| 	peer1.AddBlocks(2, 3)                  // partially complete sections block 2 missing
 | ||||
| 	peer2.AddPeer()                        //
 | ||||
| 	go peer2.AddBlocks(5, 6)               //
 | ||||
| 	go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
 | ||||
| 	peer2.AddBlocks(2, 3, 4, 5, 6)         // block 2 still missing.
 | ||||
| 	peer2.AddBlocks(2, 3, 4, 5)            // block 2 still missing.
 | ||||
| 	blockPool.RemovePeer("peer2")          // peer2 disconnects, peer1 is promoted again as best peer
 | ||||
| 	peer1.AddBlockHashes(7, 3)             // tests that hash request from fork root is remembered even though section process completed
 | ||||
| 	go peer1.AddBlockHashes(2, 1, 0)       //
 | ||||
| 	// peer1.AddBlockHashes(7, 3)             // tests that hash request from fork root is remembered even though section process completed
 | ||||
| 	go peer1.AddBlockHashes(2, 1, 0) //
 | ||||
| 	peer1.AddBlocks(0, 1, 2) | ||||
| 
 | ||||
| 	blockPool.Wait(waitTimeout * time.Second) | ||||
|  | ||||
| @ -16,6 +16,7 @@ const ( | ||||
| 	ErrInvalidBlock | ||||
| 	ErrInvalidPoW | ||||
| 	ErrUnrequestedBlock | ||||
| 	ErrInsufficientChainInfo | ||||
| ) | ||||
| 
 | ||||
| var errorToString = map[int]string{ | ||||
| @ -30,6 +31,7 @@ var errorToString = map[int]string{ | ||||
| 	ErrInvalidBlock:            "Invalid block", | ||||
| 	ErrInvalidPoW:              "Invalid PoW", | ||||
| 	ErrUnrequestedBlock:        "Unrequested block", | ||||
| 	ErrInsufficientChainInfo:   "Insufficient chain info", | ||||
| } | ||||
| 
 | ||||
| type protocolError struct { | ||||
|  | ||||
| @ -67,6 +67,8 @@ type newBlockMsgData struct { | ||||
| 	TD    *big.Int | ||||
| } | ||||
| 
 | ||||
| const maxHashes = 255 | ||||
| 
 | ||||
| type getBlockHashesMsgData struct { | ||||
| 	Hash   []byte | ||||
| 	Amount uint64 | ||||
| @ -122,7 +124,7 @@ func (self *ethProtocol) handle() error { | ||||
| 	defer msg.Discard() | ||||
| 
 | ||||
| 	switch msg.Code { | ||||
| 
 | ||||
| 	case GetTxMsg: // ignore
 | ||||
| 	case StatusMsg: | ||||
| 		return self.protoError(ErrExtraStatusMsg, "") | ||||
| 
 | ||||
| @ -139,8 +141,13 @@ func (self *ethProtocol) handle() error { | ||||
| 		if err := msg.Decode(&request); err != nil { | ||||
| 			return self.protoError(ErrDecode, "->msg %v: %v", msg, err) | ||||
| 		} | ||||
| 
 | ||||
| 		//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
 | ||||
| 		if request.Amount > maxHashes { | ||||
| 			request.Amount = maxHashes | ||||
| 		} | ||||
| 		hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount) | ||||
| 		return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...) | ||||
| 		return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...) | ||||
| 
 | ||||
| 	case BlockHashesMsg: | ||||
| 		// TODO: redo using lazy decode , this way very inefficient on known chains
 | ||||
| @ -185,7 +192,7 @@ func (self *ethProtocol) handle() error { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		return self.rw.EncodeMsg(BlocksMsg, blocks...) | ||||
| 		return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...) | ||||
| 
 | ||||
| 	case BlocksMsg: | ||||
| 		msgStream := rlp.NewStream(msg.Payload) | ||||
| @ -211,16 +218,6 @@ func (self *ethProtocol) handle() error { | ||||
| 		// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
 | ||||
| 		// (or selected as new best peer)
 | ||||
| 		if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) { | ||||
| 			called := true | ||||
| 			iter := func() ([]byte, bool) { | ||||
| 				if called { | ||||
| 					called = false | ||||
| 					return hash, true | ||||
| 				} else { | ||||
| 					return nil, false | ||||
| 				} | ||||
| 			} | ||||
| 			self.blockPool.AddBlockHashes(iter, self.id) | ||||
| 			self.blockPool.AddBlock(request.Block, self.id) | ||||
| 		} | ||||
| 
 | ||||
| @ -298,12 +295,12 @@ func (self *ethProtocol) handleStatus() error { | ||||
| 
 | ||||
| func (self *ethProtocol) requestBlockHashes(from []byte) error { | ||||
| 	self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4]) | ||||
| 	return self.rw.EncodeMsg(GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize)) | ||||
| 	return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize)) | ||||
| } | ||||
| 
 | ||||
| func (self *ethProtocol) requestBlocks(hashes [][]byte) error { | ||||
| 	self.peer.Debugf("fetching %v blocks", len(hashes)) | ||||
| 	return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...) | ||||
| 	return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...) | ||||
| } | ||||
| 
 | ||||
| func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) { | ||||
|  | ||||
| @ -41,10 +41,6 @@ func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error { | ||||
| 	return self.WriteMsg(p2p.NewMsg(code, data...)) | ||||
| } | ||||
| 
 | ||||
| func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) { | ||||
| 	msg, ok := <-self.in | ||||
| 	if !ok { | ||||
|  | ||||
| @ -12,8 +12,8 @@ EOF | ||||
| 
 | ||||
| peer 11 01 | ||||
| peer 12 02 | ||||
| P13ID=$PID | ||||
| P12ID=$PID | ||||
| test_node $NAME "" -loglevel 5 $JSFILE | ||||
| sleep 0.5 | ||||
| kill $P13ID | ||||
| sleep 0.3 | ||||
| kill $P12ID | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| TIMEOUT=35 | ||||
| TIMEOUT=12 | ||||
| 
 | ||||
| cat >> $JSFILE <<EOF | ||||
| eth.addPeer("localhost:30311"); | ||||
| sleep(30000); | ||||
| sleep(10000); | ||||
| eth.export("$CHAIN_TEST"); | ||||
| EOF | ||||
| 
 | ||||
|  | ||||
| @ -3,14 +3,14 @@ | ||||
| # launched by run.sh | ||||
| function test_node { | ||||
|   rm -rf $DIR/$1 | ||||
|   ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1" | ||||
|   ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1 -port 303$1" | ||||
|   if [ "" != "$2" ]; then | ||||
|     chain="chains/$2.chain" | ||||
|     echo "import chain $chain" | ||||
|     $ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import | ||||
|   fi | ||||
|   echo "starting test node $1 with extra args ${@:3}" | ||||
|   $ETH $ARGS -port 303$1 ${@:3} & | ||||
|   echo "starting test node $1 with args $ARGS ${@:3}" | ||||
|   $ETH $ARGS  ${@:3} & | ||||
|   PID=$! | ||||
|   PIDS="$PIDS $PID" | ||||
| } | ||||
|  | ||||
| @ -84,4 +84,5 @@ var ( | ||||
| 	BigFalse = Big0 | ||||
| 	Big32    = big.NewInt(32) | ||||
| 	Big256   = big.NewInt(0xff) | ||||
| 	Big257   = big.NewInt(257) | ||||
| ) | ||||
|  | ||||
| @ -10,8 +10,6 @@ import ( | ||||
| 
 | ||||
| // Config struct
 | ||||
| type ConfigManager struct { | ||||
| 	Db Database | ||||
| 
 | ||||
| 	ExecPath string | ||||
| 	Debug    bool | ||||
| 	Diff     bool | ||||
|  | ||||
| @ -12,7 +12,7 @@ func ExpandHomePath(p string) (path string) { | ||||
| 	path = p | ||||
| 
 | ||||
| 	// Check in case of paths like "/something/~/something/"
 | ||||
| 	if path[:2] == "~/" { | ||||
| 	if len(path) > 1 && path[:2] == "~/" { | ||||
| 		usr, _ := user.Current() | ||||
| 		dir := usr.HomeDir | ||||
| 
 | ||||
|  | ||||
| @ -137,7 +137,7 @@ func Encode(object interface{}) []byte { | ||||
| 		case byte: | ||||
| 			buff.Write(Encode(big.NewInt(int64(t)))) | ||||
| 		case *big.Int: | ||||
| 			// Not sure how this is possible while we check for
 | ||||
| 			// Not sure how this is possible while we check for nil
 | ||||
| 			if t == nil { | ||||
| 				buff.WriteByte(0xc0) | ||||
| 			} else { | ||||
|  | ||||
| @ -68,3 +68,11 @@ out: | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (self *Filters) Match(a, b Filter) bool { | ||||
| 	return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b) | ||||
| } | ||||
| 
 | ||||
| func (self *Filters) Get(i int) Filter { | ||||
| 	return self.watchers[i] | ||||
| } | ||||
|  | ||||
| @ -129,10 +129,9 @@ func (self *JSRE) initStdFuncs() { | ||||
|  */ | ||||
| 
 | ||||
| func (self *JSRE) dump(call otto.FunctionCall) otto.Value { | ||||
| 	var state *state.StateDB | ||||
| 	var block *types.Block | ||||
| 
 | ||||
| 	if len(call.ArgumentList) > 0 { | ||||
| 		var block *types.Block | ||||
| 		if call.Argument(0).IsNumber() { | ||||
| 			num, _ := call.Argument(0).ToInteger() | ||||
| 			block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num)) | ||||
| @ -149,12 +148,12 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value { | ||||
| 			return otto.UndefinedValue() | ||||
| 		} | ||||
| 
 | ||||
| 		state = block.State() | ||||
| 	} else { | ||||
| 		state = self.ethereum.ChainManager().State() | ||||
| 		block = self.ethereum.ChainManager().CurrentBlock() | ||||
| 	} | ||||
| 
 | ||||
| 	v, _ := self.Vm.ToValue(state.Dump()) | ||||
| 	statedb := state.New(block.Root(), self.ethereum.Db()) | ||||
| 	v, _ := self.Vm.ToValue(statedb.Dump()) | ||||
| 
 | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| @ -31,6 +31,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/ethutil" | ||||
| 	"github.com/ethereum/go-ethereum/pow" | ||||
| 	"github.com/ethereum/go-ethereum/pow/ezp" | ||||
| 	"github.com/ethereum/go-ethereum/state" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/types" | ||||
| @ -69,6 +70,7 @@ type Miner struct { | ||||
| 	mining bool | ||||
| 
 | ||||
| 	MinAcceptedGasPrice *big.Int | ||||
| 	Extra               string | ||||
| } | ||||
| 
 | ||||
| func New(coinbase []byte, eth *eth.Ethereum) *Miner { | ||||
| @ -177,7 +179,9 @@ func (self *Miner) mine() { | ||||
| 		blockProcessor = self.eth.BlockProcessor() | ||||
| 		chainMan       = self.eth.ChainManager() | ||||
| 		block          = chainMan.NewBlock(self.Coinbase) | ||||
| 		state          = state.New(block.Root(), self.eth.Db()) | ||||
| 	) | ||||
| 	block.Header().Extra = self.Extra | ||||
| 
 | ||||
| 	// Apply uncles
 | ||||
| 	if len(self.uncles) > 0 { | ||||
| @ -185,13 +189,11 @@ func (self *Miner) mine() { | ||||
| 	} | ||||
| 
 | ||||
| 	parent := chainMan.GetBlock(block.ParentHash()) | ||||
| 	coinbase := block.State().GetOrNewStateObject(block.Coinbase()) | ||||
| 	coinbase := state.GetOrNewStateObject(block.Coinbase()) | ||||
| 	coinbase.SetGasPool(core.CalcGasLimit(parent, block)) | ||||
| 
 | ||||
| 	transactions := self.finiliseTxs() | ||||
| 
 | ||||
| 	state := block.State() | ||||
| 
 | ||||
| 	// Accumulate all valid transactions and apply them to the new state
 | ||||
| 	// Error may be ignored. It's not important during mining
 | ||||
| 	receipts, txs, _, erroneous, err := blockProcessor.ApplyTransactions(coinbase, state, block, transactions, true) | ||||
|  | ||||
| @ -71,14 +71,11 @@ type MsgReader interface { | ||||
| } | ||||
| 
 | ||||
| type MsgWriter interface { | ||||
| 	// WriteMsg sends an existing message.
 | ||||
| 	// The Payload reader of the message is consumed.
 | ||||
| 	// WriteMsg sends a message. It will block until the message's
 | ||||
| 	// Payload has been consumed by the other end.
 | ||||
| 	//
 | ||||
| 	// Note that messages can be sent only once.
 | ||||
| 	WriteMsg(Msg) error | ||||
| 
 | ||||
| 	// EncodeMsg writes an RLP-encoded message with the given
 | ||||
| 	// code and data elements.
 | ||||
| 	EncodeMsg(code uint64, data ...interface{}) error | ||||
| } | ||||
| 
 | ||||
| // MsgReadWriter provides reading and writing of encoded messages.
 | ||||
| @ -87,6 +84,12 @@ type MsgReadWriter interface { | ||||
| 	MsgWriter | ||||
| } | ||||
| 
 | ||||
| // EncodeMsg writes an RLP-encoded message with the given code and
 | ||||
| // data elements.
 | ||||
| func EncodeMsg(w MsgWriter, code uint64, data ...interface{}) error { | ||||
| 	return w.WriteMsg(NewMsg(code, data...)) | ||||
| } | ||||
| 
 | ||||
| var magicToken = []byte{34, 64, 8, 145} | ||||
| 
 | ||||
| func writeMsg(w io.Writer, msg Msg) error { | ||||
| @ -209,11 +212,6 @@ func (p *MsgPipeRW) WriteMsg(msg Msg) error { | ||||
| 	return ErrPipeClosed | ||||
| } | ||||
| 
 | ||||
| // EncodeMsg is a convenient shorthand for sending an RLP-encoded message.
 | ||||
| func (p *MsgPipeRW) EncodeMsg(code uint64, data ...interface{}) error { | ||||
| 	return p.WriteMsg(NewMsg(code, data...)) | ||||
| } | ||||
| 
 | ||||
| // ReadMsg returns a message sent on the other end of the pipe.
 | ||||
| func (p *MsgPipeRW) ReadMsg() (Msg, error) { | ||||
| 	if atomic.LoadInt32(p.closed) == 0 { | ||||
|  | ||||
| @ -75,8 +75,8 @@ func TestDecodeRealMsg(t *testing.T) { | ||||
| func ExampleMsgPipe() { | ||||
| 	rw1, rw2 := MsgPipe() | ||||
| 	go func() { | ||||
| 		rw1.EncodeMsg(8, []byte{0, 0}) | ||||
| 		rw1.EncodeMsg(5, []byte{1, 1}) | ||||
| 		EncodeMsg(rw1, 8, []byte{0, 0}) | ||||
| 		EncodeMsg(rw1, 5, []byte{1, 1}) | ||||
| 		rw1.Close() | ||||
| 	}() | ||||
| 
 | ||||
| @ -100,7 +100,7 @@ loop: | ||||
| 		rw1, rw2 := MsgPipe() | ||||
| 		done := make(chan struct{}) | ||||
| 		go func() { | ||||
| 			if err := rw1.EncodeMsg(1); err == nil { | ||||
| 			if err := EncodeMsg(rw1, 1); err == nil { | ||||
| 				t.Error("EncodeMsg returned nil error") | ||||
| 			} else if err != ErrPipeClosed { | ||||
| 				t.Error("EncodeMsg returned wrong error: got %v, want %v", err, ErrPipeClosed) | ||||
|  | ||||
							
								
								
									
										22
									
								
								p2p/peer.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								p2p/peer.go
									
									
									
									
									
								
							| @ -460,25 +460,3 @@ func (r *eofSignal) Read(buf []byte) (int, error) { | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| func (peer *Peer) PeerList() []interface{} { | ||||
| 	peers := peer.otherPeers() | ||||
| 	ds := make([]interface{}, 0, len(peers)) | ||||
| 	for _, p := range peers { | ||||
| 		p.infolock.Lock() | ||||
| 		addr := p.listenAddr | ||||
| 		p.infolock.Unlock() | ||||
| 		// filter out this peer and peers that are not listening or
 | ||||
| 		// have not completed the handshake.
 | ||||
| 		// TODO: track previously sent peers and exclude them as well.
 | ||||
| 		if p == peer || addr == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		ds = append(ds, addr) | ||||
| 	} | ||||
| 	ourAddr := peer.ourListenAddr | ||||
| 	if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() { | ||||
| 		ds = append(ds, ourAddr) | ||||
| 	} | ||||
| 	return ds | ||||
| } | ||||
|  | ||||
| @ -126,10 +126,10 @@ func TestPeerProtoEncodeMsg(t *testing.T) { | ||||
| 		Name:   "a", | ||||
| 		Length: 2, | ||||
| 		Run: func(peer *Peer, rw MsgReadWriter) error { | ||||
| 			if err := rw.EncodeMsg(2); err == nil { | ||||
| 			if err := EncodeMsg(rw, 2); err == nil { | ||||
| 				t.Error("expected error for out-of-range msg code, got nil") | ||||
| 			} | ||||
| 			if err := rw.EncodeMsg(1, "foo", "bar"); err != nil { | ||||
| 			if err := EncodeMsg(rw, 1, "foo", "bar"); err != nil { | ||||
| 				t.Errorf("write error: %v", err) | ||||
| 			} | ||||
| 			return nil | ||||
|  | ||||
| @ -119,14 +119,14 @@ func (bp *baseProtocol) loop(quit <-chan error) error { | ||||
| 
 | ||||
| 	getPeersTick := time.NewTicker(10 * time.Second) | ||||
| 	defer getPeersTick.Stop() | ||||
| 	err := bp.rw.EncodeMsg(getPeersMsg) | ||||
| 	err := EncodeMsg(bp.rw, getPeersMsg) | ||||
| 
 | ||||
| 	for err == nil { | ||||
| 		select { | ||||
| 		case err = <-quit: | ||||
| 			return err | ||||
| 		case <-getPeersTick.C: | ||||
| 			err = bp.rw.EncodeMsg(getPeersMsg) | ||||
| 			err = EncodeMsg(bp.rw, getPeersMsg) | ||||
| 		case event := <-activity.Chan(): | ||||
| 			ping.Reset(pingTimeout) | ||||
| 			lastActive = event.(time.Time) | ||||
| @ -134,7 +134,7 @@ func (bp *baseProtocol) loop(quit <-chan error) error { | ||||
| 			if lastActive.Add(pingTimeout * 2).Before(t) { | ||||
| 				err = newPeerError(errPingTimeout, "") | ||||
| 			} else if lastActive.Add(pingTimeout).Before(t) { | ||||
| 				err = bp.rw.EncodeMsg(pingMsg) | ||||
| 				err = EncodeMsg(bp.rw, pingMsg) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -164,12 +164,12 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error { | ||||
| 		return discRequestedError(reason[0]) | ||||
| 
 | ||||
| 	case pingMsg: | ||||
| 		return bp.rw.EncodeMsg(pongMsg) | ||||
| 		return EncodeMsg(bp.rw, pongMsg) | ||||
| 
 | ||||
| 	case pongMsg: | ||||
| 
 | ||||
| 	case getPeersMsg: | ||||
| 		peers := bp.peer.PeerList() | ||||
| 		peers := bp.peerList() | ||||
| 		// this is dangerous. the spec says that we should _delay_
 | ||||
| 		// sending the response if no new information is available.
 | ||||
| 		// this means that would need to send a response later when
 | ||||
| @ -177,7 +177,7 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error { | ||||
| 		//
 | ||||
| 		// TODO: add event mechanism to notify baseProtocol for new peers
 | ||||
| 		if len(peers) > 0 { | ||||
| 			return bp.rw.EncodeMsg(peersMsg, peers...) | ||||
| 			return EncodeMsg(bp.rw, peersMsg, peers...) | ||||
| 		} | ||||
| 
 | ||||
| 	case peersMsg: | ||||
| @ -264,3 +264,25 @@ func (bp *baseProtocol) handshakeMsg() Msg { | ||||
| 		bp.peer.ourID.Pubkey()[1:], | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (bp *baseProtocol) peerList() []interface{} { | ||||
| 	peers := bp.peer.otherPeers() | ||||
| 	ds := make([]interface{}, 0, len(peers)) | ||||
| 	for _, p := range peers { | ||||
| 		p.infolock.Lock() | ||||
| 		addr := p.listenAddr | ||||
| 		p.infolock.Unlock() | ||||
| 		// filter out this peer and peers that are not listening or
 | ||||
| 		// have not completed the handshake.
 | ||||
| 		// TODO: track previously sent peers and exclude them as well.
 | ||||
| 		if p == bp.peer || addr == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		ds = append(ds, addr) | ||||
| 	} | ||||
| 	ourAddr := bp.peer.ourListenAddr | ||||
| 	if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() { | ||||
| 		ds = append(ds, ourAddr) | ||||
| 	} | ||||
| 	return ds | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| @ -36,50 +37,71 @@ func newTestPeer() (peer *Peer) { | ||||
| } | ||||
| 
 | ||||
| func TestBaseProtocolPeers(t *testing.T) { | ||||
| 	cannedPeerList := []*peerAddr{ | ||||
| 	peerList := []*peerAddr{ | ||||
| 		{IP: net.ParseIP("1.2.3.4"), Port: 2222, Pubkey: []byte{}}, | ||||
| 		{IP: net.ParseIP("5.6.7.8"), Port: 3333, Pubkey: []byte{}}, | ||||
| 	} | ||||
| 	var ownAddr *peerAddr = &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}} | ||||
| 	listenAddr := &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}} | ||||
| 	rw1, rw2 := MsgPipe() | ||||
| 	defer rw1.Close() | ||||
| 	wg := new(sync.WaitGroup) | ||||
| 
 | ||||
| 	// run matcher, close pipe when addresses have arrived
 | ||||
| 	addrChan := make(chan *peerAddr, len(cannedPeerList)) | ||||
| 	numPeers := len(peerList) + 1 | ||||
| 	addrChan := make(chan *peerAddr) | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		for _, want := range cannedPeerList { | ||||
| 			got := <-addrChan | ||||
| 			t.Logf("got peer: %+v", got) | ||||
| 		i := 0 | ||||
| 		for got := range addrChan { | ||||
| 			var want *peerAddr | ||||
| 			switch { | ||||
| 			case i < len(peerList): | ||||
| 				want = peerList[i] | ||||
| 			case i == len(peerList): | ||||
| 				want = listenAddr // listenAddr should be the last thing sent
 | ||||
| 			} | ||||
| 			t.Logf("got peer %d/%d: %v", i+1, numPeers, got) | ||||
| 			if !reflect.DeepEqual(want, got) { | ||||
| 				t.Errorf("mismatch:  got %#v, want %#v", got, want) | ||||
| 				t.Errorf("mismatch: got %+v, want %+v", got, want) | ||||
| 			} | ||||
| 			i++ | ||||
| 			if i == numPeers { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		close(addrChan) | ||||
| 		var own []*peerAddr | ||||
| 		var got *peerAddr | ||||
| 		for got = range addrChan { | ||||
| 			own = append(own, got) | ||||
| 		if i != numPeers { | ||||
| 			t.Errorf("wrong number of peers received: got %d, want %d", i, numPeers) | ||||
| 		} | ||||
| 		if len(own) != 1 || !reflect.DeepEqual(ownAddr, own[0]) { | ||||
| 			t.Errorf("mismatch: peers own address is incorrectly or not given, got %v, want %#v", ownAddr) | ||||
| 		} | ||||
| 		rw2.Close() | ||||
| 		rw1.Close() | ||||
| 		wg.Done() | ||||
| 	}() | ||||
| 	// run first peer
 | ||||
| 
 | ||||
| 	// run first peer (in background)
 | ||||
| 	peer1 := newTestPeer() | ||||
| 	peer1.ourListenAddr = ownAddr | ||||
| 	peer1.ourListenAddr = listenAddr | ||||
| 	peer1.otherPeers = func() []*Peer { | ||||
| 		pl := make([]*Peer, len(cannedPeerList)) | ||||
| 		for i, addr := range cannedPeerList { | ||||
| 		pl := make([]*Peer, len(peerList)) | ||||
| 		for i, addr := range peerList { | ||||
| 			pl[i] = &Peer{listenAddr: addr} | ||||
| 		} | ||||
| 		return pl | ||||
| 	} | ||||
| 	go runBaseProtocol(peer1, rw1) | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		runBaseProtocol(peer1, rw1) | ||||
| 		wg.Done() | ||||
| 	}() | ||||
| 
 | ||||
| 	// run second peer
 | ||||
| 	peer2 := newTestPeer() | ||||
| 	peer2.newPeerAddr = addrChan // feed peer suggestions into matcher
 | ||||
| 	if err := runBaseProtocol(peer2, rw2); err != ErrPipeClosed { | ||||
| 		t.Errorf("peer2 terminated with unexpected error: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// terminate matcher
 | ||||
| 	close(addrChan) | ||||
| 	wg.Wait() | ||||
| } | ||||
| 
 | ||||
| func TestBaseProtocolDisconnect(t *testing.T) { | ||||
| @ -93,7 +115,7 @@ func TestBaseProtocolDisconnect(t *testing.T) { | ||||
| 		if err := expectMsg(rw2, handshakeMsg); err != nil { | ||||
| 			t.Error(err) | ||||
| 		} | ||||
| 		err := rw2.EncodeMsg(handshakeMsg, | ||||
| 		err := EncodeMsg(rw2, handshakeMsg, | ||||
| 			baseProtocolVersion, | ||||
| 			"", | ||||
| 			[]interface{}{}, | ||||
| @ -106,7 +128,7 @@ func TestBaseProtocolDisconnect(t *testing.T) { | ||||
| 		if err := expectMsg(rw2, getPeersMsg); err != nil { | ||||
| 			t.Error(err) | ||||
| 		} | ||||
| 		if err := rw2.EncodeMsg(discMsg, DiscQuitting); err != nil { | ||||
| 		if err := EncodeMsg(rw2, discMsg, DiscQuitting); err != nil { | ||||
| 			t.Error(err) | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
| @ -333,7 +333,7 @@ func (srv *Server) dialLoop() { | ||||
| 		case desc := <-suggest: | ||||
| 			// candidate peer found, will dial out asyncronously
 | ||||
| 			// if connection fails slot will be released
 | ||||
| 			srvlog.Infof("dial %v (%v)", desc, *slot) | ||||
| 			srvlog.DebugDetailf("dial %v (%v)", desc, *slot) | ||||
| 			go srv.dialPeer(desc, *slot) | ||||
| 			// we can watch if more peers needed in the next loop
 | ||||
| 			slots = srv.peerSlots | ||||
| @ -355,7 +355,7 @@ func (srv *Server) dialPeer(desc *peerAddr, slot int) { | ||||
| 	srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot) | ||||
| 	conn, err := srv.Dialer.Dial(desc.Network(), desc.String()) | ||||
| 	if err != nil { | ||||
| 		srvlog.Errorf("Dial error: %v", err) | ||||
| 		srvlog.DebugDetailf("dial error: %v", err) | ||||
| 		srv.peerSlots <- slot | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -1,12 +0,0 @@ | ||||
| package ar | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/trie" | ||||
| ) | ||||
| 
 | ||||
| type Block interface { | ||||
| 	Trie() *trie.Trie | ||||
| 	Diff() *big.Int | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| package ar | ||||
| 
 | ||||
| import "math/big" | ||||
| 
 | ||||
| const lenops int64 = 9 | ||||
| 
 | ||||
| type OpsFunc func(a, b *big.Int) *big.Int | ||||
| 
 | ||||
| var ops [lenops]OpsFunc | ||||
| 
 | ||||
| func init() { | ||||
| 	ops[0] = Add | ||||
| 	ops[1] = Mul | ||||
| 	ops[2] = Mod | ||||
| 	ops[3] = Xor | ||||
| 	ops[4] = And | ||||
| 	ops[5] = Or | ||||
| 	ops[6] = Sub1 | ||||
| 	ops[7] = XorSub | ||||
| 	ops[8] = Rsh | ||||
| } | ||||
| 
 | ||||
| func Add(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Add(x, y) | ||||
| } | ||||
| func Mul(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Mul(x, y) | ||||
| } | ||||
| func Mod(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Mod(x, y) | ||||
| } | ||||
| func Xor(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Xor(x, y) | ||||
| } | ||||
| func And(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).And(x, y) | ||||
| } | ||||
| func Or(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Or(x, y) | ||||
| } | ||||
| func Sub1(x, y *big.Int) *big.Int { | ||||
| 	a := big.NewInt(-1) | ||||
| 	a.Sub(a, x) | ||||
| 
 | ||||
| 	return a | ||||
| } | ||||
| func XorSub(x, y *big.Int) *big.Int { | ||||
| 	t := Sub1(x, nil) | ||||
| 
 | ||||
| 	return t.Xor(t, y) | ||||
| } | ||||
| func Rsh(x, y *big.Int) *big.Int { | ||||
| 	return new(big.Int).Rsh(x, uint(y.Uint64()%64)) | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user