mirror of
https://github.com/status-im/op-geth.git
synced 2025-02-27 05:50:41 +00:00
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
|
||||||
*/**/.DS_Store
|
*/**/.DS_Store
|
||||||
.ethtest
|
.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 file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -61,6 +64,7 @@ var (
|
|||||||
ImportChain string
|
ImportChain string
|
||||||
SHH bool
|
SHH bool
|
||||||
Dial bool
|
Dial bool
|
||||||
|
PrintVersion bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// flags specific to cli client
|
// flags specific to cli client
|
||||||
@ -117,6 +121,7 @@ func Init() {
|
|||||||
|
|
||||||
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
|
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
|
||||||
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
|
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
|
||||||
|
flag.BoolVar(&PrintVersion, "version", false, "prints version number")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -28,6 +31,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -49,6 +53,11 @@ func main() {
|
|||||||
// precedence: code-internal flag default < config file < environment variables < command line
|
// precedence: code-internal flag default < config file < environment variables < command line
|
||||||
Init() // parsing command line
|
Init() // parsing command line
|
||||||
|
|
||||||
|
if PrintVersion {
|
||||||
|
printVersion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
||||||
|
|
||||||
ethereum, err := eth.New(ð.Config{
|
ethereum, err := eth.New(ð.Config{
|
||||||
@ -95,7 +104,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Leave the Println. This needs clean output for piping
|
// 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)
|
fmt.Println(block)
|
||||||
|
|
||||||
@ -134,3 +144,13 @@ func main() {
|
|||||||
// this blocks the thread
|
// this blocks the thread
|
||||||
ethereum.WaitForShutdown()
|
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) {
|
func (self *JSRepl) parseInput(code string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println("[native] error", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
value, err := self.re.Run(code)
|
value, err := self.re.Run(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
/**
|
/**
|
||||||
* @authors:
|
* @authors:
|
||||||
* Jeffrey Wilcke <i@jev.io>
|
* Jeffrey Wilcke <i@jev.io>
|
||||||
* @date 2014
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@ -26,12 +24,15 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/tests/helper"
|
"github.com/ethereum/go-ethereum/tests/helper"
|
||||||
)
|
)
|
||||||
@ -43,8 +44,8 @@ type Account struct {
|
|||||||
Storage map[string]string
|
Storage map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func StateObjectFromAccount(addr string, account Account) *state.StateObject {
|
func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject {
|
||||||
obj := state.NewStateObject(ethutil.Hex2Bytes(addr))
|
obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db)
|
||||||
obj.SetBalance(ethutil.Big(account.Balance))
|
obj.SetBalance(ethutil.Big(account.Balance))
|
||||||
|
|
||||||
if ethutil.IsHex(account.Code) {
|
if ethutil.IsHex(account.Code) {
|
||||||
@ -66,19 +67,20 @@ type VmTest struct {
|
|||||||
Pre map[string]Account
|
Pre map[string]Account
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunVmTest(js string) (failed int) {
|
func RunVmTest(r io.Reader) (failed int) {
|
||||||
tests := make(map[string]VmTest)
|
tests := make(map[string]VmTest)
|
||||||
|
|
||||||
data, _ := ioutil.ReadAll(strings.NewReader(js))
|
data, _ := ioutil.ReadAll(r)
|
||||||
err := json.Unmarshal(data, &tests)
|
err := json.Unmarshal(data, &tests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
state := state.New(helper.NewTrie())
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state := state.New(nil, db)
|
||||||
for addr, account := range test.Pre {
|
for addr, account := range test.Pre {
|
||||||
obj := StateObjectFromAccount(addr, account)
|
obj := StateObjectFromAccount(db, addr, account)
|
||||||
state.SetStateObject(obj)
|
state.SetStateObject(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +120,8 @@ func RunVmTest(js string) (failed int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -125,9 +129,10 @@ func RunVmTest(js string) (failed int) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
helper.Logger.SetLogLevel(5)
|
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
|
* @authors
|
||||||
* Jeffrey Wilcke <i@jev.io>
|
* Jeffrey Wilcke <i@jev.io>
|
||||||
* @date 2014
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@ -37,7 +35,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/ptrie"
|
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
@ -65,7 +62,7 @@ func main() {
|
|||||||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := state.New(ptrie.New(nil, db))
|
statedb := state.New(nil, db)
|
||||||
sender := statedb.NewStateObject([]byte("sender"))
|
sender := statedb.NewStateObject([]byte("sender"))
|
||||||
receiver := statedb.NewStateObject([]byte("receiver"))
|
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||||
//receiver.SetCode([]byte(*code))
|
//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) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||||
func (self *VMEnv) Depth() int { return 0 }
|
func (self *VMEnv) Depth() int { return 0 }
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
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) {
|
func (self *VMEnv) AddLog(log state.Log) {
|
||||||
self.state.AddLog(log)
|
self.state.AddLog(log)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ ApplicationWindow {
|
|||||||
property alias dataText: rawDataField.text
|
property alias dataText: rawDataField.text
|
||||||
|
|
||||||
onClosing: {
|
onClosing: {
|
||||||
dbg.Stop()
|
//dbg.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
menuBar: MenuBar {
|
menuBar: MenuBar {
|
||||||
@ -353,6 +353,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
|
visible: false
|
||||||
id: snippets
|
id: snippets
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
model: ListModel {
|
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:
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
*.swp
|
||||||
/tmp
|
/tmp
|
||||||
*/**/*un~
|
*/**/*un~
|
||||||
*un~
|
*un~
|
||||||
@ -11,4 +12,7 @@
|
|||||||
*/**/.DS_Store
|
*/**/.DS_Store
|
||||||
ethereum/ethereum
|
ethereum/ethereum
|
||||||
ethereal/ethereal
|
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 3.0
|
||||||
import QtWebKit.experimental 1.0
|
import QtWebKit.experimental 1.0
|
||||||
import QtQuick.Controls 1.0;
|
import QtQuick.Controls 1.0;
|
||||||
@ -8,437 +8,484 @@ import QtQuick.Window 2.1;
|
|||||||
import Ethereum 1.0
|
import Ethereum 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: window
|
id: window
|
||||||
property var title: "Browser"
|
objectName: "browserView"
|
||||||
property var iconSource: "../browser.png"
|
anchors.fill: parent
|
||||||
property var menuItem
|
color: "#00000000"
|
||||||
|
|
||||||
property alias url: webview.url
|
property var title: "Browser"
|
||||||
property alias webView: webview
|
property var iconSource: "../browser.png"
|
||||||
|
property var menuItem
|
||||||
|
|
||||||
property var cleanPath: false
|
property alias url: webview.url
|
||||||
property var open: function(url) {
|
property alias webView: webview
|
||||||
if(!window.cleanPath) {
|
|
||||||
var uri = url;
|
|
||||||
if(!/.*\:\/\/.*/.test(uri)) {
|
|
||||||
uri = "http://" + uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
|
||||||
uri.replace(reg, function(match, pre, domain, path) {
|
|
||||||
uri = pre;
|
|
||||||
|
|
||||||
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
if(reg.test(uri)) {
|
||||||
var ip = [];
|
uri.replace(reg, function(match, pre, domain, path) {
|
||||||
for(var i = 0, l = lookup.length; i < l; i++) {
|
uri = pre;
|
||||||
ip.push(lookup.charCodeAt(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ip.length != 0) {
|
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
||||||
uri += lookup;
|
var ip = [];
|
||||||
} else {
|
for(var i = 0, l = lookup.length; i < l; i++) {
|
||||||
uri += domain;
|
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>");
|
webview.url = uri;
|
||||||
uriNav.text = uri;
|
|
||||||
} else {
|
|
||||||
// Prevent inf loop.
|
|
||||||
window.cleanPath = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
|
||||||
webview.url = "http://etherian.io"
|
uriNav.text = uri;
|
||||||
}
|
} else {
|
||||||
|
// Prevent inf loop.
|
||||||
|
window.cleanPath = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signal messages(var messages, int id);
|
Component.onCompleted: {
|
||||||
onMessages: {
|
webview.url = "http://etherian.io"
|
||||||
// Bit of a cheat to get proper JSON
|
}
|
||||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
|
||||||
webview.postEvent("messages", id, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShhMessage(message, id) {
|
signal messages(var messages, int id);
|
||||||
webview.postEvent("shhChanged", id, message)
|
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 {
|
function onShhMessage(message, id) {
|
||||||
objectName: "root"
|
webview.postEvent("shh_changed", id, message)
|
||||||
id: root
|
}
|
||||||
anchors.fill: parent
|
|
||||||
state: "inspectorShown"
|
|
||||||
|
|
||||||
RowLayout {
|
Item {
|
||||||
id: navBar
|
objectName: "root"
|
||||||
height: 40
|
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 {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
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
|
top: navBar.bottom
|
||||||
}
|
}
|
||||||
|
z: -1
|
||||||
|
height: 1
|
||||||
|
color: "#CCCCCC"
|
||||||
|
}
|
||||||
|
|
||||||
//property var cleanPath: false
|
WebView {
|
||||||
onNavigationRequested: {
|
objectName: "webView"
|
||||||
window.open(request.url.toString());
|
id: webview
|
||||||
}
|
anchors {
|
||||||
|
left: parent.left
|
||||||
function sendMessage(data) {
|
right: parent.right
|
||||||
webview.experimental.postMessage(JSON.stringify(data))
|
bottom: parent.bottom
|
||||||
}
|
top: divider.bottom
|
||||||
|
|
||||||
|
|
||||||
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]] || "";
|
|
||||||
}
|
}
|
||||||
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))
|
function injectJs(js) {
|
||||||
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
|
webview.experimental.navigatorQtObjectEnabled = true;
|
||||||
break;
|
webview.experimental.evaluateJavaScript(js)
|
||||||
}
|
webview.experimental.javascriptEnabled = true;
|
||||||
} catch(e) {
|
}
|
||||||
console.log(data.call + ": " + e)
|
|
||||||
|
|
||||||
postData(data._id, null);
|
function sendMessage(data) {
|
||||||
}
|
webview.experimental.postMessage(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function post(seed, data) {
|
experimental.preferences.javascriptEnabled: true
|
||||||
postData(data._id, data)
|
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) {
|
Menu {
|
||||||
if(args.length < num) {
|
visible: true
|
||||||
throw("required argument count of "+num+" got "+args.length);
|
id: itemSelector
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
Instantiator {
|
||||||
var messages = JSON.parse(data)
|
model: selectorModel.items
|
||||||
postEvent("watched:"+id, messages)
|
delegate: MenuItem {
|
||||||
}
|
text: model.text
|
||||||
|
onTriggered: {
|
||||||
|
selectorModel.accept(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onObjectAdded: itemSelector.insertItem(index, object)
|
||||||
|
onObjectRemoved: itemSelector.removeItem(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onNewBlockCb(block) {
|
Component.onCompleted: {
|
||||||
postEvent("block:new", block)
|
itemSelector.popup()
|
||||||
}
|
}
|
||||||
function onObjectChangeCb(stateObject) {
|
}
|
||||||
postEvent("object:"+stateObject.address(), stateObject)
|
experimental.preferences.webAudioEnabled: true
|
||||||
}
|
experimental.preferences.navigatorQtObjectEnabled: true
|
||||||
function onStorageChangeCb(storageObject) {
|
experimental.preferences.developerExtrasEnabled: true
|
||||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"]
|
||||||
postEvent(ev, [storageObject.address, storageObject.value])
|
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 {
|
case "shh_newFilter":
|
||||||
id: sizeGrip
|
require(1);
|
||||||
color: "gray"
|
var id = shh.watch(data.args[0], window);
|
||||||
visible: false
|
postData(data._id, id);
|
||||||
height: 10
|
break;
|
||||||
anchors {
|
|
||||||
left: root.left
|
|
||||||
right: root.right
|
|
||||||
}
|
|
||||||
y: Math.round(root.height * 2 / 3)
|
|
||||||
|
|
||||||
MouseArea {
|
case "shh_newIdentity":
|
||||||
anchors.fill: parent
|
var id = shh.newIdentity()
|
||||||
drag.target: sizeGrip
|
postData(data._id, id)
|
||||||
drag.minimumY: 0
|
|
||||||
drag.maximumY: root.height
|
|
||||||
drag.axis: Drag.YAxis
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WebView {
|
break
|
||||||
id: inspector
|
|
||||||
visible: false
|
|
||||||
anchors {
|
|
||||||
left: root.left
|
|
||||||
right: root.right
|
|
||||||
top: sizeGrip.bottom
|
|
||||||
bottom: root.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
states: [
|
case "shh_post":
|
||||||
State {
|
require(1);
|
||||||
name: "inspectorShown"
|
|
||||||
PropertyChanges {
|
var params = data.args[0];
|
||||||
target: inspector
|
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);
|
mainSplit.setView(wallet.view, wallet.menuItem);
|
||||||
|
|
||||||
// Call the ready handler
|
// Command setup
|
||||||
gui.done();
|
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) {
|
function addViews(view, path, options) {
|
||||||
@ -284,6 +295,7 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProgressBar {
|
ProgressBar {
|
||||||
|
visible: false
|
||||||
id: downloadIndicator
|
id: downloadIndicator
|
||||||
value: 0
|
value: 0
|
||||||
objectName: "downloadIndicator"
|
objectName: "downloadIndicator"
|
||||||
@ -293,6 +305,7 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
visible: false
|
||||||
objectName: "downloadLabel"
|
objectName: "downloadLabel"
|
||||||
//y: 7
|
//y: 7
|
||||||
anchors.left: downloadIndicator.right
|
anchors.left: downloadIndicator.right
|
||||||
@ -445,7 +458,7 @@ ApplicationWindow {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainSplit.setView(view, menuItem)
|
activeView(view, menuItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,14 +525,14 @@ ApplicationWindow {
|
|||||||
var section;
|
var section;
|
||||||
switch(options.section) {
|
switch(options.section) {
|
||||||
case "ethereum":
|
case "ethereum":
|
||||||
section = menuDefault;
|
section = menuDefault;
|
||||||
break;
|
break;
|
||||||
case "legacy":
|
case "legacy":
|
||||||
section = menuLegacy;
|
section = menuLegacy;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
section = menuApps;
|
section = menuApps;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var comp = menuItemTemplate.createObject(section)
|
var comp = menuItemTemplate.createObject(section)
|
||||||
@ -606,6 +619,7 @@ ApplicationWindow {
|
|||||||
* Main view
|
* Main view
|
||||||
********************/
|
********************/
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: rootView
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.left: menu.right
|
anchors.left: menu.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
@ -639,8 +653,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if(/^https?/.test(this.text)) {
|
if(/^https?/.test(this.text)) {
|
||||||
root.browser.view.open(this.text);
|
activeView(root.browser.view, root.browser.menuItem);
|
||||||
mainSplit.setView(root.browser.view, root.browser.menuItem);
|
|
||||||
} else {
|
} else {
|
||||||
addPlugin(this.text, {close: true, section: "apps"})
|
addPlugin(this.text, {close: true, section: "apps"})
|
||||||
}
|
}
|
||||||
@ -864,13 +877,13 @@ ApplicationWindow {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"})
|
pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"})
|
||||||
/*
|
/*
|
||||||
var ips = eth.pastPeers()
|
var ips = eth.pastPeers()
|
||||||
for(var i = 0; i < ips.length; i++) {
|
for(var i = 0; i < ips.length; i++) {
|
||||||
pastPeers.append({text: ips.get(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"
|
text: "Start"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
eth.setGasPrice(minGasPrice.text || "10000000000000");
|
eth.setGasPrice(minGasPrice.text || "10000000000000");
|
||||||
|
eth.setExtra(blockExtra.text)
|
||||||
if (eth.toggleMining()) {
|
if (eth.toggleMining()) {
|
||||||
this.text = "Stop";
|
this.text = "Stop";
|
||||||
} else {
|
} else {
|
||||||
@ -55,6 +56,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: minGasPriceRect
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 2
|
anchors.topMargin: 2
|
||||||
width: 200
|
width: 200
|
||||||
@ -65,6 +67,23 @@ Rectangle {
|
|||||||
validator: RegExpValidator { regExp: /\d*/ }
|
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 file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -26,6 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
type plugin struct {
|
type plugin struct {
|
||||||
@ -118,7 +122,7 @@ func (self *Gui) DumpState(hash, path string) {
|
|||||||
return
|
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)
|
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 file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -37,7 +40,7 @@ type DebuggerWindow struct {
|
|||||||
engine *qml.Engine
|
engine *qml.Engine
|
||||||
lib *UiLib
|
lib *UiLib
|
||||||
|
|
||||||
vm *vm.DebugVm
|
vm *vm.Vm
|
||||||
Db *Debugger
|
Db *Debugger
|
||||||
|
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
@ -54,7 +57,7 @@ func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
|||||||
|
|
||||||
win := component.CreateWindow(nil)
|
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)
|
w.Db = NewDebugger(w)
|
||||||
|
|
||||||
return w
|
return w
|
||||||
@ -264,6 +267,9 @@ type storeVal struct {
|
|||||||
Key, Value string
|
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 {
|
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)
|
self.main.Logln("break on instr:", pc)
|
||||||
|
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -89,7 +92,7 @@ func defaultAssetPath() string {
|
|||||||
}
|
}
|
||||||
func defaultDataDir() string {
|
func defaultDataDir() string {
|
||||||
usr, _ := user.Current()
|
usr, _ := user.Current()
|
||||||
return path.Join(usr.HomeDir, ".mist")
|
return path.Join(usr.HomeDir, ".ethereum")
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
|
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 file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import "C"
|
import "C"
|
||||||
@ -23,7 +26,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -45,15 +50,22 @@ import (
|
|||||||
|
|
||||||
var guilogger = logger.NewLogger("GUI")
|
var guilogger = logger.NewLogger("GUI")
|
||||||
|
|
||||||
|
type ServEv byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
setup ServEv = iota
|
||||||
|
update
|
||||||
|
)
|
||||||
|
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
// The main application window
|
// The main application window
|
||||||
win *qml.Window
|
win *qml.Window
|
||||||
// QML Engine
|
// QML Engine
|
||||||
engine *qml.Engine
|
engine *qml.Engine
|
||||||
component *qml.Common
|
component *qml.Common
|
||||||
qmlDone bool
|
|
||||||
// The ethereum interface
|
// The ethereum interface
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
|
serviceEvents chan ServEv
|
||||||
|
|
||||||
// The public Ethereum library
|
// The public Ethereum library
|
||||||
uiLib *UiLib
|
uiLib *UiLib
|
||||||
@ -83,7 +95,17 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
|
|||||||
}
|
}
|
||||||
|
|
||||||
xeth := xeth.NewJSXEth(ethereum)
|
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"))
|
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
|
||||||
json.Unmarshal([]byte(data), &gui.plugins)
|
json.Unmarshal([]byte(data), &gui.plugins)
|
||||||
|
|
||||||
@ -95,6 +117,8 @@ func (gui *Gui) Start(assetPath string) {
|
|||||||
|
|
||||||
guilogger.Infoln("Starting GUI")
|
guilogger.Infoln("Starting GUI")
|
||||||
|
|
||||||
|
go gui.service()
|
||||||
|
|
||||||
// Register ethereum functions
|
// Register ethereum functions
|
||||||
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||||
Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
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("eth", gui.uiLib)
|
||||||
context.SetVar("shh", gui.whisper)
|
context.SetVar("shh", gui.whisper)
|
||||||
|
|
||||||
// Load the main QML interface
|
win, err := gui.showWallet(context)
|
||||||
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
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err)
|
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()
|
win.Show()
|
||||||
|
|
||||||
// only add the gui guilogger after window is shown otherwise slider wont be shown
|
// only add the gui guilogger after window is shown otherwise slider wont be shown
|
||||||
if addlog {
|
logger.AddLogSystem(gui)
|
||||||
logger.AddLogSystem(gui)
|
|
||||||
}
|
|
||||||
win.Wait()
|
win.Wait()
|
||||||
|
|
||||||
// need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.win = gui.createWindow(component)
|
gui.createWindow(component)
|
||||||
|
|
||||||
gui.update()
|
|
||||||
|
|
||||||
return gui.win, nil
|
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) {
|
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 {
|
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
|
||||||
win := comp.CreateWindow(nil)
|
gui.win = comp.CreateWindow(nil)
|
||||||
|
gui.uiLib.win = gui.win
|
||||||
gui.win = win
|
|
||||||
gui.uiLib.win = win
|
|
||||||
|
|
||||||
return gui.win
|
return gui.win
|
||||||
}
|
}
|
||||||
@ -272,7 +274,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ptx = xeth.NewJSTx(tx, gui.xeth.World().State())
|
ptx = xeth.NewJSTx(tx)
|
||||||
send = nameReg.Storage(tx.From())
|
send = nameReg.Storage(tx.From())
|
||||||
rec = nameReg.Storage(tx.To())
|
rec = nameReg.Storage(tx.To())
|
||||||
s, r string
|
s, r string
|
||||||
@ -345,11 +347,48 @@ func (self *Gui) getObjectByName(objectName string) qml.Object {
|
|||||||
return self.win.Root().ObjectByName(objectName)
|
return self.win.Root().ObjectByName(objectName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple go routine function that updates the list of peers in the GUI
|
func loadJavascriptAssets(gui *Gui) (jsfiles string) {
|
||||||
func (gui *Gui) update() {
|
for _, fn := range []string{"ext/q.js", "ext/eth.js/main.js", "ext/eth.js/qt.js", "ext/setup.js"} {
|
||||||
// We have to wait for qml to be done loading all the windows.
|
f, err := os.Open(gui.uiLib.AssetPath(fn))
|
||||||
for !gui.qmlDone {
|
if err != nil {
|
||||||
time.Sleep(300 * time.Millisecond)
|
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() {
|
go func() {
|
||||||
@ -359,14 +398,21 @@ func (gui *Gui) update() {
|
|||||||
gui.setPeerInfo()
|
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 {
|
gui.whisper.SetView(gui.getObjectByName("whisperView"))
|
||||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
|
||||||
|
|
||||||
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)
|
peerUpdateTicker := time.NewTicker(5 * time.Second)
|
||||||
generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||||
statsUpdateTicker := time.NewTicker(5 * time.Second)
|
statsUpdateTicker := time.NewTicker(5 * time.Second)
|
||||||
@ -385,77 +431,75 @@ func (gui *Gui) update() {
|
|||||||
core.TxPostEvent{},
|
core.TxPostEvent{},
|
||||||
)
|
)
|
||||||
|
|
||||||
go func() {
|
defer events.Unsubscribe()
|
||||||
defer events.Unsubscribe()
|
for {
|
||||||
for {
|
select {
|
||||||
select {
|
case ev, isopen := <-events.Chan():
|
||||||
case ev, isopen := <-events.Chan():
|
if !isopen {
|
||||||
if !isopen {
|
return
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
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() {
|
func (gui *Gui) setStatsPane() {
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// This library is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public
|
go-ethereum is free software: you can redistribute it and/or modify
|
||||||
// License as published by the Free Software Foundation; either
|
it under the terms of the GNU General Public License as published by
|
||||||
// version 2.1 of the License, or (at your option) any later version.
|
the Free Software Foundation, either version 3 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
|
go-ethereum is distributed in the hope that it will be useful,
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// General Public License for more details.
|
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
|
You should have received a copy of the GNU General Public License
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
// MA 02110-1301 USA
|
*/
|
||||||
|
/**
|
||||||
|
* @authors
|
||||||
|
* Jeffrey Wilcke <i@jev.io>
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
/*
|
||||||
//
|
This file is part of go-ethereum
|
||||||
// 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
|
|
||||||
|
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
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 := &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.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||||
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
||||||
|
go lib.filterManager.Start()
|
||||||
|
|
||||||
return lib
|
return lib
|
||||||
}
|
}
|
||||||
@ -208,16 +212,16 @@ func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
|
|||||||
dbWindow := NewDebuggerWindow(self)
|
dbWindow := NewDebuggerWindow(self)
|
||||||
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
|
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
|
||||||
if len(object.Code) > 0 {
|
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()
|
dbWindow.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *UiLib) StartDbWithCode(code string) {
|
func (self *UiLib) StartDbWithCode(code string) {
|
||||||
dbWindow := NewDebuggerWindow(self)
|
dbWindow := NewDebuggerWindow(self)
|
||||||
dbWindow.SetCode("0x" + code)
|
dbWindow.SetCode(code)
|
||||||
dbWindow.Show()
|
dbWindow.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +283,10 @@ func (self *UiLib) SetGasPrice(price string) {
|
|||||||
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
|
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) SetExtra(extra string) {
|
||||||
|
self.miner.Extra = extra
|
||||||
|
}
|
||||||
|
|
||||||
func (self *UiLib) ToggleMining() bool {
|
func (self *UiLib) ToggleMining() bool {
|
||||||
if !self.miner.Mining() {
|
if !self.miner.Mining() {
|
||||||
self.miner.Start()
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -12,29 +27,32 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/p2p"
|
"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() {
|
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))
|
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel))
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y)
|
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y)
|
||||||
|
|
||||||
srv := p2p.Server{
|
srv := p2p.Server{
|
||||||
MaxPeers: 100,
|
MaxPeers: 100,
|
||||||
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)),
|
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", marshaled),
|
||||||
ListenAddr: ":30301",
|
ListenAddr: *listenAddr,
|
||||||
NAT: p2p.UPNP(),
|
NAT: nat,
|
||||||
|
NoDial: true,
|
||||||
}
|
}
|
||||||
if err := srv.Start(); err != nil {
|
if err := srv.Start(); err != nil {
|
||||||
fmt.Println("could not start server:", err)
|
log.Fatal("could not start server:", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {}
|
select {}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
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.
|
// rlpdump is a pretty-printer for RLP data.
|
||||||
package main
|
package main
|
||||||
@ -106,8 +110,7 @@ func dump(s *rlp.Stream, depth int) error {
|
|||||||
s.List()
|
s.List()
|
||||||
defer s.ListEnd()
|
defer s.ListEnd()
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
fmt.Printf(ws(depth) + "[]")
|
fmt.Print(ws(depth) + "[]")
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(ws(depth) + "[")
|
fmt.Println(ws(depth) + "[")
|
||||||
for i := 0; ; i++ {
|
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
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -18,6 +39,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -238,7 +260,8 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
|||||||
|
|
||||||
parent := ethereum.ChainManager().GetBlock(block.ParentHash())
|
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 {
|
if err != nil {
|
||||||
return err
|
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
|
package utils
|
||||||
|
|
||||||
import (
|
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
|
package utils
|
||||||
|
|
||||||
import (
|
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/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/event/filter"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"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/websocket"
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
@ -15,16 +40,19 @@ func args(v ...interface{}) []interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebSocketServer struct {
|
type WebSocketServer struct {
|
||||||
ethereum *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
filterCallbacks map[int][]int
|
filterManager *filter.FilterManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebSocketServer(eth *eth.Ethereum) *WebSocketServer {
|
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() {
|
func (self *WebSocketServer) Serv() {
|
||||||
pipe := xeth.NewJSXEth(self.ethereum)
|
pipe := xeth.NewJSXEth(self.eth)
|
||||||
|
|
||||||
wsServ := websocket.NewServer("/eth", ":40404")
|
wsServ := websocket.NewServer("/eth", ":40404")
|
||||||
wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) {
|
wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) {
|
||||||
@ -33,74 +61,91 @@ func (self *WebSocketServer) Serv() {
|
|||||||
data := ethutil.NewValue(msg.Args)
|
data := ethutil.NewValue(msg.Args)
|
||||||
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
|
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Write(args(nil, err.Error()), msg.Seed)
|
c.Write(args(nil, err.Error()), msg.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
code := ethutil.Bytes2Hex(bcode)
|
code := ethutil.Bytes2Hex(bcode)
|
||||||
c.Write(args(code, nil), msg.Seed)
|
c.Write(args(code, nil), msg.Id)
|
||||||
case "getBlockByNumber":
|
case "eth_blockByNumber":
|
||||||
args := msg.Arguments()
|
args := msg.Arguments()
|
||||||
|
|
||||||
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
|
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
|
||||||
c.Write(block, msg.Seed)
|
c.Write(block, msg.Id)
|
||||||
|
|
||||||
case "getKey":
|
case "eth_blockByHash":
|
||||||
c.Write(pipe.Key().PrivateKey, msg.Seed)
|
args := msg.Arguments()
|
||||||
case "transact":
|
|
||||||
|
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Id)
|
||||||
|
|
||||||
|
case "eth_transact":
|
||||||
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
||||||
object := mapToTxParams(mp)
|
object := mapToTxParams(mp)
|
||||||
c.Write(
|
c.Write(
|
||||||
args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
|
args(pipe.Transact(pipe.Key().PrivateKey, object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
|
||||||
msg.Seed,
|
msg.Id,
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
case "getCoinBase":
|
case "eth_gasPrice":
|
||||||
c.Write(pipe.CoinBase(), msg.Seed)
|
c.Write("10000000000000", msg.Id)
|
||||||
|
case "eth_coinbase":
|
||||||
|
c.Write(pipe.CoinBase(), msg.Id)
|
||||||
|
|
||||||
case "getIsListening":
|
case "eth_listening":
|
||||||
c.Write(pipe.IsListening(), msg.Seed)
|
c.Write(pipe.IsListening(), msg.Id)
|
||||||
|
|
||||||
case "getIsMining":
|
case "eth_mining":
|
||||||
c.Write(pipe.IsMining(), msg.Seed)
|
c.Write(pipe.IsMining(), msg.Id)
|
||||||
|
|
||||||
case "getPeerCoint":
|
case "eth_peerCount":
|
||||||
c.Write(pipe.PeerCount(), msg.Seed)
|
c.Write(pipe.PeerCount(), msg.Id)
|
||||||
|
|
||||||
case "getCountAt":
|
case "eth_countAt":
|
||||||
args := msg.Arguments()
|
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()
|
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()
|
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()
|
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":
|
case "eth_accounts":
|
||||||
args := msg.Arguments()
|
c.Write(pipe.Accounts(), msg.Id)
|
||||||
|
|
||||||
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed)
|
case "eth_newFilter":
|
||||||
|
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
||||||
case "getSecretToAddress":
|
var id int
|
||||||
args := msg.Arguments()
|
filter := ui.NewFilterFromMap(mp, self.eth)
|
||||||
|
filter.MessageCallback = func(messages state.Messages) {
|
||||||
c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed)
|
c.Event(toMessages(messages), "eth_changed", id)
|
||||||
|
}
|
||||||
case "newFilter":
|
id = self.filterManager.InstallFilter(filter)
|
||||||
case "newFilterString":
|
c.Write(id, msg.Id)
|
||||||
case "messages":
|
}
|
||||||
// TODO
|
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()
|
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) {
|
func StartWebSockets(eth *eth.Ethereum) {
|
||||||
wslogger.Infoln("Starting WebSockets")
|
wslogger.Infoln("Starting WebSockets")
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
@ -36,6 +35,7 @@ type EthManager interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BlockProcessor struct {
|
type BlockProcessor struct {
|
||||||
|
db ethutil.Database
|
||||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
// Canonical block chain
|
// Canonical block chain
|
||||||
@ -57,8 +57,9 @@ type BlockProcessor struct {
|
|||||||
eventMux *event.TypeMux
|
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{
|
sm := &BlockProcessor{
|
||||||
|
db: db,
|
||||||
mem: make(map[string]*big.Int),
|
mem: make(map[string]*big.Int),
|
||||||
Pow: ezp.New(),
|
Pow: ezp.New(),
|
||||||
bc: chainManager,
|
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) {
|
func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||||
sm.lastAttemptedBlock = block
|
sm.lastAttemptedBlock = block
|
||||||
|
|
||||||
state := state.New(parent.Trie().Copy())
|
state := state.New(parent.Root(), sm.db)
|
||||||
|
//state := state.New(parent.Trie().Copy())
|
||||||
|
|
||||||
// Block validation
|
// Block validation
|
||||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||||
@ -214,52 +216,33 @@ func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the new total difficulty and sync back to the db
|
// Calculate the td for this block
|
||||||
if td, ok := sm.CalculateTD(block); ok {
|
td = CalculateTD(block, parent)
|
||||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||||
state.Sync()
|
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
|
return td, messages, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validates the current block. Returns an error if the block was invalid,
|
// Validates the current block. Returns an error if the block was invalid,
|
||||||
// an uncle or anything that isn't on the current block chain.
|
// an uncle or anything that isn't on the current block chain.
|
||||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||||
func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
|
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)
|
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)
|
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 {
|
func (sm *BlockProcessor) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||||
reward := new(big.Int).Set(BlockReward)
|
reward := new(big.Int).Set(BlockReward)
|
||||||
|
|
||||||
knownUncles := set.New()
|
ancestors := set.New()
|
||||||
for _, uncle := range parent.Uncles() {
|
for _, ancestor := range sm.bc.GetAncestors(block, 7) {
|
||||||
knownUncles.Add(string(uncle.Hash()))
|
ancestors.Add(string(ancestor.Hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
nonces := ethutil.NewSet(block.Header().Nonce)
|
uncles := set.New()
|
||||||
|
uncles.Add(string(block.Hash()))
|
||||||
for _, uncle := range block.Uncles() {
|
for _, uncle := range block.Uncles() {
|
||||||
if nonces.Include(uncle.Nonce) {
|
if uncles.Has(string(uncle.Hash())) {
|
||||||
// Error not unique
|
// Error not unique
|
||||||
return UncleError("Uncle not unique")
|
return UncleError("Uncle not unique")
|
||||||
}
|
}
|
||||||
|
uncles.Add(string(uncle.Hash()))
|
||||||
|
|
||||||
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
if !ancestors.Has(string(uncle.ParentHash)) {
|
||||||
if uncleParent == nil {
|
|
||||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
|
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())) {
|
if uncleParent.Number().Cmp(new(big.Int).Sub(parent.Number(), big.NewInt(6))) < 0 {
|
||||||
return UncleError("Uncle in chain")
|
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 := new(big.Int)
|
||||||
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
|
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 (
|
var (
|
||||||
parent = sm.bc.GetBlock(block.Header().ParentHash)
|
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()
|
defer state.Reset()
|
||||||
|
@ -23,17 +23,30 @@ type StateQuery interface {
|
|||||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||||
diff := new(big.Int)
|
diff := new(big.Int)
|
||||||
|
|
||||||
bh, ph := block.Header(), parent.Header()
|
adjust := new(big.Int).Rsh(parent.Difficulty(), 10)
|
||||||
adjust := new(big.Int).Rsh(ph.Difficulty, 10)
|
if block.Time() >= parent.Time()+8 {
|
||||||
if bh.Time >= ph.Time+5 {
|
diff.Sub(parent.Difficulty(), adjust)
|
||||||
diff.Sub(ph.Difficulty, adjust)
|
|
||||||
} else {
|
} else {
|
||||||
diff.Add(ph.Difficulty, adjust)
|
diff.Add(parent.Difficulty(), adjust)
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff
|
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 {
|
func CalcGasLimit(parent, block *types.Block) *big.Int {
|
||||||
if block.Number().Cmp(big.NewInt(0)) == 0 {
|
if block.Number().Cmp(big.NewInt(0)) == 0 {
|
||||||
return ethutil.BigPow(10, 6)
|
return ethutil.BigPow(10, 6)
|
||||||
@ -55,6 +68,7 @@ func CalcGasLimit(parent, block *types.Block) *big.Int {
|
|||||||
|
|
||||||
type ChainManager struct {
|
type ChainManager struct {
|
||||||
//eth EthManager
|
//eth EthManager
|
||||||
|
db ethutil.Database
|
||||||
processor types.BlockProcessor
|
processor types.BlockProcessor
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
@ -96,13 +110,9 @@ func (self *ChainManager) CurrentBlock() *types.Block {
|
|||||||
return self.currentBlock
|
return self.currentBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
|
||||||
bc := &ChainManager{}
|
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
|
||||||
bc.genesisBlock = GenesisBlock()
|
|
||||||
bc.eventMux = mux
|
|
||||||
|
|
||||||
bc.setLastBlock()
|
bc.setLastBlock()
|
||||||
|
|
||||||
bc.transState = bc.State().Copy()
|
bc.transState = bc.State().Copy()
|
||||||
|
|
||||||
return bc
|
return bc
|
||||||
@ -120,7 +130,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) State() *state.StateDB {
|
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 {
|
func (self *ChainManager) TransState() *state.StateDB {
|
||||||
@ -128,7 +138,7 @@ func (self *ChainManager) TransState() *state.StateDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) setLastBlock() {
|
func (bc *ChainManager) setLastBlock() {
|
||||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
data, _ := bc.db.Get([]byte("LastBlock"))
|
||||||
if len(data) != 0 {
|
if len(data) != 0 {
|
||||||
var block types.Block
|
var block types.Block
|
||||||
rlp.Decode(bytes.NewReader(data), &block)
|
rlp.Decode(bytes.NewReader(data), &block)
|
||||||
@ -137,12 +147,12 @@ func (bc *ChainManager) setLastBlock() {
|
|||||||
bc.lastBlockNumber = block.Header().Number.Uint64()
|
bc.lastBlockNumber = block.Header().Number.Uint64()
|
||||||
|
|
||||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
// 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 {
|
} else {
|
||||||
bc.Reset()
|
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
|
// Block creation & chain handling
|
||||||
@ -183,7 +193,7 @@ func (bc *ChainManager) Reset() {
|
|||||||
defer bc.mu.Unlock()
|
defer bc.mu.Unlock()
|
||||||
|
|
||||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
|
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
|
// Prepare the genesis block
|
||||||
@ -210,7 +220,7 @@ func (self *ChainManager) Export() []byte {
|
|||||||
|
|
||||||
func (bc *ChainManager) insert(block *types.Block) {
|
func (bc *ChainManager) insert(block *types.Block) {
|
||||||
encodedBlock := ethutil.Encode(block)
|
encodedBlock := ethutil.Encode(block)
|
||||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
bc.db.Put([]byte("LastBlock"), encodedBlock)
|
||||||
bc.currentBlock = block
|
bc.currentBlock = block
|
||||||
bc.lastBlockHash = block.Hash()
|
bc.lastBlockHash = block.Hash()
|
||||||
}
|
}
|
||||||
@ -218,8 +228,8 @@ func (bc *ChainManager) insert(block *types.Block) {
|
|||||||
func (bc *ChainManager) write(block *types.Block) {
|
func (bc *ChainManager) write(block *types.Block) {
|
||||||
bc.writeBlockInfo(block)
|
bc.writeBlockInfo(block)
|
||||||
|
|
||||||
encodedBlock := ethutil.Encode(block)
|
encodedBlock := ethutil.Encode(block.RlpDataForStorage())
|
||||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
bc.db.Put(block.Hash(), encodedBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
@ -229,7 +239,7 @@ func (bc *ChainManager) Genesis() *types.Block {
|
|||||||
|
|
||||||
// Block fetching methods
|
// Block fetching methods
|
||||||
func (bc *ChainManager) HasBlock(hash []byte) bool {
|
func (bc *ChainManager) HasBlock(hash []byte) bool {
|
||||||
data, _ := ethutil.Config.Db.Get(hash)
|
data, _ := bc.db.Get(hash)
|
||||||
return len(data) != 0
|
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)
|
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||||
for i := uint64(0); i < max; i++ {
|
for i := uint64(0); i < max; i++ {
|
||||||
|
block = self.GetBlock(block.Header().ParentHash)
|
||||||
chain = append(chain, block.Hash())
|
chain = append(chain, block.Hash())
|
||||||
|
|
||||||
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
block = self.GetBlock(block.Header().ParentHash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||||
data, _ := ethutil.Config.Db.Get(hash)
|
data, _ := self.db.Get(hash)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -267,6 +275,28 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
|||||||
return &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 {
|
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||||
self.mu.RLock()
|
self.mu.RLock()
|
||||||
defer self.mu.RUnlock()
|
defer self.mu.RUnlock()
|
||||||
@ -286,7 +316,7 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
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
|
bc.td = td
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,12 +373,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
|||||||
cblock := self.currentBlock
|
cblock := self.currentBlock
|
||||||
if td.Cmp(self.td) > 0 {
|
if td.Cmp(self.td) > 0 {
|
||||||
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 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.setTotalDifficulty(td)
|
||||||
self.insert(block)
|
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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
@ -21,14 +21,6 @@ func init() {
|
|||||||
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
|
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) {
|
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)
|
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 {
|
if err != nil {
|
||||||
@ -54,7 +46,7 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestChainInsertions(t *testing.T) {
|
func TestChainInsertions(t *testing.T) {
|
||||||
reset()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
|
||||||
chain1, err := loadChain("valid1", t)
|
chain1, err := loadChain("valid1", t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -69,9 +61,9 @@ func TestChainInsertions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var eventMux event.TypeMux
|
var eventMux event.TypeMux
|
||||||
chainMan := NewChainManager(&eventMux)
|
chainMan := NewChainManager(db, &eventMux)
|
||||||
txPool := NewTxPool(&eventMux)
|
txPool := NewTxPool(&eventMux)
|
||||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
|
||||||
chainMan.SetProcessor(blockMan)
|
chainMan.SetProcessor(blockMan)
|
||||||
|
|
||||||
const max = 2
|
const max = 2
|
||||||
@ -84,17 +76,17 @@ func TestChainInsertions(t *testing.T) {
|
|||||||
<-done
|
<-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")
|
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")
|
t.Error("chain1 isn't canonical and should be")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChainMultipleInsertions(t *testing.T) {
|
func TestChainMultipleInsertions(t *testing.T) {
|
||||||
reset()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
|
||||||
const max = 4
|
const max = 4
|
||||||
chains := make([]types.Blocks, max)
|
chains := make([]types.Blocks, max)
|
||||||
@ -113,9 +105,9 @@ func TestChainMultipleInsertions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var eventMux event.TypeMux
|
var eventMux event.TypeMux
|
||||||
chainMan := NewChainManager(&eventMux)
|
chainMan := NewChainManager(db, &eventMux)
|
||||||
txPool := NewTxPool(&eventMux)
|
txPool := NewTxPool(&eventMux)
|
||||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
|
||||||
chainMan.SetProcessor(blockMan)
|
chainMan.SetProcessor(blockMan)
|
||||||
done := make(chan bool, max)
|
done := make(chan bool, max)
|
||||||
for i, chain := range chains {
|
for i, chain := range chains {
|
||||||
@ -132,7 +124,25 @@ func TestChainMultipleInsertions(t *testing.T) {
|
|||||||
<-done
|
<-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")
|
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"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
@ -13,7 +14,6 @@ type Execution struct {
|
|||||||
env vm.Environment
|
env vm.Environment
|
||||||
address, input []byte
|
address, input []byte
|
||||||
Gas, price, value *big.Int
|
Gas, price, value *big.Int
|
||||||
SkipTransfer bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
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) {
|
func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) {
|
||||||
env := self.env
|
env := self.env
|
||||||
evm := vm.New(env, vm.DebugVmTy)
|
evm := vm.New(env)
|
||||||
|
|
||||||
if env.Depth() == vm.MaxCallDepth {
|
if env.Depth() == vm.MaxCallDepth {
|
||||||
caller.ReturnGas(self.Gas, self.price)
|
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{}
|
return nil, vm.DepthError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
vsnapshot := env.State().Copy()
|
||||||
// Skipping transfer is used on testing for the initial call
|
if len(self.address) == 0 {
|
||||||
if !self.SkipTransfer {
|
// Generate a new address
|
||||||
err = env.Transfer(from, to, self.value)
|
nonce := env.State().GetNonce(caller.Address())
|
||||||
if err != nil {
|
self.address = crypto.CreateAddress(caller.Address(), nonce)
|
||||||
caller.ReturnGas(self.Gas, self.price)
|
env.State().SetNonce(caller.Address(), nonce+1)
|
||||||
|
}
|
||||||
|
|
||||||
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance)
|
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||||
return
|
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()
|
snapshot := env.State().Copy()
|
||||||
|
@ -19,18 +19,20 @@ var ZeroHash512 = make([]byte, 64)
|
|||||||
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
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 := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
|
||||||
genesis.Header().Number = ethutil.Big0
|
genesis.Header().Number = ethutil.Big0
|
||||||
genesis.Header().GasLimit = big.NewInt(1000000)
|
genesis.Header().GasLimit = big.NewInt(1000000)
|
||||||
genesis.Header().GasUsed = ethutil.Big0
|
genesis.Header().GasUsed = ethutil.Big0
|
||||||
genesis.Header().Time = 0
|
genesis.Header().Time = 0
|
||||||
|
genesis.Td = ethutil.Big0
|
||||||
|
|
||||||
genesis.SetUncles([]*types.Header{})
|
genesis.SetUncles([]*types.Header{})
|
||||||
genesis.SetTransactions(types.Transactions{})
|
genesis.SetTransactions(types.Transactions{})
|
||||||
genesis.SetReceipts(types.Receipts{})
|
genesis.SetReceipts(types.Receipts{})
|
||||||
|
|
||||||
statedb := state.New(genesis.Trie())
|
statedb := state.New(genesis.Root(), db)
|
||||||
|
//statedb := state.New(genesis.Trie())
|
||||||
for _, addr := range []string{
|
for _, addr := range []string{
|
||||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||||
|
@ -77,7 +77,6 @@ func NewTestManager() *TestManager {
|
|||||||
fmt.Println("Could not create mem-db, failing")
|
fmt.Println("Could not create mem-db, failing")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ethutil.Config.Db = db
|
|
||||||
|
|
||||||
testManager := &TestManager{}
|
testManager := &TestManager{}
|
||||||
testManager.eventMux = new(event.TypeMux)
|
testManager.eventMux = new(event.TypeMux)
|
||||||
|
@ -192,8 +192,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
dataGas.Mul(dataGas, vm.GasCreateByte)
|
dataGas.Mul(dataGas, vm.GasCreateByte)
|
||||||
if err = self.UseGas(dataGas); err == nil {
|
if err := self.UseGas(dataGas); err == nil {
|
||||||
//self.state.SetCode(ref.Address(), ret)
|
|
||||||
ref.SetCode(ret)
|
ref.SetCode(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,11 +56,6 @@ func NewTxPool(eventMux *event.TypeMux) *TxPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
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 {
|
if len(tx.To()) != 0 && len(tx.To()) != 20 {
|
||||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
|
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 {
|
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)
|
err := self.ValidateTransaction(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -149,6 +148,7 @@ func (pool *TxPool) RemoveInvalid(query StateQuery) {
|
|||||||
for _, tx := range pool.txs {
|
for _, tx := range pool.txs {
|
||||||
sender := query.GetAccount(tx.From())
|
sender := query.GetAccount(tx.From())
|
||||||
err := pool.ValidateTransaction(tx)
|
err := pool.ValidateTransaction(tx)
|
||||||
|
fmt.Println(err, sender.Nonce, tx.Nonce())
|
||||||
if err != nil || sender.Nonce >= tx.Nonce() {
|
if err != nil || sender.Nonce >= tx.Nonce() {
|
||||||
removedTxs = append(removedTxs, tx)
|
removedTxs = append(removedTxs, tx)
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,22 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
// State query interface
|
// 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 {
|
func (self stateQuery) GetAccount(addr []byte) *state.StateObject {
|
||||||
return state.NewStateObject(addr)
|
return state.NewStateObject(addr, self.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func transaction() *types.Transaction {
|
func transaction() *types.Transaction {
|
||||||
@ -55,7 +61,7 @@ func TestAddInvalidTx(t *testing.T) {
|
|||||||
func TestRemoveSet(t *testing.T) {
|
func TestRemoveSet(t *testing.T) {
|
||||||
pool, _ := setup()
|
pool, _ := setup()
|
||||||
tx1 := transaction()
|
tx1 := transaction()
|
||||||
pool.pool.Add(tx1)
|
pool.addTx(tx1)
|
||||||
pool.RemoveSet(types.Transactions{tx1})
|
pool.RemoveSet(types.Transactions{tx1})
|
||||||
if pool.Size() > 0 {
|
if pool.Size() > 0 {
|
||||||
t.Error("expected pool size to be 0")
|
t.Error("expected pool size to be 0")
|
||||||
@ -65,16 +71,16 @@ func TestRemoveSet(t *testing.T) {
|
|||||||
func TestRemoveInvalid(t *testing.T) {
|
func TestRemoveInvalid(t *testing.T) {
|
||||||
pool, key := setup()
|
pool, key := setup()
|
||||||
tx1 := transaction()
|
tx1 := transaction()
|
||||||
pool.pool.Add(tx1)
|
pool.addTx(tx1)
|
||||||
pool.RemoveInvalid(stateQuery{})
|
pool.RemoveInvalid(SQ())
|
||||||
if pool.Size() > 0 {
|
if pool.Size() > 0 {
|
||||||
t.Error("expected pool size to be 0")
|
t.Error("expected pool size to be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
tx1.SetNonce(1)
|
tx1.SetNonce(1)
|
||||||
tx1.SignECDSA(key)
|
tx1.SignECDSA(key)
|
||||||
pool.pool.Add(tx1)
|
pool.addTx(tx1)
|
||||||
pool.RemoveInvalid(stateQuery{})
|
pool.RemoveInvalid(SQ())
|
||||||
if pool.Size() != 1 {
|
if pool.Size() != 1 {
|
||||||
t.Error("expected pool size to be 1, is", pool.Size())
|
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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/ptrie"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Header struct {
|
type Header struct {
|
||||||
@ -168,16 +166,18 @@ func (self *Block) RlpDataForStorage() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Header accessors (add as you need them)
|
// Header accessors (add as you need them)
|
||||||
func (self *Block) Number() *big.Int { return self.header.Number }
|
func (self *Block) Number() *big.Int { return self.header.Number }
|
||||||
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
||||||
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
||||||
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
||||||
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
||||||
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
||||||
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
|
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) 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) 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))) }
|
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
|
||||||
|
|
||||||
// Implement pow.Block
|
// Implement pow.Block
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/ptrie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DerivableList interface {
|
type DerivableList interface {
|
||||||
@ -11,7 +12,8 @@ type DerivableList interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeriveSha(list DerivableList) []byte {
|
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++ {
|
for i := 0; i < list.Len(); i++ {
|
||||||
trie.Update(ethutil.Encode(i), list.GetRlp(i))
|
trie.Update(ethutil.Encode(i), list.GetRlp(i))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,12 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/obscuren/secp256k1-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These tests are sanity checks.
|
// 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)
|
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 (
|
const (
|
||||||
seedNodeAddress = "poc-7.ethdev.com:30300"
|
seedNodeAddress = "poc-8.ethdev.com:30303"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -81,7 +81,7 @@ type Ethereum struct {
|
|||||||
func New(config *Config) (*Ethereum, error) {
|
func New(config *Config) (*Ethereum, error) {
|
||||||
// Boostrap database
|
// Boostrap database
|
||||||
logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel)
|
logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel)
|
||||||
db, err := ethdb.NewLDBDatabase("database")
|
db, err := ethdb.NewLDBDatabase("blockchain")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey())
|
clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey())
|
||||||
|
|
||||||
saveProtocolVersion(db)
|
saveProtocolVersion(db)
|
||||||
ethutil.Config.Db = db
|
//ethutil.Config.Db = db
|
||||||
|
|
||||||
eth := &Ethereum{
|
eth := &Ethereum{
|
||||||
shutdownChan: make(chan bool),
|
shutdownChan: make(chan bool),
|
||||||
@ -123,9 +123,9 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.chainManager = core.NewChainManager(eth.EventMux())
|
eth.chainManager = core.NewChainManager(db, eth.EventMux())
|
||||||
eth.txPool = core.NewTxPool(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.chainManager.SetProcessor(eth.blockProcessor)
|
||||||
eth.whisper = whisper.New()
|
eth.whisper = whisper.New()
|
||||||
|
|
||||||
@ -134,24 +134,20 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
||||||
|
|
||||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||||
protocols := []p2p.Protocol{ethProto}
|
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||||
|
|
||||||
if config.Shh {
|
|
||||||
eth.whisper = whisper.New()
|
|
||||||
protocols = append(protocols, eth.whisper.Protocol())
|
|
||||||
}
|
|
||||||
|
|
||||||
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
|
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
fmt.Println(nat)
|
||||||
|
|
||||||
eth.net = &p2p.Server{
|
eth.net = &p2p.Server{
|
||||||
Identity: clientId,
|
Identity: clientId,
|
||||||
MaxPeers: config.MaxPeers,
|
MaxPeers: config.MaxPeers,
|
||||||
Protocols: protocols,
|
Protocols: protocols,
|
||||||
Blacklist: eth.blacklist,
|
Blacklist: eth.blacklist,
|
||||||
NAT: nat,
|
NAT: p2p.UPNP(),
|
||||||
NoDial: !config.Dial,
|
NoDial: !config.Dial,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +245,7 @@ func (s *Ethereum) Start(seed bool) error {
|
|||||||
if seed {
|
if seed {
|
||||||
logger.Infof("Connect to seed node %v", seedNodeAddress)
|
logger.Infof("Connect to seed node %v", seedNodeAddress)
|
||||||
if err := s.SuggestPeer(seedNodeAddress); err != nil {
|
if err := s.SuggestPeer(seedNodeAddress); err != nil {
|
||||||
return err
|
logger.Infoln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -24,8 +25,8 @@ const (
|
|||||||
blocksRequestRepetition = 1
|
blocksRequestRepetition = 1
|
||||||
blockHashesRequestInterval = 500 // ms
|
blockHashesRequestInterval = 500 // ms
|
||||||
blocksRequestMaxIdleRounds = 100
|
blocksRequestMaxIdleRounds = 100
|
||||||
cacheTimeout = 3 // minutes
|
blockHashesTimeout = 60 // seconds
|
||||||
blockTimeout = 5 // minutes
|
blocksTimeout = 120 // seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
type poolNode struct {
|
type poolNode struct {
|
||||||
@ -70,9 +71,14 @@ type BlockPool struct {
|
|||||||
type peerInfo struct {
|
type peerInfo struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
td *big.Int
|
td *big.Int
|
||||||
currentBlock []byte
|
currentBlockHash []byte
|
||||||
id string
|
currentBlock *types.Block
|
||||||
|
currentBlockC chan *types.Block
|
||||||
|
parentHash []byte
|
||||||
|
headSection *section
|
||||||
|
headSectionC chan *section
|
||||||
|
id string
|
||||||
|
|
||||||
requestBlockHashes func([]byte) error
|
requestBlockHashes func([]byte) error
|
||||||
requestBlocks 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
|
// 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
|
// 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
|
// 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()
|
self.peersLock.Lock()
|
||||||
defer self.peersLock.Unlock()
|
defer self.peersLock.Unlock()
|
||||||
peer, ok := self.peers[peerId]
|
peer, ok := self.peers[peerId]
|
||||||
if ok {
|
if ok {
|
||||||
poolLogger.Debugf("Update peer %v with td %v and current block %x", peerId, td, currentBlock[:4])
|
if bytes.Compare(peer.currentBlockHash, currentBlockHash) != 0 {
|
||||||
peer.td = td
|
poolLogger.Debugf("Update peer %v with td %v and current block %s", peerId, td, name(currentBlockHash))
|
||||||
peer.currentBlock = currentBlock
|
peer.lock.Lock()
|
||||||
|
peer.td = td
|
||||||
|
peer.currentBlockHash = currentBlockHash
|
||||||
|
peer.currentBlock = nil
|
||||||
|
peer.parentHash = nil
|
||||||
|
peer.headSection = nil
|
||||||
|
peer.lock.Unlock()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
peer = &peerInfo{
|
peer = &peerInfo{
|
||||||
td: td,
|
td: td,
|
||||||
currentBlock: currentBlock,
|
currentBlockHash: currentBlockHash,
|
||||||
id: peerId, //peer.Identity().Pubkey()
|
id: peerId, //peer.Identity().Pubkey()
|
||||||
requestBlockHashes: requestBlockHashes,
|
requestBlockHashes: requestBlockHashes,
|
||||||
requestBlocks: requestBlocks,
|
requestBlocks: requestBlocks,
|
||||||
peerError: peerError,
|
peerError: peerError,
|
||||||
sections: make(map[string]*section),
|
sections: make(map[string]*section),
|
||||||
|
currentBlockC: make(chan *types.Block),
|
||||||
|
headSectionC: make(chan *section),
|
||||||
}
|
}
|
||||||
self.peers[peerId] = peer
|
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
|
// check peer current head
|
||||||
if self.hasBlock(currentBlock) {
|
if self.hasBlock(currentBlockHash) {
|
||||||
// peer not ahead
|
// peer not ahead
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -234,22 +249,135 @@ func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string,
|
|||||||
if self.peer == peer {
|
if self.peer == peer {
|
||||||
// new block update
|
// new block update
|
||||||
// peer is already active best peer, request hashes
|
// peer is already active best peer, request hashes
|
||||||
poolLogger.Debugf("[%s] already the best peer. request hashes from %s", peerId, name(currentBlock))
|
poolLogger.Debugf("[%s] already the best peer. Request new head section info from %s", peerId, name(currentBlockHash))
|
||||||
peer.requestBlockHashes(currentBlock)
|
peer.headSectionC <- nil
|
||||||
return true
|
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
|
func (self *BlockPool) requestHeadSection(peer *peerInfo) {
|
||||||
if self.peer != nil {
|
self.wg.Add(1)
|
||||||
currentTD = self.peer.td
|
self.procWg.Add(1)
|
||||||
}
|
poolLogger.Debugf("[%s] head section at [%s] requesting info", peer.id, name(peer.currentBlockHash))
|
||||||
if td.Cmp(currentTD) > 0 {
|
|
||||||
poolLogger.Debugf("peer %v promoted best peer", peerId)
|
go func() {
|
||||||
self.switchPeer(self.peer, peer)
|
var idle bool
|
||||||
self.peer = peer
|
peer.lock.RLock()
|
||||||
return true
|
quitC := peer.quitC
|
||||||
}
|
currentBlockHash := peer.currentBlockHash
|
||||||
return false
|
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
|
// RemovePeer is called by the eth protocol when the peer disconnects
|
||||||
@ -274,13 +402,13 @@ func (self *BlockPool) RemovePeer(peerId string) {
|
|||||||
newPeer = info
|
newPeer = info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.peer = newPeer
|
|
||||||
self.switchPeer(peer, newPeer)
|
|
||||||
if newPeer != nil {
|
if newPeer != nil {
|
||||||
poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id, newPeer.td)
|
poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id, newPeer.td)
|
||||||
} else {
|
} else {
|
||||||
poolLogger.Warnln("no peers")
|
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
|
return
|
||||||
}
|
}
|
||||||
// peer is still the best
|
// peer is still the best
|
||||||
poolLogger.Debugf("adding hashes for best peer %s", peerId)
|
|
||||||
|
|
||||||
var size, n int
|
var size, n int
|
||||||
var hash []byte
|
var hash []byte
|
||||||
var ok bool
|
var ok, headSection bool
|
||||||
var section, child, parent *section
|
var sec, child, parent *section
|
||||||
var entry *poolEntry
|
var entry *poolEntry
|
||||||
var nodes []*poolNode
|
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:
|
LOOP:
|
||||||
// iterate using next (rlp stream lazy decoder) feeding hashesC
|
// iterate using next (rlp stream lazy decoder) feeding hashesC
|
||||||
for hash, ok = next(); ok; hash, ok = next() {
|
for ; ok; hash, ok = next() {
|
||||||
n++
|
n++
|
||||||
select {
|
select {
|
||||||
case <-self.quit:
|
case <-self.quit:
|
||||||
return
|
return
|
||||||
case <-peer.quitC:
|
case <-quitC:
|
||||||
// if the peer is demoted, no more hashes taken
|
// if the peer is demoted, no more hashes taken
|
||||||
peer = nil
|
bestPeer = nil
|
||||||
break LOOP
|
break LOOP
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -325,8 +484,8 @@ LOOP:
|
|||||||
// check if known block connecting the downloaded chain to our blockchain
|
// check if known block connecting the downloaded chain to our blockchain
|
||||||
poolLogger.DebugDetailf("[%s] known block", name(hash))
|
poolLogger.DebugDetailf("[%s] known block", name(hash))
|
||||||
// mark child as absolute pool root with parent known to blockchain
|
// mark child as absolute pool root with parent known to blockchain
|
||||||
if section != nil {
|
if sec != nil {
|
||||||
self.connectToBlockChain(section)
|
self.connectToBlockChain(sec)
|
||||||
} else {
|
} else {
|
||||||
if child != nil {
|
if child != nil {
|
||||||
self.connectToBlockChain(child)
|
self.connectToBlockChain(child)
|
||||||
@ -340,6 +499,7 @@ LOOP:
|
|||||||
// reached a known chain in the pool
|
// reached a known chain in the pool
|
||||||
if entry.node == entry.section.bottom && n == 1 {
|
if entry.node == entry.section.bottom && n == 1 {
|
||||||
// the first block hash received is an orphan in the pool, so rejoice and continue
|
// 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
|
child = entry.section
|
||||||
continue LOOP
|
continue LOOP
|
||||||
}
|
}
|
||||||
@ -353,7 +513,7 @@ LOOP:
|
|||||||
peer: peerId,
|
peer: peerId,
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
section = newSection()
|
sec = newSection()
|
||||||
}
|
}
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
size++
|
size++
|
||||||
@ -379,10 +539,10 @@ LOOP:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
self.processSection(section, nodes)
|
self.processSection(sec, nodes)
|
||||||
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(section), size, sectionName(child))
|
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(sec), size, sectionName(child))
|
||||||
self.link(parent, section)
|
self.link(parent, sec)
|
||||||
self.link(section, child)
|
self.link(sec, child)
|
||||||
} else {
|
} else {
|
||||||
poolLogger.DebugDetailf("[%s]->[%s] connecting known sections", sectionName(parent), sectionName(child))
|
poolLogger.DebugDetailf("[%s]->[%s] connecting known sections", sectionName(parent), sectionName(child))
|
||||||
self.link(parent, child)
|
self.link(parent, child)
|
||||||
@ -390,15 +550,31 @@ LOOP:
|
|||||||
|
|
||||||
self.chainLock.Unlock()
|
self.chainLock.Unlock()
|
||||||
|
|
||||||
if parent != nil && peer != nil {
|
if parent != nil && bestPeer != nil {
|
||||||
self.activateChain(parent, peer)
|
self.activateChain(parent, peer)
|
||||||
poolLogger.Debugf("[%s] activate parent section [%s]", name(parent.top.hash), sectionName(parent))
|
poolLogger.Debugf("[%s] activate parent section [%s]", name(parent.top.hash), sectionName(parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
if section != nil {
|
if sec != nil {
|
||||||
peer.addSection(section.top.hash, section)
|
peer.addSection(sec.top.hash, sec)
|
||||||
section.controlC <- peer
|
// request next section here once, only repeat if bottom block arrives,
|
||||||
poolLogger.Debugf("[%s] activate new section", sectionName(section))
|
// 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
|
// only the first PoW-valid block for a hash is considered legit
|
||||||
func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
||||||
hash := block.Hash()
|
hash := block.Hash()
|
||||||
if self.hasBlock(hash) {
|
self.peersLock.Lock()
|
||||||
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
peer := self.peer
|
||||||
return
|
self.peersLock.Unlock()
|
||||||
}
|
|
||||||
entry := self.get(hash)
|
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 {
|
if entry == nil {
|
||||||
poolLogger.Warnf("unrequested block [%x] by peer %s", hash, peerId)
|
|
||||||
self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,17 +626,21 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
|||||||
|
|
||||||
// check if block already present
|
// check if block already present
|
||||||
if node.block != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate block for PoW
|
if self.hasBlock(hash) {
|
||||||
if !self.verifyPoW(block) {
|
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
||||||
poolLogger.Warnf("invalid pow on block [%x] by peer %s", hash, peerId)
|
} else {
|
||||||
self.peerError(peerId, ErrInvalidPoW, "%x", hash)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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)
|
poolLogger.Debugf("added block [%s] sent by peer %s", name(hash), peerId)
|
||||||
node.block = block
|
node.block = block
|
||||||
node.blockBy = peerId
|
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 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 turned back on it recursively calls itself on the root of the next chain section
|
||||||
// - when exits, signals to
|
// - 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 {
|
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)
|
self.set(node.hash, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
section.bottom = nodes[len(nodes)-1]
|
sec.bottom = nodes[len(nodes)-1]
|
||||||
section.top = nodes[0]
|
sec.top = nodes[0]
|
||||||
section.nodes = nodes
|
sec.nodes = nodes
|
||||||
poolLogger.DebugDetailf("[%s] setup section process", sectionName(section))
|
poolLogger.DebugDetailf("[%s] setup section process", sectionName(sec))
|
||||||
|
|
||||||
self.wg.Add(1)
|
self.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
||||||
// absolute time after which sub-chain is killed if not complete (some blocks are missing)
|
// 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
|
var peer, newPeer *peerInfo
|
||||||
|
|
||||||
@ -580,21 +767,23 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
var insertChain bool
|
var insertChain bool
|
||||||
var quitC chan bool
|
var quitC chan bool
|
||||||
|
|
||||||
var blockChainC = section.blockChainC
|
var blockChainC = sec.blockChainC
|
||||||
|
|
||||||
|
var parentHash []byte
|
||||||
|
|
||||||
LOOP:
|
LOOP:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
if insertChain {
|
if insertChain {
|
||||||
insertChain = false
|
insertChain = false
|
||||||
rest, err := self.addSectionToBlockChain(section)
|
rest, err := self.addSectionToBlockChain(sec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
close(section.suicideC)
|
close(sec.suicideC)
|
||||||
continue LOOP
|
continue LOOP
|
||||||
}
|
}
|
||||||
if rest == 0 {
|
if rest == 0 {
|
||||||
blocksRequestsComplete = true
|
blocksRequestsComplete = true
|
||||||
child := self.getChild(section)
|
child := self.getChild(sec)
|
||||||
if child != nil {
|
if child != nil {
|
||||||
self.connectToBlockChain(child)
|
self.connectToBlockChain(child)
|
||||||
}
|
}
|
||||||
@ -603,7 +792,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
|
|
||||||
if blockHashesRequestsComplete && blocksRequestsComplete {
|
if blockHashesRequestsComplete && blocksRequestsComplete {
|
||||||
// not waiting for hashes any more
|
// 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
|
break LOOP
|
||||||
} // otherwise suicide if no hashes coming
|
} // otherwise suicide if no hashes coming
|
||||||
|
|
||||||
@ -611,11 +800,12 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
// went through all blocks in section
|
// went through all blocks in section
|
||||||
if missing == 0 {
|
if missing == 0 {
|
||||||
// no missing blocks
|
// 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
|
blocksRequestsComplete = true
|
||||||
blocksRequestTimer = nil
|
blocksRequestTimer = nil
|
||||||
blocksRequestTime = false
|
blocksRequestTime = false
|
||||||
} else {
|
} else {
|
||||||
|
poolLogger.DebugDetailf("[%s] section checked: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth)
|
||||||
// some missing blocks
|
// some missing blocks
|
||||||
blocksRequests++
|
blocksRequests++
|
||||||
if len(hashes) > 0 {
|
if len(hashes) > 0 {
|
||||||
@ -630,8 +820,8 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
idle++
|
idle++
|
||||||
// too many idle rounds
|
// too many idle rounds
|
||||||
if idle >= blocksRequestMaxIdleRounds {
|
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)
|
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(section.suicideC)
|
close(sec.suicideC)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
idle = 0
|
idle = 0
|
||||||
@ -653,22 +843,39 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
if ready && blocksRequestTime && !blocksRequestsComplete {
|
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)
|
blocksRequestTimer = time.After(blocksRequestInterval * time.Millisecond)
|
||||||
blocksRequestTime = false
|
blocksRequestTime = false
|
||||||
processC = offC
|
processC = offC
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockHashesRequestTime {
|
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
|
// 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
|
blockHashesRequestTimer = nil
|
||||||
blockHashesRequestsComplete = true
|
blockHashesRequestsComplete = true
|
||||||
} else {
|
} else {
|
||||||
blockHashesRequests++
|
blockHashesRequests++
|
||||||
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(section), blockHashesRequests)
|
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(sec), blockHashesRequests)
|
||||||
peer.requestBlockHashes(section.bottom.hash)
|
peer.requestBlockHashes(sec.bottom.hash)
|
||||||
blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Millisecond)
|
blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Millisecond)
|
||||||
}
|
}
|
||||||
blockHashesRequestTime = false
|
blockHashesRequestTime = false
|
||||||
@ -682,27 +889,27 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
// peer quit or demoted, put section in idle mode
|
// peer quit or demoted, put section in idle mode
|
||||||
quitC = nil
|
quitC = nil
|
||||||
go func() {
|
go func() {
|
||||||
section.controlC <- nil
|
sec.controlC <- nil
|
||||||
}()
|
}()
|
||||||
|
|
||||||
case <-self.purgeC:
|
case <-self.purgeC:
|
||||||
suicideTimer = time.After(0)
|
suicideTimer = time.After(0)
|
||||||
|
|
||||||
case <-suicideTimer:
|
case <-suicideTimer:
|
||||||
close(section.suicideC)
|
close(sec.suicideC)
|
||||||
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||||
|
|
||||||
case <-section.suicideC:
|
case <-sec.suicideC:
|
||||||
poolLogger.Debugf("[%s] suicide", sectionName(section))
|
poolLogger.Debugf("[%s] suicide", sectionName(sec))
|
||||||
|
|
||||||
// first delink from child and parent under chainlock
|
// first delink from child and parent under chainlock
|
||||||
self.chainLock.Lock()
|
self.chainLock.Lock()
|
||||||
self.link(nil, section)
|
self.link(nil, sec)
|
||||||
self.link(section, nil)
|
self.link(sec, nil)
|
||||||
self.chainLock.Unlock()
|
self.chainLock.Unlock()
|
||||||
// delete node entries from pool index under pool lock
|
// delete node entries from pool index under pool lock
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
for _, node := range section.nodes {
|
for _, node := range sec.nodes {
|
||||||
delete(self.pool, string(node.hash))
|
delete(self.pool, string(node.hash))
|
||||||
}
|
}
|
||||||
self.lock.Unlock()
|
self.lock.Unlock()
|
||||||
@ -710,20 +917,20 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
break LOOP
|
break LOOP
|
||||||
|
|
||||||
case <-blocksRequestTimer:
|
case <-blocksRequestTimer:
|
||||||
poolLogger.DebugDetailf("[%s] block request time", sectionName(section))
|
poolLogger.DebugDetailf("[%s] block request time", sectionName(sec))
|
||||||
blocksRequestTime = true
|
blocksRequestTime = true
|
||||||
|
|
||||||
case <-blockHashesRequestTimer:
|
case <-blockHashesRequestTimer:
|
||||||
poolLogger.DebugDetailf("[%s] hash request time", sectionName(section))
|
poolLogger.DebugDetailf("[%s] hash request time", sectionName(sec))
|
||||||
blockHashesRequestTime = true
|
blockHashesRequestTime = true
|
||||||
|
|
||||||
case newPeer = <-section.controlC:
|
case newPeer = <-sec.controlC:
|
||||||
|
|
||||||
// active -> idle
|
// active -> idle
|
||||||
if peer != nil && newPeer == nil {
|
if peer != nil && newPeer == nil {
|
||||||
self.procWg.Done()
|
self.procWg.Done()
|
||||||
if init {
|
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
|
blocksRequestTime = false
|
||||||
blocksRequestTimer = nil
|
blocksRequestTimer = nil
|
||||||
@ -739,11 +946,11 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
if peer == nil && newPeer != nil {
|
if peer == nil && newPeer != nil {
|
||||||
self.procWg.Add(1)
|
self.procWg.Add(1)
|
||||||
|
|
||||||
poolLogger.Debugf("[%s] active mode", sectionName(section))
|
poolLogger.Debugf("[%s] active mode", sectionName(sec))
|
||||||
if !blocksRequestsComplete {
|
if !blocksRequestsComplete {
|
||||||
blocksRequestTime = true
|
blocksRequestTime = true
|
||||||
}
|
}
|
||||||
if !blockHashesRequestsComplete {
|
if !blockHashesRequestsComplete && parentHash != nil {
|
||||||
blockHashesRequestTime = true
|
blockHashesRequestTime = true
|
||||||
}
|
}
|
||||||
if !init {
|
if !init {
|
||||||
@ -753,13 +960,13 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
missing = 0
|
missing = 0
|
||||||
self.wg.Add(1)
|
self.wg.Add(1)
|
||||||
self.procWg.Add(1)
|
self.procWg.Add(1)
|
||||||
depth = len(section.nodes)
|
depth = len(sec.nodes)
|
||||||
lastMissing = depth
|
lastMissing = depth
|
||||||
// if not run at least once fully, launch iterator
|
// if not run at least once fully, launch iterator
|
||||||
go func() {
|
go func() {
|
||||||
var node *poolNode
|
var node *poolNode
|
||||||
IT:
|
IT:
|
||||||
for _, node = range section.nodes {
|
for _, node = range sec.nodes {
|
||||||
select {
|
select {
|
||||||
case processC <- node:
|
case processC <- node:
|
||||||
case <-self.quit:
|
case <-self.quit:
|
||||||
@ -771,7 +978,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
self.procWg.Done()
|
self.procWg.Done()
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
poolLogger.Debugf("[%s] restore earlier state", sectionName(section))
|
poolLogger.Debugf("[%s] restore earlier state", sectionName(sec))
|
||||||
processC = offC
|
processC = offC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -781,7 +988,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
}
|
}
|
||||||
peer = newPeer
|
peer = newPeer
|
||||||
|
|
||||||
case waiter := <-section.forkC:
|
case waiter := <-sec.forkC:
|
||||||
// this case just blocks the process until section is split at the fork
|
// this case just blocks the process until section is split at the fork
|
||||||
<-waiter
|
<-waiter
|
||||||
init = false
|
init = false
|
||||||
@ -794,7 +1001,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
init = true
|
init = true
|
||||||
done = true
|
done = true
|
||||||
processC = make(chan *poolNode, missing)
|
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
|
continue LOOP
|
||||||
}
|
}
|
||||||
if ready {
|
if ready {
|
||||||
@ -811,17 +1018,24 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
missing++
|
missing++
|
||||||
hashes = append(hashes, node.hash)
|
hashes = append(hashes, node.hash)
|
||||||
if len(hashes) == blockBatchSize {
|
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)
|
self.requestBlocks(blocksRequests, hashes)
|
||||||
hashes = nil
|
hashes = nil
|
||||||
}
|
}
|
||||||
missingC <- node
|
missingC <- node
|
||||||
} else {
|
} else {
|
||||||
if blockChainC == nil && i == lastMissing {
|
if i == lastMissing {
|
||||||
insertChain = true
|
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 {
|
if i == lastMissing && init {
|
||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
@ -829,23 +1043,22 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||||||
case <-blockChainC:
|
case <-blockChainC:
|
||||||
// closed blockChain channel indicates that the blockpool is reached
|
// closed blockChain channel indicates that the blockpool is reached
|
||||||
// connected to the blockchain, insert the longest chain of blocks
|
// 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
|
blockChainC = nil
|
||||||
// switch off hash requests in case they were on
|
// switch off hash requests in case they were on
|
||||||
blockHashesRequestTime = false
|
blockHashesRequestTime = false
|
||||||
blockHashesRequestTimer = nil
|
blockHashesRequestTimer = nil
|
||||||
blockHashesRequestsComplete = true
|
blockHashesRequestsComplete = true
|
||||||
// section root has block
|
// 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
|
insertChain = true
|
||||||
}
|
}
|
||||||
continue LOOP
|
continue LOOP
|
||||||
|
|
||||||
} // select
|
} // select
|
||||||
} // for
|
} // 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()
|
self.wg.Done()
|
||||||
if peer != nil {
|
if peer != nil {
|
||||||
@ -917,22 +1130,28 @@ func (self *peerInfo) addSection(hash []byte, section *section) (found *section)
|
|||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
key := string(hash)
|
key := string(hash)
|
||||||
found = self.sections[key]
|
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
|
self.sections[key] = section
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
||||||
if newPeer != nil {
|
if newPeer != nil {
|
||||||
entry := self.get(newPeer.currentBlock)
|
newPeer.quitC = make(chan bool)
|
||||||
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)
|
|
||||||
}
|
|
||||||
poolLogger.DebugDetailf("[%s] activate section processes", newPeer.id)
|
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 {
|
for hash, section := range newPeer.sections {
|
||||||
// this will block if section process is waiting for peer lock
|
// this will block if section process is waiting for peer lock
|
||||||
select {
|
select {
|
||||||
@ -940,12 +1159,26 @@ func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
|||||||
poolLogger.DebugDetailf("[%s][%x] section process complete - remove", newPeer.id, hash[:4])
|
poolLogger.DebugDetailf("[%s][%x] section process complete - remove", newPeer.id, hash[:4])
|
||||||
delete(newPeer.sections, hash)
|
delete(newPeer.sections, hash)
|
||||||
case section.controlC <- newPeer:
|
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 {
|
if oldPeer != nil {
|
||||||
|
poolLogger.DebugDetailf("[%s] quit section processes", oldPeer.id)
|
||||||
close(oldPeer.quitC)
|
close(oldPeer.quitC)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
const waitTimeout = 60 // seconds
|
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
|
var ini = false
|
||||||
|
|
||||||
@ -336,12 +336,12 @@ func (self *peerTester) AddPeer() bool {
|
|||||||
|
|
||||||
// peer sends blockhashes if and when gets a request
|
// peer sends blockhashes if and when gets a request
|
||||||
func (self *peerTester) AddBlockHashes(indexes ...int) {
|
func (self *peerTester) AddBlockHashes(indexes ...int) {
|
||||||
i := 0
|
|
||||||
fmt.Printf("ready to add block hashes %v\n", indexes)
|
fmt.Printf("ready to add block hashes %v\n", indexes)
|
||||||
|
|
||||||
self.waitBlockHashesRequests(indexes[0])
|
self.waitBlockHashesRequests(indexes[0])
|
||||||
fmt.Printf("adding block hashes %v\n", indexes)
|
fmt.Printf("adding block hashes %v\n", indexes)
|
||||||
hashes := self.hashPool.indexesToHashes(indexes)
|
hashes := self.hashPool.indexesToHashes(indexes)
|
||||||
|
i := 1
|
||||||
next := func() (hash []byte, ok bool) {
|
next := func() (hash []byte, ok bool) {
|
||||||
if i < len(hashes) {
|
if i < len(hashes) {
|
||||||
hash = hashes[i]
|
hash = hashes[i]
|
||||||
@ -415,7 +415,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.id != "peer0" {
|
if blockPool.peer.id != "peer0" {
|
||||||
t.Errorf("peer0 (TD=1) not set as best")
|
t.Errorf("peer0 (TD=1) not set as best")
|
||||||
}
|
}
|
||||||
peer0.checkBlockHashesRequests(0)
|
// peer0.checkBlockHashesRequests(0)
|
||||||
|
|
||||||
best = peer2.AddPeer()
|
best = peer2.AddPeer()
|
||||||
if !best {
|
if !best {
|
||||||
@ -424,7 +424,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.id != "peer2" {
|
if blockPool.peer.id != "peer2" {
|
||||||
t.Errorf("peer2 (TD=3) not set as best")
|
t.Errorf("peer2 (TD=3) not set as best")
|
||||||
}
|
}
|
||||||
peer2.checkBlockHashesRequests(2)
|
peer2.waitBlocksRequests(2)
|
||||||
|
|
||||||
best = peer1.AddPeer()
|
best = peer1.AddPeer()
|
||||||
if best {
|
if best {
|
||||||
@ -449,7 +449,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 {
|
if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 {
|
||||||
t.Errorf("peer2 TD not updated")
|
t.Errorf("peer2 TD not updated")
|
||||||
}
|
}
|
||||||
peer2.checkBlockHashesRequests(2, 3)
|
peer2.waitBlocksRequests(3)
|
||||||
|
|
||||||
peer1.td = 3
|
peer1.td = 3
|
||||||
peer1.currentBlock = 2
|
peer1.currentBlock = 2
|
||||||
@ -474,7 +474,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.id != "peer1" {
|
if blockPool.peer.id != "peer1" {
|
||||||
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
||||||
}
|
}
|
||||||
peer1.checkBlockHashesRequests(2)
|
peer1.waitBlocksRequests(2)
|
||||||
|
|
||||||
blockPool.RemovePeer("peer1")
|
blockPool.RemovePeer("peer1")
|
||||||
peer, best = blockPool.getPeer("peer1")
|
peer, best = blockPool.getPeer("peer1")
|
||||||
@ -485,6 +485,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.id != "peer0" {
|
if blockPool.peer.id != "peer0" {
|
||||||
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
||||||
}
|
}
|
||||||
|
peer0.waitBlocksRequests(0)
|
||||||
|
|
||||||
blockPool.RemovePeer("peer0")
|
blockPool.RemovePeer("peer0")
|
||||||
peer, best = blockPool.getPeer("peer0")
|
peer, best = blockPool.getPeer("peer0")
|
||||||
@ -502,7 +503,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
if blockPool.peer.id != "peer0" {
|
if blockPool.peer.id != "peer0" {
|
||||||
t.Errorf("peer0 (TD=1) should be set as best")
|
t.Errorf("peer0 (TD=1) should be set as best")
|
||||||
}
|
}
|
||||||
peer0.checkBlockHashesRequests(0, 0, 3)
|
peer0.waitBlocksRequests(3)
|
||||||
|
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
|
|
||||||
@ -513,17 +514,36 @@ func TestPeerWithKnownBlock(t *testing.T) {
|
|||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||||
blockPoolTester.refBlockChain[0] = nil
|
blockPoolTester.refBlockChain[0] = nil
|
||||||
blockPoolTester.blockChain[0] = nil
|
blockPoolTester.blockChain[0] = nil
|
||||||
// hashPool, blockPool, blockPoolTester := newTestBlockPool()
|
|
||||||
blockPool.Start()
|
blockPool.Start()
|
||||||
|
|
||||||
peer0 := blockPoolTester.newPeer("0", 1, 0)
|
peer0 := blockPoolTester.newPeer("0", 1, 0)
|
||||||
peer0.AddPeer()
|
peer0.AddPeer()
|
||||||
|
|
||||||
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
// no request on known block
|
// no request on known block
|
||||||
peer0.checkBlockHashesRequests()
|
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) {
|
func TestSimpleChain(t *testing.T) {
|
||||||
logInit()
|
logInit()
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||||
@ -534,8 +554,9 @@ func TestSimpleChain(t *testing.T) {
|
|||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
peer1.AddBlocks(1, 2)
|
||||||
go peer1.AddBlockHashes(2, 1, 0)
|
go peer1.AddBlockHashes(2, 1, 0)
|
||||||
peer1.AddBlocks(0, 1, 2)
|
peer1.AddBlocks(0, 1)
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
@ -543,6 +564,26 @@ func TestSimpleChain(t *testing.T) {
|
|||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
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) {
|
func TestInvalidBlock(t *testing.T) {
|
||||||
logInit()
|
logInit()
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||||
@ -554,8 +595,9 @@ func TestInvalidBlock(t *testing.T) {
|
|||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(2, 3)
|
||||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||||
peer1.AddBlocks(0, 1, 2, 3)
|
peer1.AddBlocks(0, 1, 2)
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
@ -566,7 +608,7 @@ func TestInvalidBlock(t *testing.T) {
|
|||||||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock)
|
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock)
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
blockPoolTester.blockPool.verifyPoW = func(b pow.Block) bool {
|
||||||
bb, _ := b.(*types.Block)
|
bb, _ := b.(*types.Block)
|
||||||
indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()})
|
indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()})
|
||||||
if indexes[0] == 1 && !first {
|
if indexes[0] == 2 && !first {
|
||||||
first = true
|
first = true
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
@ -590,15 +632,17 @@ func TestVerifyPoW(t *testing.T) {
|
|||||||
|
|
||||||
blockPool.Start()
|
blockPool.Start()
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||||
peer1.AddPeer()
|
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, 2)
|
||||||
peer1.AddBlocks(0, 1)
|
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
// blockPool.Wait(waitTimeout * time.Second)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
blockPoolTester.refBlockChain[2] = []int{}
|
blockPoolTester.refBlockChain[1] = []int{}
|
||||||
|
delete(blockPoolTester.refBlockChain, 2)
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||||
if len(peer1.peerErrors) == 1 {
|
if len(peer1.peerErrors) == 1 {
|
||||||
if peer1.peerErrors[0] != ErrInvalidPoW {
|
if peer1.peerErrors[0] != ErrInvalidPoW {
|
||||||
@ -620,8 +664,9 @@ func TestMultiSectionChain(t *testing.T) {
|
|||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(4, 5)
|
||||||
go peer1.AddBlockHashes(5, 4, 3)
|
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)
|
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||||
peer1.AddBlocks(0, 1, 2)
|
peer1.AddBlocks(0, 1, 2)
|
||||||
|
|
||||||
@ -641,14 +686,17 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
|||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(4, 5) // partially complete section
|
||||||
go peer1.AddBlockHashes(5, 4, 3)
|
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 found new blocks
|
||||||
peer1.td = 2
|
peer1.td = 2
|
||||||
peer1.currentBlock = 7
|
peer1.currentBlock = 7
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(6, 7)
|
||||||
go peer1.AddBlockHashes(7, 6, 5)
|
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
|
go peer1.AddBlockHashes(3, 2, 1, 0) // tests that hash request from known chain root is remembered
|
||||||
peer1.AddBlocks(0, 1, 2)
|
peer1.AddBlocks(0, 1, 2)
|
||||||
|
|
||||||
@ -658,35 +706,37 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
|||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPeerSwitch(t *testing.T) {
|
func TestPeerSwitchUp(t *testing.T) {
|
||||||
logInit()
|
logInit()
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||||
blockPoolTester.blockChain[0] = nil
|
blockPoolTester.blockChain[0] = nil
|
||||||
blockPoolTester.initRefBlockChain(6)
|
blockPoolTester.initRefBlockChain(7)
|
||||||
|
|
||||||
blockPool.Start()
|
blockPool.Start()
|
||||||
|
|
||||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
peer1 := blockPoolTester.newPeer("peer1", 1, 6)
|
||||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
peer2 := blockPoolTester.newPeer("peer2", 2, 7)
|
||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer1.AddPeer()
|
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
|
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
|
peer2.AddPeer() // peer2 is promoted as best peer, peer1 is demoted
|
||||||
go peer2.AddBlockHashes(6, 5) //
|
go peer2.AddBlocks(6, 7)
|
||||||
go peer2.AddBlocks(4, 5, 6) // tests that block request for earlier section is remembered
|
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 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
|
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
|
peer2.AddBlocks(0, 1, 2) // final blocks linking to blockchain sent
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
blockPoolTester.refBlockChain[6] = []int{}
|
blockPoolTester.refBlockChain[7] = []int{}
|
||||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPeerDownSwitch(t *testing.T) {
|
func TestPeerSwitchDown(t *testing.T) {
|
||||||
logInit()
|
logInit()
|
||||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||||
blockPoolTester.blockChain[0] = nil
|
blockPoolTester.blockChain[0] = nil
|
||||||
@ -698,12 +748,39 @@ func TestPeerDownSwitch(t *testing.T) {
|
|||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer2.AddPeer()
|
peer2.AddPeer()
|
||||||
go peer2.AddBlockHashes(6, 5, 4)
|
|
||||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
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
|
blockPool.RemovePeer("peer2") // peer2 disconnects
|
||||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
||||||
go peer1.AddBlockHashes(4, 3, 2, 1, 0) //
|
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)
|
peer1.AddBlocks(0, 1, 2, 3)
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
@ -725,11 +802,13 @@ func TestPeerSwitchBack(t *testing.T) {
|
|||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer2.AddPeer()
|
peer2.AddPeer()
|
||||||
|
go peer2.AddBlocks(7, 8)
|
||||||
go peer2.AddBlockHashes(8, 7, 6)
|
go peer2.AddBlockHashes(8, 7, 6)
|
||||||
go peer2.AddBlockHashes(6, 5, 4)
|
go peer2.AddBlockHashes(6, 5, 4)
|
||||||
peer2.AddBlocks(4, 5) // section partially complete
|
peer2.AddBlocks(4, 5) // section partially complete
|
||||||
peer1.AddPeer() // peer1 is promoted as best peer
|
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
|
blockPool.RemovePeer("peer1") // peer1 disconnects
|
||||||
go peer2.AddBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
|
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
|
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
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(8, 9)
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
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
|
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.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)
|
go peer2.AddBlockHashes(2, 1, 0)
|
||||||
peer2.AddBlocks(0, 1, 2)
|
peer2.AddBlocks(0, 1, 2)
|
||||||
|
|
||||||
@ -790,23 +871,24 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
|||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
peer1.AddBlocks(8, 9) //
|
||||||
peer1.AddBlocks(8, 9) // partial section
|
go peer1.AddBlockHashes(9, 8, 7, 3, 2) //
|
||||||
|
peer1.AddBlocks(7, 8) // partial section
|
||||||
peer2.AddPeer() //
|
peer2.AddPeer() //
|
||||||
|
peer2.AddBlocks(5, 6) //
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
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 finds new blocks
|
||||||
peer1.td = 3
|
peer1.td = 3
|
||||||
peer1.currentBlock = 11
|
peer1.currentBlock = 11
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(10, 11)
|
||||||
go peer1.AddBlockHashes(11, 10, 9)
|
go peer1.AddBlockHashes(11, 10, 9)
|
||||||
peer1.AddBlocks(7, 8, 9, 10, 11)
|
peer1.AddBlocks(9, 10)
|
||||||
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.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
|
|
||||||
go peer1.AddBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is 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.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
@ -834,16 +916,18 @@ func TestForkSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(8, 9)
|
||||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||||
peer1.AddBlocks(8, 9)
|
peer1.AddBlocks(7, 8)
|
||||||
peer2.AddPeer() //
|
peer2.AddPeer()
|
||||||
|
go peer2.AddBlocks(5, 6) //
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
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
|
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) // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
|
||||||
go peer1.AddBlocks(3, 7, 8) // tests that block requests on earlier fork are remembered
|
go peer1.AddBlocks(1, 2) //
|
||||||
go peer1.AddBlockHashes(2, 1, 0) //
|
go peer1.AddBlockHashes(2, 1, 0) //
|
||||||
peer1.AddBlocks(0, 1, 2, 3)
|
peer1.AddBlocks(0, 1)
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
blockPool.Stop()
|
blockPool.Stop()
|
||||||
@ -871,17 +955,19 @@ func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||||
|
|
||||||
peer1.AddPeer()
|
peer1.AddPeer()
|
||||||
|
go peer1.AddBlocks(8, 9)
|
||||||
go peer1.AddBlockHashes(9, 8, 7)
|
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)
|
time.Sleep(1 * time.Second)
|
||||||
go peer1.AddBlockHashes(7, 3, 2) // block 3/7 is section boundary
|
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() //
|
peer2.AddPeer() //
|
||||||
|
go peer2.AddBlocks(5, 6) //
|
||||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
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
|
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
|
// peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
||||||
go peer1.AddBlockHashes(2, 1, 0) //
|
go peer1.AddBlockHashes(2, 1, 0) //
|
||||||
peer1.AddBlocks(0, 1, 2)
|
peer1.AddBlocks(0, 1, 2)
|
||||||
|
|
||||||
blockPool.Wait(waitTimeout * time.Second)
|
blockPool.Wait(waitTimeout * time.Second)
|
||||||
|
@ -16,6 +16,7 @@ const (
|
|||||||
ErrInvalidBlock
|
ErrInvalidBlock
|
||||||
ErrInvalidPoW
|
ErrInvalidPoW
|
||||||
ErrUnrequestedBlock
|
ErrUnrequestedBlock
|
||||||
|
ErrInsufficientChainInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
var errorToString = map[int]string{
|
var errorToString = map[int]string{
|
||||||
@ -30,6 +31,7 @@ var errorToString = map[int]string{
|
|||||||
ErrInvalidBlock: "Invalid block",
|
ErrInvalidBlock: "Invalid block",
|
||||||
ErrInvalidPoW: "Invalid PoW",
|
ErrInvalidPoW: "Invalid PoW",
|
||||||
ErrUnrequestedBlock: "Unrequested block",
|
ErrUnrequestedBlock: "Unrequested block",
|
||||||
|
ErrInsufficientChainInfo: "Insufficient chain info",
|
||||||
}
|
}
|
||||||
|
|
||||||
type protocolError struct {
|
type protocolError struct {
|
||||||
|
@ -67,6 +67,8 @@ type newBlockMsgData struct {
|
|||||||
TD *big.Int
|
TD *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxHashes = 255
|
||||||
|
|
||||||
type getBlockHashesMsgData struct {
|
type getBlockHashesMsgData struct {
|
||||||
Hash []byte
|
Hash []byte
|
||||||
Amount uint64
|
Amount uint64
|
||||||
@ -122,7 +124,7 @@ func (self *ethProtocol) handle() error {
|
|||||||
defer msg.Discard()
|
defer msg.Discard()
|
||||||
|
|
||||||
switch msg.Code {
|
switch msg.Code {
|
||||||
|
case GetTxMsg: // ignore
|
||||||
case StatusMsg:
|
case StatusMsg:
|
||||||
return self.protoError(ErrExtraStatusMsg, "")
|
return self.protoError(ErrExtraStatusMsg, "")
|
||||||
|
|
||||||
@ -139,8 +141,13 @@ func (self *ethProtocol) handle() error {
|
|||||||
if err := msg.Decode(&request); err != nil {
|
if err := msg.Decode(&request); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
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)
|
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:
|
case BlockHashesMsg:
|
||||||
// TODO: redo using lazy decode , this way very inefficient on known chains
|
// TODO: redo using lazy decode , this way very inefficient on known chains
|
||||||
@ -185,7 +192,7 @@ func (self *ethProtocol) handle() error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.rw.EncodeMsg(BlocksMsg, blocks...)
|
return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...)
|
||||||
|
|
||||||
case BlocksMsg:
|
case BlocksMsg:
|
||||||
msgStream := rlp.NewStream(msg.Payload)
|
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
|
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
|
||||||
// (or selected as new best peer)
|
// (or selected as new best peer)
|
||||||
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
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)
|
self.blockPool.AddBlock(request.Block, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,12 +295,12 @@ func (self *ethProtocol) handleStatus() error {
|
|||||||
|
|
||||||
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
||||||
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
|
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 {
|
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
||||||
self.peer.Debugf("fetching %v blocks", len(hashes))
|
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) {
|
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
|
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) {
|
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||||
msg, ok := <-self.in
|
msg, ok := <-self.in
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -12,8 +12,8 @@ EOF
|
|||||||
|
|
||||||
peer 11 01
|
peer 11 01
|
||||||
peer 12 02
|
peer 12 02
|
||||||
P13ID=$PID
|
P12ID=$PID
|
||||||
test_node $NAME "" -loglevel 5 $JSFILE
|
test_node $NAME "" -loglevel 5 $JSFILE
|
||||||
sleep 0.5
|
sleep 0.3
|
||||||
kill $P13ID
|
kill $P12ID
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
TIMEOUT=35
|
TIMEOUT=12
|
||||||
|
|
||||||
cat >> $JSFILE <<EOF
|
cat >> $JSFILE <<EOF
|
||||||
eth.addPeer("localhost:30311");
|
eth.addPeer("localhost:30311");
|
||||||
sleep(30000);
|
sleep(10000);
|
||||||
eth.export("$CHAIN_TEST");
|
eth.export("$CHAIN_TEST");
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
# launched by run.sh
|
# launched by run.sh
|
||||||
function test_node {
|
function test_node {
|
||||||
rm -rf $DIR/$1
|
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
|
if [ "" != "$2" ]; then
|
||||||
chain="chains/$2.chain"
|
chain="chains/$2.chain"
|
||||||
echo "import chain $chain"
|
echo "import chain $chain"
|
||||||
$ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import
|
$ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import
|
||||||
fi
|
fi
|
||||||
echo "starting test node $1 with extra args ${@:3}"
|
echo "starting test node $1 with args $ARGS ${@:3}"
|
||||||
$ETH $ARGS -port 303$1 ${@:3} &
|
$ETH $ARGS ${@:3} &
|
||||||
PID=$!
|
PID=$!
|
||||||
PIDS="$PIDS $PID"
|
PIDS="$PIDS $PID"
|
||||||
}
|
}
|
||||||
|
@ -84,4 +84,5 @@ var (
|
|||||||
BigFalse = Big0
|
BigFalse = Big0
|
||||||
Big32 = big.NewInt(32)
|
Big32 = big.NewInt(32)
|
||||||
Big256 = big.NewInt(0xff)
|
Big256 = big.NewInt(0xff)
|
||||||
|
Big257 = big.NewInt(257)
|
||||||
)
|
)
|
||||||
|
@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
// Config struct
|
// Config struct
|
||||||
type ConfigManager struct {
|
type ConfigManager struct {
|
||||||
Db Database
|
|
||||||
|
|
||||||
ExecPath string
|
ExecPath string
|
||||||
Debug bool
|
Debug bool
|
||||||
Diff bool
|
Diff bool
|
||||||
|
@ -12,7 +12,7 @@ func ExpandHomePath(p string) (path string) {
|
|||||||
path = p
|
path = p
|
||||||
|
|
||||||
// Check in case of paths like "/something/~/something/"
|
// Check in case of paths like "/something/~/something/"
|
||||||
if path[:2] == "~/" {
|
if len(path) > 1 && path[:2] == "~/" {
|
||||||
usr, _ := user.Current()
|
usr, _ := user.Current()
|
||||||
dir := usr.HomeDir
|
dir := usr.HomeDir
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ func Encode(object interface{}) []byte {
|
|||||||
case byte:
|
case byte:
|
||||||
buff.Write(Encode(big.NewInt(int64(t))))
|
buff.Write(Encode(big.NewInt(int64(t))))
|
||||||
case *big.Int:
|
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 {
|
if t == nil {
|
||||||
buff.WriteByte(0xc0)
|
buff.WriteByte(0xc0)
|
||||||
} else {
|
} 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 {
|
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
||||||
var state *state.StateDB
|
var block *types.Block
|
||||||
|
|
||||||
if len(call.ArgumentList) > 0 {
|
if len(call.ArgumentList) > 0 {
|
||||||
var block *types.Block
|
|
||||||
if call.Argument(0).IsNumber() {
|
if call.Argument(0).IsNumber() {
|
||||||
num, _ := call.Argument(0).ToInteger()
|
num, _ := call.Argument(0).ToInteger()
|
||||||
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
|
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
|
||||||
@ -149,12 +148,12 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
|||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
state = block.State()
|
|
||||||
} else {
|
} 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
|
return v
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -69,6 +70,7 @@ type Miner struct {
|
|||||||
mining bool
|
mining bool
|
||||||
|
|
||||||
MinAcceptedGasPrice *big.Int
|
MinAcceptedGasPrice *big.Int
|
||||||
|
Extra string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(coinbase []byte, eth *eth.Ethereum) *Miner {
|
func New(coinbase []byte, eth *eth.Ethereum) *Miner {
|
||||||
@ -177,7 +179,9 @@ func (self *Miner) mine() {
|
|||||||
blockProcessor = self.eth.BlockProcessor()
|
blockProcessor = self.eth.BlockProcessor()
|
||||||
chainMan = self.eth.ChainManager()
|
chainMan = self.eth.ChainManager()
|
||||||
block = chainMan.NewBlock(self.Coinbase)
|
block = chainMan.NewBlock(self.Coinbase)
|
||||||
|
state = state.New(block.Root(), self.eth.Db())
|
||||||
)
|
)
|
||||||
|
block.Header().Extra = self.Extra
|
||||||
|
|
||||||
// Apply uncles
|
// Apply uncles
|
||||||
if len(self.uncles) > 0 {
|
if len(self.uncles) > 0 {
|
||||||
@ -185,13 +189,11 @@ func (self *Miner) mine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parent := chainMan.GetBlock(block.ParentHash())
|
parent := chainMan.GetBlock(block.ParentHash())
|
||||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
coinbase := state.GetOrNewStateObject(block.Coinbase())
|
||||||
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||||
|
|
||||||
transactions := self.finiliseTxs()
|
transactions := self.finiliseTxs()
|
||||||
|
|
||||||
state := block.State()
|
|
||||||
|
|
||||||
// Accumulate all valid transactions and apply them to the new state
|
// Accumulate all valid transactions and apply them to the new state
|
||||||
// Error may be ignored. It's not important during mining
|
// Error may be ignored. It's not important during mining
|
||||||
receipts, txs, _, erroneous, err := blockProcessor.ApplyTransactions(coinbase, state, block, transactions, true)
|
receipts, txs, _, erroneous, err := blockProcessor.ApplyTransactions(coinbase, state, block, transactions, true)
|
||||||
|
@ -71,14 +71,11 @@ type MsgReader interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MsgWriter interface {
|
type MsgWriter interface {
|
||||||
// WriteMsg sends an existing message.
|
// WriteMsg sends a message. It will block until the message's
|
||||||
// The Payload reader of the message is consumed.
|
// Payload has been consumed by the other end.
|
||||||
|
//
|
||||||
// Note that messages can be sent only once.
|
// Note that messages can be sent only once.
|
||||||
WriteMsg(Msg) error
|
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.
|
// MsgReadWriter provides reading and writing of encoded messages.
|
||||||
@ -87,6 +84,12 @@ type MsgReadWriter interface {
|
|||||||
MsgWriter
|
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}
|
var magicToken = []byte{34, 64, 8, 145}
|
||||||
|
|
||||||
func writeMsg(w io.Writer, msg Msg) error {
|
func writeMsg(w io.Writer, msg Msg) error {
|
||||||
@ -209,11 +212,6 @@ func (p *MsgPipeRW) WriteMsg(msg Msg) error {
|
|||||||
return ErrPipeClosed
|
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.
|
// ReadMsg returns a message sent on the other end of the pipe.
|
||||||
func (p *MsgPipeRW) ReadMsg() (Msg, error) {
|
func (p *MsgPipeRW) ReadMsg() (Msg, error) {
|
||||||
if atomic.LoadInt32(p.closed) == 0 {
|
if atomic.LoadInt32(p.closed) == 0 {
|
||||||
|
@ -75,8 +75,8 @@ func TestDecodeRealMsg(t *testing.T) {
|
|||||||
func ExampleMsgPipe() {
|
func ExampleMsgPipe() {
|
||||||
rw1, rw2 := MsgPipe()
|
rw1, rw2 := MsgPipe()
|
||||||
go func() {
|
go func() {
|
||||||
rw1.EncodeMsg(8, []byte{0, 0})
|
EncodeMsg(rw1, 8, []byte{0, 0})
|
||||||
rw1.EncodeMsg(5, []byte{1, 1})
|
EncodeMsg(rw1, 5, []byte{1, 1})
|
||||||
rw1.Close()
|
rw1.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ loop:
|
|||||||
rw1, rw2 := MsgPipe()
|
rw1, rw2 := MsgPipe()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
if err := rw1.EncodeMsg(1); err == nil {
|
if err := EncodeMsg(rw1, 1); err == nil {
|
||||||
t.Error("EncodeMsg returned nil error")
|
t.Error("EncodeMsg returned nil error")
|
||||||
} else if err != ErrPipeClosed {
|
} else if err != ErrPipeClosed {
|
||||||
t.Error("EncodeMsg returned wrong error: got %v, want %v", 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
|
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",
|
Name: "a",
|
||||||
Length: 2,
|
Length: 2,
|
||||||
Run: func(peer *Peer, rw MsgReadWriter) error {
|
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")
|
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)
|
t.Errorf("write error: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -119,14 +119,14 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
|||||||
|
|
||||||
getPeersTick := time.NewTicker(10 * time.Second)
|
getPeersTick := time.NewTicker(10 * time.Second)
|
||||||
defer getPeersTick.Stop()
|
defer getPeersTick.Stop()
|
||||||
err := bp.rw.EncodeMsg(getPeersMsg)
|
err := EncodeMsg(bp.rw, getPeersMsg)
|
||||||
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
select {
|
select {
|
||||||
case err = <-quit:
|
case err = <-quit:
|
||||||
return err
|
return err
|
||||||
case <-getPeersTick.C:
|
case <-getPeersTick.C:
|
||||||
err = bp.rw.EncodeMsg(getPeersMsg)
|
err = EncodeMsg(bp.rw, getPeersMsg)
|
||||||
case event := <-activity.Chan():
|
case event := <-activity.Chan():
|
||||||
ping.Reset(pingTimeout)
|
ping.Reset(pingTimeout)
|
||||||
lastActive = event.(time.Time)
|
lastActive = event.(time.Time)
|
||||||
@ -134,7 +134,7 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
|||||||
if lastActive.Add(pingTimeout * 2).Before(t) {
|
if lastActive.Add(pingTimeout * 2).Before(t) {
|
||||||
err = newPeerError(errPingTimeout, "")
|
err = newPeerError(errPingTimeout, "")
|
||||||
} else if lastActive.Add(pingTimeout).Before(t) {
|
} 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])
|
return discRequestedError(reason[0])
|
||||||
|
|
||||||
case pingMsg:
|
case pingMsg:
|
||||||
return bp.rw.EncodeMsg(pongMsg)
|
return EncodeMsg(bp.rw, pongMsg)
|
||||||
|
|
||||||
case pongMsg:
|
case pongMsg:
|
||||||
|
|
||||||
case getPeersMsg:
|
case getPeersMsg:
|
||||||
peers := bp.peer.PeerList()
|
peers := bp.peerList()
|
||||||
// this is dangerous. the spec says that we should _delay_
|
// this is dangerous. the spec says that we should _delay_
|
||||||
// sending the response if no new information is available.
|
// sending the response if no new information is available.
|
||||||
// this means that would need to send a response later when
|
// 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
|
// TODO: add event mechanism to notify baseProtocol for new peers
|
||||||
if len(peers) > 0 {
|
if len(peers) > 0 {
|
||||||
return bp.rw.EncodeMsg(peersMsg, peers...)
|
return EncodeMsg(bp.rw, peersMsg, peers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
case peersMsg:
|
case peersMsg:
|
||||||
@ -264,3 +264,25 @@ func (bp *baseProtocol) handshakeMsg() Msg {
|
|||||||
bp.peer.ourID.Pubkey()[1:],
|
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"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -36,50 +37,71 @@ func newTestPeer() (peer *Peer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseProtocolPeers(t *testing.T) {
|
func TestBaseProtocolPeers(t *testing.T) {
|
||||||
cannedPeerList := []*peerAddr{
|
peerList := []*peerAddr{
|
||||||
{IP: net.ParseIP("1.2.3.4"), Port: 2222, Pubkey: []byte{}},
|
{IP: net.ParseIP("1.2.3.4"), Port: 2222, Pubkey: []byte{}},
|
||||||
{IP: net.ParseIP("5.6.7.8"), Port: 3333, 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()
|
rw1, rw2 := MsgPipe()
|
||||||
|
defer rw1.Close()
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
|
||||||
// run matcher, close pipe when addresses have arrived
|
// 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() {
|
go func() {
|
||||||
for _, want := range cannedPeerList {
|
i := 0
|
||||||
got := <-addrChan
|
for got := range addrChan {
|
||||||
t.Logf("got peer: %+v", got)
|
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) {
|
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)
|
if i != numPeers {
|
||||||
var own []*peerAddr
|
t.Errorf("wrong number of peers received: got %d, want %d", i, numPeers)
|
||||||
var got *peerAddr
|
|
||||||
for got = range addrChan {
|
|
||||||
own = append(own, got)
|
|
||||||
}
|
}
|
||||||
if len(own) != 1 || !reflect.DeepEqual(ownAddr, own[0]) {
|
rw1.Close()
|
||||||
t.Errorf("mismatch: peers own address is incorrectly or not given, got %v, want %#v", ownAddr)
|
wg.Done()
|
||||||
}
|
|
||||||
rw2.Close()
|
|
||||||
}()
|
}()
|
||||||
// run first peer
|
|
||||||
|
// run first peer (in background)
|
||||||
peer1 := newTestPeer()
|
peer1 := newTestPeer()
|
||||||
peer1.ourListenAddr = ownAddr
|
peer1.ourListenAddr = listenAddr
|
||||||
peer1.otherPeers = func() []*Peer {
|
peer1.otherPeers = func() []*Peer {
|
||||||
pl := make([]*Peer, len(cannedPeerList))
|
pl := make([]*Peer, len(peerList))
|
||||||
for i, addr := range cannedPeerList {
|
for i, addr := range peerList {
|
||||||
pl[i] = &Peer{listenAddr: addr}
|
pl[i] = &Peer{listenAddr: addr}
|
||||||
}
|
}
|
||||||
return pl
|
return pl
|
||||||
}
|
}
|
||||||
go runBaseProtocol(peer1, rw1)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
runBaseProtocol(peer1, rw1)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
// run second peer
|
// run second peer
|
||||||
peer2 := newTestPeer()
|
peer2 := newTestPeer()
|
||||||
peer2.newPeerAddr = addrChan // feed peer suggestions into matcher
|
peer2.newPeerAddr = addrChan // feed peer suggestions into matcher
|
||||||
if err := runBaseProtocol(peer2, rw2); err != ErrPipeClosed {
|
if err := runBaseProtocol(peer2, rw2); err != ErrPipeClosed {
|
||||||
t.Errorf("peer2 terminated with unexpected error: %v", err)
|
t.Errorf("peer2 terminated with unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// terminate matcher
|
||||||
|
close(addrChan)
|
||||||
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseProtocolDisconnect(t *testing.T) {
|
func TestBaseProtocolDisconnect(t *testing.T) {
|
||||||
@ -93,7 +115,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
|||||||
if err := expectMsg(rw2, handshakeMsg); err != nil {
|
if err := expectMsg(rw2, handshakeMsg); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
err := rw2.EncodeMsg(handshakeMsg,
|
err := EncodeMsg(rw2, handshakeMsg,
|
||||||
baseProtocolVersion,
|
baseProtocolVersion,
|
||||||
"",
|
"",
|
||||||
[]interface{}{},
|
[]interface{}{},
|
||||||
@ -106,7 +128,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
|||||||
if err := expectMsg(rw2, getPeersMsg); err != nil {
|
if err := expectMsg(rw2, getPeersMsg); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if err := rw2.EncodeMsg(discMsg, DiscQuitting); err != nil {
|
if err := EncodeMsg(rw2, discMsg, DiscQuitting); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ func (srv *Server) dialLoop() {
|
|||||||
case desc := <-suggest:
|
case desc := <-suggest:
|
||||||
// candidate peer found, will dial out asyncronously
|
// candidate peer found, will dial out asyncronously
|
||||||
// if connection fails slot will be released
|
// 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)
|
go srv.dialPeer(desc, *slot)
|
||||||
// we can watch if more peers needed in the next loop
|
// we can watch if more peers needed in the next loop
|
||||||
slots = srv.peerSlots
|
slots = srv.peerSlots
|
||||||
@ -355,7 +355,7 @@ func (srv *Server) dialPeer(desc *peerAddr, slot int) {
|
|||||||
srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot)
|
srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot)
|
||||||
conn, err := srv.Dialer.Dial(desc.Network(), desc.String())
|
conn, err := srv.Dialer.Dial(desc.Network(), desc.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
srvlog.Errorf("Dial error: %v", err)
|
srvlog.DebugDetailf("dial error: %v", err)
|
||||||
srv.peerSlots <- slot
|
srv.peerSlots <- slot
|
||||||
return
|
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…
x
Reference in New Issue
Block a user