mirror of
https://github.com/status-im/op-geth.git
synced 2025-02-26 21:40:44 +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
|
||||
.ethtest
|
||||
|
||||
#*
|
||||
.#*
|
||||
*#
|
||||
*~
|
||||
|
12
.mailmap
Normal file
12
.mailmap
Normal file
@ -0,0 +1,12 @@
|
||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
|
||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
|
||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
|
||||
|
||||
Viktor Trón <viktor.tron@gmail.com>
|
||||
|
||||
Joseph Goulden <joegoulden@gmail.com>
|
||||
|
||||
Nick Savers <nicksavers@gmail.com>
|
||||
|
||||
Maran Hidskes <maran.hidskes@gmail.com>
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -61,6 +64,7 @@ var (
|
||||
ImportChain string
|
||||
SHH bool
|
||||
Dial bool
|
||||
PrintVersion bool
|
||||
)
|
||||
|
||||
// flags specific to cli client
|
||||
@ -117,6 +121,7 @@ func Init() {
|
||||
|
||||
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
|
||||
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
|
||||
flag.BoolVar(&PrintVersion, "version", false, "prints version number")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -28,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -49,6 +53,11 @@ func main() {
|
||||
// precedence: code-internal flag default < config file < environment variables < command line
|
||||
Init() // parsing command line
|
||||
|
||||
if PrintVersion {
|
||||
printVersion()
|
||||
return
|
||||
}
|
||||
|
||||
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
||||
|
||||
ethereum, err := eth.New(ð.Config{
|
||||
@ -95,7 +104,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Leave the Println. This needs clean output for piping
|
||||
fmt.Printf("%s\n", block.State().Dump())
|
||||
statedb := state.New(block.Root(), ethereum.Db())
|
||||
fmt.Printf("%s\n", statedb.Dump())
|
||||
|
||||
fmt.Println(block)
|
||||
|
||||
@ -134,3 +144,13 @@ func main() {
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
fmt.Printf(`%v %v
|
||||
PV=%d
|
||||
GOOS=%s
|
||||
GO=%s
|
||||
GOPATH=%s
|
||||
GOROOT=%s
|
||||
`, ClientIdentifier, Version, eth.ProtocolVersion, runtime.GOOS, runtime.Version(), os.Getenv("GOPATH"), runtime.GOROOT())
|
||||
}
|
||||
|
@ -86,6 +86,11 @@ func (self *JSRepl) Stop() {
|
||||
}
|
||||
|
||||
func (self *JSRepl) parseInput(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
|
||||
value, err := self.re.Run(code)
|
||||
if err != nil {
|
||||
|
@ -17,8 +17,6 @@
|
||||
/**
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
* @date 2014
|
||||
*
|
||||
*/
|
||||
|
||||
package main
|
||||
@ -26,12 +24,15 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/tests/helper"
|
||||
)
|
||||
@ -43,8 +44,8 @@ type Account struct {
|
||||
Storage map[string]string
|
||||
}
|
||||
|
||||
func StateObjectFromAccount(addr string, account Account) *state.StateObject {
|
||||
obj := state.NewStateObject(ethutil.Hex2Bytes(addr))
|
||||
func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject {
|
||||
obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db)
|
||||
obj.SetBalance(ethutil.Big(account.Balance))
|
||||
|
||||
if ethutil.IsHex(account.Code) {
|
||||
@ -66,19 +67,20 @@ type VmTest struct {
|
||||
Pre map[string]Account
|
||||
}
|
||||
|
||||
func RunVmTest(js string) (failed int) {
|
||||
func RunVmTest(r io.Reader) (failed int) {
|
||||
tests := make(map[string]VmTest)
|
||||
|
||||
data, _ := ioutil.ReadAll(strings.NewReader(js))
|
||||
data, _ := ioutil.ReadAll(r)
|
||||
err := json.Unmarshal(data, &tests)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
state := state.New(helper.NewTrie())
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := state.New(nil, db)
|
||||
for addr, account := range test.Pre {
|
||||
obj := StateObjectFromAccount(addr, account)
|
||||
obj := StateObjectFromAccount(db, addr, account)
|
||||
state.SetStateObject(obj)
|
||||
}
|
||||
|
||||
@ -118,6 +120,8 @@ func RunVmTest(js string) (failed int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.Flush()
|
||||
}
|
||||
|
||||
return
|
||||
@ -125,9 +129,10 @@ func RunVmTest(js string) (failed int) {
|
||||
|
||||
func main() {
|
||||
helper.Logger.SetLogLevel(5)
|
||||
if len(os.Args) == 1 {
|
||||
log.Fatalln("no json supplied")
|
||||
}
|
||||
|
||||
os.Exit(RunVmTest(os.Args[1]))
|
||||
if len(os.Args) > 1 {
|
||||
os.Exit(RunVmTest(strings.NewReader(os.Args[1])))
|
||||
} else {
|
||||
os.Exit(RunVmTest(os.Stdin))
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
* @date 2014
|
||||
*
|
||||
*/
|
||||
|
||||
package main
|
||||
@ -37,7 +35,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
@ -65,7 +62,7 @@ func main() {
|
||||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := state.New(ptrie.New(nil, db))
|
||||
statedb := state.New(nil, db)
|
||||
sender := statedb.NewStateObject([]byte("sender"))
|
||||
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||
//receiver.SetCode([]byte(*code))
|
||||
@ -133,6 +130,12 @@ func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) GetHash(n uint64) []byte {
|
||||
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
||||
return self.block.Hash()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (self *VMEnv) AddLog(log state.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ ApplicationWindow {
|
||||
property alias dataText: rawDataField.text
|
||||
|
||||
onClosing: {
|
||||
dbg.Stop()
|
||||
//dbg.Stop()
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
@ -353,6 +353,7 @@ ApplicationWindow {
|
||||
|
||||
|
||||
ComboBox {
|
||||
visible: false
|
||||
id: snippets
|
||||
anchors.right: parent.right
|
||||
model: ListModel {
|
||||
|
@ -1,18 +0,0 @@
|
||||
# Ethereum JavaScript API
|
||||
|
||||
This is the Ethereum compatible JavaScript API using `Promise`s
|
||||
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec.
|
||||
|
||||
For an example see `index.html`.
|
||||
|
||||
**Please note this repo is in it's early stage.**
|
||||
|
||||
If you'd like to run a WebSocket ethereum node check out
|
||||
[go-ethereum](https://github.com/ethereum/go-ethereum).
|
||||
|
||||
To install ethereum and spawn a node:
|
||||
|
||||
```
|
||||
go get github.com/ethereum/go-ethereum/ethereum
|
||||
ethereum -ws -loglevel=4
|
||||
```
|
@ -1,70 +0,0 @@
|
||||
(function () {
|
||||
var HttpRpcProvider = function (host) {
|
||||
this.handlers = [];
|
||||
this.host = host;
|
||||
};
|
||||
|
||||
function formatJsonRpcObject(object) {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
method: object.call,
|
||||
params: object.args,
|
||||
id: object._id
|
||||
}
|
||||
};
|
||||
|
||||
function formatJsonRpcMessage(message) {
|
||||
var object = JSON.parse(message);
|
||||
|
||||
return {
|
||||
_id: object.id,
|
||||
data: object.result
|
||||
};
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
|
||||
var data = formatJsonRpcObject(payload);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", this.host, true);
|
||||
request.send(JSON.stringify(data));
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === 4 && cb) {
|
||||
cb(request);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.send = function (payload) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, formatJsonRpcMessage(request.responseText));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.poll = function (payload, id) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
var parsed = JSON.parse(request.responseText);
|
||||
if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) {
|
||||
return;
|
||||
}
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
|
||||
set: function (handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||
web3.providers.HttpRpcProvider = HttpRpcProvider;
|
||||
}
|
||||
})();
|
||||
|
@ -1,33 +0,0 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
<script type="text/javascript" src="websocket.js"></script>
|
||||
<script type="text/javascript" src="qt.js"></script>
|
||||
<script type="text/javascript" src="httprpc.js"></script>
|
||||
<script type="text/javascript">
|
||||
function registerName() {
|
||||
var name = document.querySelector("#name").value;
|
||||
name = web3.fromAscii(name);
|
||||
|
||||
var eth = web3.eth;
|
||||
eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) {
|
||||
document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through.";
|
||||
}, function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>std::name_reg</h1>
|
||||
<input type="text" id="name"></input>
|
||||
<input type="submit" onClick="registerName();"></input>
|
||||
<div id="result"></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,432 +0,0 @@
|
||||
(function(window) {
|
||||
function isPromise(o) {
|
||||
return o instanceof Promise
|
||||
}
|
||||
|
||||
function flattenPromise (obj) {
|
||||
if (obj instanceof Promise) {
|
||||
return Promise.resolve(obj);
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return new Promise(function (resolve) {
|
||||
var promises = obj.map(function (o) {
|
||||
return flattenPromise(o);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
obj[i] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (obj instanceof Object) {
|
||||
return new Promise(function (resolve) {
|
||||
var keys = Object.keys(obj);
|
||||
var promises = keys.map(function (key) {
|
||||
return flattenPromise(obj[key]);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
obj[keys[i]] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(obj);
|
||||
};
|
||||
|
||||
var ethMethods = function () {
|
||||
var blockCall = function (args) {
|
||||
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber";
|
||||
};
|
||||
|
||||
var transactionCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
|
||||
};
|
||||
|
||||
var uncleCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
|
||||
};
|
||||
|
||||
var methods = [
|
||||
{ name: 'balanceAt', call: 'balanceAt' },
|
||||
{ name: 'stateAt', call: 'stateAt' },
|
||||
{ name: 'countAt', call: 'countAt'},
|
||||
{ name: 'codeAt', call: 'codeAt' },
|
||||
{ name: 'transact', call: 'transact' },
|
||||
{ name: 'call', call: 'call' },
|
||||
{ name: 'block', call: blockCall },
|
||||
{ name: 'transaction', call: transactionCall },
|
||||
{ name: 'uncle', call: uncleCall },
|
||||
{ name: 'compile', call: 'compile' }
|
||||
];
|
||||
return methods;
|
||||
};
|
||||
|
||||
var ethProperties = function () {
|
||||
return [
|
||||
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' },
|
||||
{ name: 'listening', getter: 'listening', setter: 'setListening' },
|
||||
{ name: 'mining', getter: 'mining', setter: 'setMining' },
|
||||
{ name: 'gasPrice', getter: 'gasPrice' },
|
||||
{ name: 'account', getter: 'account' },
|
||||
{ name: 'accounts', getter: 'accounts' },
|
||||
{ name: 'peerCount', getter: 'peerCount' },
|
||||
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' },
|
||||
{ name: 'number', getter: 'number'}
|
||||
];
|
||||
};
|
||||
|
||||
var dbMethods = function () {
|
||||
return [
|
||||
{ name: 'put', call: 'put' },
|
||||
{ name: 'get', call: 'get' },
|
||||
{ name: 'putString', call: 'putString' },
|
||||
{ name: 'getString', call: 'getString' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhMethods = function () {
|
||||
return [
|
||||
{ name: 'post', call: 'post' },
|
||||
{ name: 'newIdentity', call: 'newIdentity' },
|
||||
{ name: 'haveIdentity', call: 'haveIdentity' },
|
||||
{ name: 'newGroup', call: 'newGroup' },
|
||||
{ name: 'addToGroup', call: 'addToGroup' }
|
||||
];
|
||||
};
|
||||
|
||||
var ethWatchMethods = function () {
|
||||
var newFilter = function (args) {
|
||||
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter';
|
||||
};
|
||||
|
||||
return [
|
||||
{ name: 'newFilter', call: newFilter },
|
||||
{ name: 'uninstallFilter', call: 'uninstallFilter' },
|
||||
{ name: 'getMessages', call: 'getMessages' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhWatchMethods = function () {
|
||||
return [
|
||||
{ name: 'newFilter', call: 'shhNewFilter' },
|
||||
{ name: 'uninstallFilter', call: 'shhUninstallFilter' },
|
||||
{ name: 'getMessage', call: 'shhGetMessages' }
|
||||
];
|
||||
};
|
||||
|
||||
var setupMethods = function (obj, methods) {
|
||||
methods.forEach(function (method) {
|
||||
obj[method.name] = function () {
|
||||
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
|
||||
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
||||
return {call: call, args: args};
|
||||
}).then(function (request) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
web3.provider.send(request, function (result) {
|
||||
//if (result || typeof result === "boolean") {
|
||||
resolve(result);
|
||||
return;
|
||||
//}
|
||||
//reject(result);
|
||||
});
|
||||
});
|
||||
}).catch(function( err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var setupProperties = function (obj, properties) {
|
||||
properties.forEach(function (property) {
|
||||
var proto = {};
|
||||
proto.get = function () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
web3.provider.send({call: property.getter}, function(result) {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
if (property.setter) {
|
||||
proto.set = function (val) {
|
||||
return flattenPromise([val]).then(function (args) {
|
||||
return new Promise(function (resolve) {
|
||||
web3.provider.send({call: property.setter, args: args}, function (result) {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
Object.defineProperty(obj, property.name, proto);
|
||||
});
|
||||
};
|
||||
|
||||
var web3 = {
|
||||
_callbacks: {},
|
||||
_events: {},
|
||||
providers: {},
|
||||
toHex: function(str) {
|
||||
var hex = "";
|
||||
for(var i = 0; i < str.length; i++) {
|
||||
var n = str.charCodeAt(i).toString(16);
|
||||
hex += n.length < 2 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return hex;
|
||||
},
|
||||
|
||||
toAscii: function(hex) {
|
||||
// Find termination
|
||||
var str = "";
|
||||
var i = 0, l = hex.length;
|
||||
for(; i < l; i+=2) {
|
||||
var code = hex.charCodeAt(i)
|
||||
if(code == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
toDecimal: function (val) {
|
||||
return parseInt(val, 16);
|
||||
},
|
||||
|
||||
fromAscii: function(str, pad) {
|
||||
pad = pad === undefined ? 32 : pad;
|
||||
var hex = this.toHex(str);
|
||||
while(hex.length < pad*2)
|
||||
hex += "00";
|
||||
return hex
|
||||
},
|
||||
|
||||
eth: {
|
||||
prototype: Object(),
|
||||
watch: function (params) {
|
||||
return new Filter(params, ethWatch);
|
||||
},
|
||||
},
|
||||
|
||||
db: {
|
||||
prototype: Object()
|
||||
},
|
||||
|
||||
shh: {
|
||||
prototype: Object(),
|
||||
watch: function (params) {
|
||||
return new Filter(params, shhWatch);
|
||||
}
|
||||
},
|
||||
|
||||
on: function(event, id, cb) {
|
||||
if(web3._events[event] === undefined) {
|
||||
web3._events[event] = {};
|
||||
}
|
||||
|
||||
web3._events[event][id] = cb;
|
||||
return this
|
||||
},
|
||||
|
||||
off: function(event, id) {
|
||||
if(web3._events[event] !== undefined) {
|
||||
delete web3._events[event][id];
|
||||
}
|
||||
|
||||
return this
|
||||
},
|
||||
|
||||
trigger: function(event, id, data) {
|
||||
var callbacks = web3._events[event];
|
||||
if (!callbacks || !callbacks[id]) {
|
||||
return;
|
||||
}
|
||||
var cb = callbacks[id];
|
||||
cb(data);
|
||||
},
|
||||
};
|
||||
|
||||
var eth = web3.eth;
|
||||
setupMethods(eth, ethMethods());
|
||||
setupProperties(eth, ethProperties());
|
||||
setupMethods(web3.db, dbMethods());
|
||||
setupMethods(web3.shh, shhMethods());
|
||||
|
||||
var ethWatch = {
|
||||
changed: 'changed'
|
||||
};
|
||||
setupMethods(ethWatch, ethWatchMethods());
|
||||
var shhWatch = {
|
||||
changed: 'shhChanged'
|
||||
};
|
||||
setupMethods(shhWatch, shhWatchMethods());
|
||||
|
||||
var ProviderManager = function() {
|
||||
this.queued = [];
|
||||
this.polls = [];
|
||||
this.ready = false;
|
||||
this.provider = undefined;
|
||||
this.id = 1;
|
||||
|
||||
var self = this;
|
||||
var poll = function () {
|
||||
if (self.provider && self.provider.poll) {
|
||||
self.polls.forEach(function (data) {
|
||||
data.data._id = self.id;
|
||||
self.id++;
|
||||
self.provider.poll(data.data, data.id);
|
||||
});
|
||||
}
|
||||
setTimeout(poll, 12000);
|
||||
};
|
||||
poll();
|
||||
};
|
||||
|
||||
ProviderManager.prototype.send = function(data, cb) {
|
||||
data._id = this.id;
|
||||
if (cb) {
|
||||
web3._callbacks[data._id] = cb;
|
||||
}
|
||||
|
||||
data.args = data.args || [];
|
||||
this.id++;
|
||||
|
||||
if(this.provider !== undefined) {
|
||||
this.provider.send(data);
|
||||
} else {
|
||||
console.warn("provider is not set");
|
||||
this.queued.push(data);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.set = function(provider) {
|
||||
if(this.provider !== undefined && this.provider.unload !== undefined) {
|
||||
this.provider.unload();
|
||||
}
|
||||
|
||||
this.provider = provider;
|
||||
this.ready = true;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.sendQueued = function() {
|
||||
for(var i = 0; this.queued.length; i++) {
|
||||
// Resend
|
||||
this.send(this.queued[i]);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.installed = function() {
|
||||
return this.provider !== undefined;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.startPolling = function (data, pollId) {
|
||||
if (!this.provider || !this.provider.poll) {
|
||||
return;
|
||||
}
|
||||
this.polls.push({data: data, id: pollId});
|
||||
};
|
||||
|
||||
ProviderManager.prototype.stopPolling = function (pollId) {
|
||||
for (var i = this.polls.length; i--;) {
|
||||
var poll = this.polls[i];
|
||||
if (poll.id === pollId) {
|
||||
this.polls.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
web3.provider = new ProviderManager();
|
||||
|
||||
web3.setProvider = function(provider) {
|
||||
provider.onmessage = messageHandler;
|
||||
web3.provider.set(provider);
|
||||
web3.provider.sendQueued();
|
||||
};
|
||||
|
||||
var Filter = function(options, impl) {
|
||||
this.impl = impl;
|
||||
this.callbacks = [];
|
||||
|
||||
var self = this;
|
||||
this.promise = impl.newFilter(options);
|
||||
this.promise.then(function (id) {
|
||||
self.id = id;
|
||||
web3.on(impl.changed, id, self.trigger.bind(self));
|
||||
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.arrived = function(callback) {
|
||||
this.changed(callback);
|
||||
}
|
||||
|
||||
Filter.prototype.changed = function(callback) {
|
||||
var self = this;
|
||||
this.promise.then(function(id) {
|
||||
self.callbacks.push(callback);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.trigger = function(messages) {
|
||||
for(var i = 0; i < this.callbacks.length; i++) {
|
||||
this.callbacks[i].call(this, messages);
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.uninstall = function() {
|
||||
var self = this;
|
||||
this.promise.then(function (id) {
|
||||
self.impl.uninstallFilter(id);
|
||||
web3.provider.stopPolling(id);
|
||||
web3.off(impl.changed, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.messages = function() {
|
||||
var self = this;
|
||||
return this.promise.then(function (id) {
|
||||
return self.impl.getMessages(id);
|
||||
});
|
||||
};
|
||||
|
||||
function messageHandler(data) {
|
||||
if(data._event !== undefined) {
|
||||
web3.trigger(data._event, data._id, data.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if(data._id) {
|
||||
var cb = web3._callbacks[data._id];
|
||||
if (cb) {
|
||||
cb.call(this, data.data)
|
||||
delete web3._callbacks[data._id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Install default provider
|
||||
if(!web3.provider.installed()) {
|
||||
var sock = new web3.WebSocket("ws://localhost:40404/eth");
|
||||
|
||||
web3.setProvider(sock);
|
||||
}
|
||||
*/
|
||||
|
||||
window.web3 = web3;
|
||||
|
||||
})(this);
|
@ -1,27 +0,0 @@
|
||||
(function() {
|
||||
var QtProvider = function() {
|
||||
this.handlers = [];
|
||||
|
||||
var self = this;
|
||||
navigator.qt.onmessage = function (message) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, JSON.parse(message.data));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
QtProvider.prototype.send = function(payload) {
|
||||
navigator.qt.postMessage(JSON.stringify(payload));
|
||||
};
|
||||
|
||||
Object.defineProperty(QtProvider.prototype, "onmessage", {
|
||||
set: function(handler) {
|
||||
this.handlers.push(handler);
|
||||
},
|
||||
});
|
||||
|
||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||
web3.providers.QtProvider = QtProvider;
|
||||
}
|
||||
})();
|
||||
|
@ -1,51 +0,0 @@
|
||||
(function() {
|
||||
var WebSocketProvider = function(host) {
|
||||
// onmessage handlers
|
||||
this.handlers = [];
|
||||
// queue will be filled with messages if send is invoked before the ws is ready
|
||||
this.queued = [];
|
||||
this.ready = false;
|
||||
|
||||
this.ws = new WebSocket(host);
|
||||
|
||||
var self = this;
|
||||
this.ws.onmessage = function(event) {
|
||||
for(var i = 0; i < self.handlers.length; i++) {
|
||||
self.handlers[i].call(self, JSON.parse(event.data), event)
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onopen = function() {
|
||||
self.ready = true;
|
||||
|
||||
for(var i = 0; i < self.queued.length; i++) {
|
||||
// Resend
|
||||
self.send(self.queued[i]);
|
||||
}
|
||||
};
|
||||
};
|
||||
WebSocketProvider.prototype.send = function(payload) {
|
||||
if(this.ready) {
|
||||
var data = JSON.stringify(payload);
|
||||
|
||||
this.ws.send(data);
|
||||
} else {
|
||||
this.queued.push(payload);
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.onMessage = function(handler) {
|
||||
this.handlers.push(handler);
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.unload = function() {
|
||||
this.ws.close();
|
||||
};
|
||||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
|
||||
set: function(provider) { this.onMessage(provider); }
|
||||
});
|
||||
|
||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||
web3.providers.WebSocketProvider = WebSocketProvider;
|
||||
}
|
||||
})();
|
@ -1,312 +0,0 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
// Main Ethereum library
|
||||
window.eth = {
|
||||
prototype: Object(),
|
||||
_callbacks: {},
|
||||
_onCallbacks: {},
|
||||
|
||||
test: function() {
|
||||
var t = undefined;
|
||||
postData({call: "test"})
|
||||
navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; }
|
||||
for(;;) {
|
||||
if(t !== undefined) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mutan: function(code, cb) {
|
||||
postData({call: "mutan", args: [code]}, cb)
|
||||
},
|
||||
|
||||
toHex: function(str) {
|
||||
var hex = "";
|
||||
for(var i = 0; i < str.length; i++) {
|
||||
var n = str.charCodeAt(i).toString(16);
|
||||
hex += n.length < 2 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return hex;
|
||||
},
|
||||
|
||||
toAscii: function(hex) {
|
||||
// Find termination
|
||||
var str = "";
|
||||
var i = 0, l = hex.length;
|
||||
for(; i < l; i+=2) {
|
||||
var code = hex.charCodeAt(i)
|
||||
if(code == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
fromAscii: function(str, pad) {
|
||||
if(pad === undefined) {
|
||||
pad = 32
|
||||
}
|
||||
|
||||
var hex = this.toHex(str);
|
||||
|
||||
while(hex.length < pad*2)
|
||||
hex += "00";
|
||||
|
||||
return hex
|
||||
},
|
||||
|
||||
|
||||
// Retrieve block
|
||||
//
|
||||
// Either supply a number or a string. Type is determent for the lookup method
|
||||
// string - Retrieves the block by looking up the hash
|
||||
// number - Retrieves the block by looking up the block number
|
||||
getBlock: function(numberOrHash, cb) {
|
||||
var func;
|
||||
if(typeof numberOrHash == "string") {
|
||||
func = "getBlockByHash";
|
||||
} else {
|
||||
func = "getBlockByNumber";
|
||||
}
|
||||
postData({call: func, args: [numberOrHash]}, cb);
|
||||
},
|
||||
|
||||
// Create transaction
|
||||
//
|
||||
// Transact between two state objects
|
||||
transact: function(params, cb) {
|
||||
if(params === undefined) {
|
||||
params = {};
|
||||
}
|
||||
|
||||
if(params.endowment !== undefined)
|
||||
params.value = params.endowment;
|
||||
if(params.code !== undefined)
|
||||
params.data = params.code;
|
||||
|
||||
// Make sure everything is string
|
||||
var fields = ["to", "from", "value", "gas", "gasPrice"];
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
if(params[fields[i]] === undefined) {
|
||||
params[fields[i]] = "";
|
||||
}
|
||||
params[fields[i]] = params[fields[i]].toString();
|
||||
}
|
||||
|
||||
var data;
|
||||
if(typeof params.data === "object") {
|
||||
data = "";
|
||||
for(var i = 0; i < params.data.length; i++) {
|
||||
data += params.data[i]
|
||||
}
|
||||
} else {
|
||||
data = params.data;
|
||||
}
|
||||
|
||||
postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb);
|
||||
},
|
||||
|
||||
getMessages: function(filter, cb) {
|
||||
postData({call: "messages", args: [filter]}, cb);
|
||||
},
|
||||
|
||||
getStorageAt: function(address, storageAddress, cb) {
|
||||
postData({call: "getStorage", args: [address, storageAddress]}, cb);
|
||||
},
|
||||
|
||||
getEachStorageAt: function(address, cb){
|
||||
postData({call: "getEachStorage", args: [address]}, cb);
|
||||
},
|
||||
|
||||
getKey: function(cb) {
|
||||
postData({call: "getKey"}, cb);
|
||||
},
|
||||
|
||||
getTxCountAt: function(address, cb) {
|
||||
postData({call: "getTxCountAt", args: [address]}, cb);
|
||||
},
|
||||
getIsMining: function(cb){
|
||||
postData({call: "getIsMining"}, cb)
|
||||
},
|
||||
getIsListening: function(cb){
|
||||
postData({call: "getIsListening"}, cb)
|
||||
},
|
||||
getCoinBase: function(cb){
|
||||
postData({call: "getCoinBase"}, cb);
|
||||
},
|
||||
getPeerCount: function(cb){
|
||||
postData({call: "getPeerCount"}, cb);
|
||||
},
|
||||
getBalanceAt: function(address, cb) {
|
||||
postData({call: "getBalance", args: [address]}, cb);
|
||||
},
|
||||
getTransactionsFor: function(address, cb) {
|
||||
postData({call: "getTransactionsFor", args: [address]}, cb);
|
||||
},
|
||||
|
||||
getSecretToAddress: function(sec, cb) {
|
||||
postData({call: "getSecretToAddress", args: [sec]}, cb);
|
||||
},
|
||||
|
||||
/*
|
||||
watch: function(address, storageAddrOrCb, cb) {
|
||||
var ev;
|
||||
if(cb === undefined) {
|
||||
cb = storageAddrOrCb;
|
||||
storageAddrOrCb = "";
|
||||
ev = "object:"+address;
|
||||
} else {
|
||||
ev = "storage:"+address+":"+storageAddrOrCb;
|
||||
}
|
||||
|
||||
eth.on(ev, cb)
|
||||
|
||||
postData({call: "watch", args: [address, storageAddrOrCb]});
|
||||
},
|
||||
|
||||
disconnect: function(address, storageAddrOrCb, cb) {
|
||||
var ev;
|
||||
if(cb === undefined) {
|
||||
cb = storageAddrOrCb;
|
||||
storageAddrOrCb = "";
|
||||
ev = "object:"+address;
|
||||
} else {
|
||||
ev = "storage:"+address+":"+storageAddrOrCb;
|
||||
}
|
||||
|
||||
eth.off(ev, cb)
|
||||
|
||||
postData({call: "disconnect", args: [address, storageAddrOrCb]});
|
||||
},
|
||||
*/
|
||||
|
||||
watch: function(options) {
|
||||
var filter = new Filter(options);
|
||||
filter.number = newWatchNum().toString()
|
||||
|
||||
postData({call: "watch", args: [options, filter.number]})
|
||||
|
||||
return filter;
|
||||
},
|
||||
|
||||
set: function(props) {
|
||||
postData({call: "set", args: props});
|
||||
},
|
||||
|
||||
on: function(event, cb) {
|
||||
if(eth._onCallbacks[event] === undefined) {
|
||||
eth._onCallbacks[event] = [];
|
||||
}
|
||||
|
||||
eth._onCallbacks[event].push(cb);
|
||||
|
||||
return this
|
||||
},
|
||||
|
||||
off: function(event, cb) {
|
||||
if(eth._onCallbacks[event] !== undefined) {
|
||||
var callbacks = eth._onCallbacks[event];
|
||||
for(var i = 0; i < callbacks.length; i++) {
|
||||
if(callbacks[i] === cb) {
|
||||
delete callbacks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
},
|
||||
|
||||
trigger: function(event, data) {
|
||||
var callbacks = eth._onCallbacks[event];
|
||||
if(callbacks !== undefined) {
|
||||
for(var i = 0; i < callbacks.length; i++) {
|
||||
// Figure out whether the returned data was an array
|
||||
// array means multiple return arguments (multiple params)
|
||||
if(data instanceof Array) {
|
||||
callbacks[i].apply(this, data);
|
||||
} else {
|
||||
callbacks[i].call(this, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
var Filter = function(options) {
|
||||
this.options = options;
|
||||
};
|
||||
Filter.prototype.changed = function(callback) {
|
||||
// Register the watched:<number>. Qml will call the appropriate event if anything
|
||||
// interesting happens in the land of Go.
|
||||
eth.on("watched:"+this.number, callback)
|
||||
}
|
||||
Filter.prototype.getMessages = function(cb) {
|
||||
return eth.getMessages(this.options, cb)
|
||||
}
|
||||
|
||||
var watchNum = 0;
|
||||
function newWatchNum() {
|
||||
return watchNum++;
|
||||
}
|
||||
|
||||
function postData(data, cb) {
|
||||
data._seed = Math.floor(Math.random() * 1000000)
|
||||
if(cb) {
|
||||
eth._callbacks[data._seed] = cb;
|
||||
}
|
||||
|
||||
if(data.args === undefined) {
|
||||
data.args = [];
|
||||
}
|
||||
|
||||
navigator.qt.postMessage(JSON.stringify(data));
|
||||
}
|
||||
|
||||
navigator.qt.onmessage = function(ev) {
|
||||
var data = JSON.parse(ev.data)
|
||||
|
||||
if(data._event !== undefined) {
|
||||
eth.trigger(data._event, data.data);
|
||||
} else {
|
||||
if(data._seed) {
|
||||
var cb = eth._callbacks[data._seed];
|
||||
if(cb) {
|
||||
cb.call(this, data.data)
|
||||
|
||||
// Remove the "trigger" callback
|
||||
delete eth._callbacks[ev._seed];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eth.on("chain:changed", function() {
|
||||
})
|
||||
|
||||
eth.on("messages", { /* filters */}, function(messages){
|
||||
})
|
||||
|
||||
eth.on("pending:changed", function() {
|
||||
})
|
||||
|
5
cmd/mist/assets/ext/ethereum.js/.bowerrc
Normal file
5
cmd/mist/assets/ext/ethereum.js/.bowerrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"directory": "example/js/",
|
||||
"cwd": "./",
|
||||
"analytics": false
|
||||
}
|
12
cmd/mist/assets/ext/ethereum.js/.editorconfig
Normal file
12
cmd/mist/assets/ext/ethereum.js/.editorconfig
Normal file
@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
@ -4,6 +4,7 @@
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
*.swp
|
||||
/tmp
|
||||
*/**/*un~
|
||||
*un~
|
||||
@ -11,4 +12,7 @@
|
||||
*/**/.DS_Store
|
||||
ethereum/ethereum
|
||||
ethereal/ethereal
|
||||
|
||||
example/js
|
||||
node_modules
|
||||
bower_components
|
||||
npm-debug.log
|
50
cmd/mist/assets/ext/ethereum.js/.jshintrc
Normal file
50
cmd/mist/assets/ext/ethereum.js/.jshintrc
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"predef": [
|
||||
"console",
|
||||
"require",
|
||||
"equal",
|
||||
"test",
|
||||
"testBoth",
|
||||
"testWithDefault",
|
||||
"raises",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"stop",
|
||||
"ok",
|
||||
"strictEqual",
|
||||
"module",
|
||||
"expect",
|
||||
"reject",
|
||||
"impl"
|
||||
],
|
||||
|
||||
"esnext": true,
|
||||
"proto": true,
|
||||
"node" : true,
|
||||
"browser" : true,
|
||||
"browserify" : true,
|
||||
|
||||
"boss" : true,
|
||||
"curly": false,
|
||||
"debug": true,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"shadow": true,
|
||||
"eqnull": true
|
||||
}
|
9
cmd/mist/assets/ext/ethereum.js/.npmignore
Normal file
9
cmd/mist/assets/ext/ethereum.js/.npmignore
Normal file
@ -0,0 +1,9 @@
|
||||
example/js
|
||||
node_modules
|
||||
test
|
||||
.gitignore
|
||||
.editorconfig
|
||||
.travis.yml
|
||||
.npmignore
|
||||
component.json
|
||||
testling.html
|
11
cmd/mist/assets/ext/ethereum.js/.travis.yml
Normal file
11
cmd/mist/assets/ext/ethereum.js/.travis.yml
Normal file
@ -0,0 +1,11 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
before_script:
|
||||
- npm install
|
||||
- npm install jshint
|
||||
script:
|
||||
- "jshint *.js lib"
|
||||
after_script:
|
||||
- npm run-script gulp
|
14
cmd/mist/assets/ext/ethereum.js/LICENSE
Normal file
14
cmd/mist/assets/ext/ethereum.js/LICENSE
Normal file
@ -0,0 +1,14 @@
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
79
cmd/mist/assets/ext/ethereum.js/README.md
Normal file
79
cmd/mist/assets/ext/ethereum.js/README.md
Normal file
@ -0,0 +1,79 @@
|
||||
# Ethereum JavaScript API
|
||||
|
||||
This is the Ethereum compatible JavaScript API using `Promise`s
|
||||
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url]
|
||||
|
||||
<!-- [](https://ci.testling.com/ethereum/ethereum.js) -->
|
||||
|
||||
## Installation
|
||||
|
||||
### Node.js
|
||||
|
||||
npm install ethereum.js
|
||||
|
||||
### For browser
|
||||
Bower
|
||||
|
||||
bower install ethereum.js
|
||||
|
||||
Component
|
||||
|
||||
component install ethereum/ethereum.js
|
||||
|
||||
* Include `ethereum.min.js` in your html file.
|
||||
* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6.
|
||||
|
||||
## Usage
|
||||
Require the library:
|
||||
|
||||
var web3 = require('web3');
|
||||
|
||||
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider)
|
||||
|
||||
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth'));
|
||||
|
||||
There you go, now you can use it:
|
||||
|
||||
```
|
||||
web3.eth.coinbase.then(function(result){
|
||||
console.log(result);
|
||||
return web3.eth.balanceAt(result);
|
||||
}).then(function(balance){
|
||||
console.log(web3.toDecimal(balance));
|
||||
}).catch(function(err){
|
||||
console.log(err);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
For another example see `example/index.html`.
|
||||
|
||||
## Building
|
||||
|
||||
* `gulp build`
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
**Please note this repo is in it's early stage.**
|
||||
|
||||
If you'd like to run a WebSocket ethereum node check out
|
||||
[go-ethereum](https://github.com/ethereum/go-ethereum).
|
||||
|
||||
To install ethereum and spawn a node:
|
||||
|
||||
```
|
||||
go get github.com/ethereum/go-ethereum/ethereum
|
||||
ethereum -ws -loglevel=4
|
||||
```
|
||||
|
||||
[npm-image]: https://badge.fury.io/js/ethereum.js.png
|
||||
[npm-url]: https://npmjs.org/package/ethereum.js
|
||||
[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg
|
||||
[travis-url]: https://travis-ci.org/ethereum/ethereum.js
|
||||
[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg
|
||||
[dep-url]: https://david-dm.org/ethereum/ethereum.js
|
||||
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
|
||||
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies
|
51
cmd/mist/assets/ext/ethereum.js/bower.json
Normal file
51
cmd/mist/assets/ext/ethereum.js/bower.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "ethereum.js",
|
||||
"namespace": "ethereum",
|
||||
"version": "0.0.3",
|
||||
"description": "Ethereum Compatible JavaScript API",
|
||||
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
|
||||
"dependencies": {
|
||||
"es6-promise": "#master"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||
},
|
||||
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"javascript",
|
||||
"API"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marek Kotewicz",
|
||||
"email": "marek@ethdev.com",
|
||||
"homepage": "https://github.com/debris"
|
||||
},
|
||||
{
|
||||
"name": "Marian Oancea",
|
||||
"email": "marian@ethdev.com",
|
||||
"homepage": "https://github.com/cubedro"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0",
|
||||
"ignore": [
|
||||
"example",
|
||||
"lib",
|
||||
"node_modules",
|
||||
"package.json",
|
||||
".bowerrc",
|
||||
".editorconfig",
|
||||
".gitignore",
|
||||
".jshintrc",
|
||||
".npmignore",
|
||||
".travis.yml",
|
||||
"gulpfile.js",
|
||||
"index.js",
|
||||
"**/*.txt"
|
||||
]
|
||||
}
|
1184
cmd/mist/assets/ext/ethereum.js/dist/ethereum.js
vendored
Normal file
1184
cmd/mist/assets/ext/ethereum.js/dist/ethereum.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
cmd/mist/assets/ext/ethereum.js/dist/ethereum.js.map
vendored
Normal file
29
cmd/mist/assets/ext/ethereum.js/dist/ethereum.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
cmd/mist/assets/ext/ethereum.js/dist/ethereum.min.js
vendored
Normal file
1
cmd/mist/assets/ext/ethereum.js/dist/ethereum.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
41
cmd/mist/assets/ext/ethereum.js/example/balance.html
Normal file
41
cmd/mist/assets/ext/ethereum.js/example/balance.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
|
||||
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var web3 = require('web3');
|
||||
web3.setProvider(new web3.providers.AutoProvider());
|
||||
|
||||
function watchBalance() {
|
||||
var coinbase = web3.eth.coinbase;
|
||||
var originalBalance = 0;
|
||||
|
||||
web3.eth.balanceAt(coinbase).then(function (balance) {
|
||||
originalBalance = web3.toDecimal(balance);
|
||||
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...';
|
||||
});
|
||||
|
||||
web3.eth.watch({altered: coinbase}).changed(function() {
|
||||
web3.eth.balanceAt(coinbase).then(function (balance) {
|
||||
var currentBalance = web3.toDecimal(balance);
|
||||
document.getElementById("current").innerText = 'current: ' + currentBalance;
|
||||
document.getElementById("diff").innerText = 'diff: ' + (currentBalance - originalBalance);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>coinbase balance</h1>
|
||||
<button type="button" onClick="watchBalance();">watch balance</button>
|
||||
<div></div>
|
||||
<div id="original"></div>
|
||||
<div id="current"></div>
|
||||
<div id="diff"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
75
cmd/mist/assets/ext/ethereum.js/example/contract.html
Normal file
75
cmd/mist/assets/ext/ethereum.js/example/contract.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
|
||||
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var web3 = require('web3');
|
||||
web3.setProvider(new web3.providers.AutoProvider());
|
||||
|
||||
// solidity source code
|
||||
var source = "" +
|
||||
"contract test {\n" +
|
||||
" function multiply(uint a) returns(uint d) {\n" +
|
||||
" return a * 7;\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
// contract description, this will be autogenerated somehow
|
||||
var desc = [{
|
||||
"name": "multiply",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "d",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
var contract;
|
||||
|
||||
function createExampleContract() {
|
||||
// hide create button
|
||||
document.getElementById('create').style.visibility = 'hidden';
|
||||
document.getElementById('source').innerText = source;
|
||||
|
||||
// create contract
|
||||
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
|
||||
contract = web3.contract(address, desc);
|
||||
document.getElementById('call').style.visibility = 'visible';
|
||||
});
|
||||
}
|
||||
|
||||
function callExampleContract() {
|
||||
// this should be generated by ethereum
|
||||
var param = parseInt(document.getElementById('value').value);
|
||||
|
||||
// call the contract
|
||||
contract.multiply(param).call().then(function(res) {
|
||||
document.getElementById('result').innerText = res[0];
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>contract</h1>
|
||||
<div id="source"></div>
|
||||
<div id='create'>
|
||||
<button type="button" onClick="createExampleContract();">create example contract</button>
|
||||
</div>
|
||||
<div id='call' style='visibility: hidden;'>
|
||||
<input type="number" id="value" onkeyup='callExampleContract()'></input>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
16
cmd/mist/assets/ext/ethereum.js/example/node-app.js
Normal file
16
cmd/mist/assets/ext/ethereum.js/example/node-app.js
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('es6-promise').polyfill();
|
||||
|
||||
var web3 = require("../index.js");
|
||||
|
||||
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080'));
|
||||
|
||||
web3.eth.coinbase.then(function(result){
|
||||
console.log(result);
|
||||
return web3.eth.balanceAt(result);
|
||||
}).then(function(balance){
|
||||
console.log(web3.toDecimal(balance));
|
||||
}).catch(function(err){
|
||||
console.log(err);
|
||||
});
|
104
cmd/mist/assets/ext/ethereum.js/gulpfile.js
Normal file
104
cmd/mist/assets/ext/ethereum.js/gulpfile.js
Normal file
@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var del = require('del');
|
||||
var gulp = require('gulp');
|
||||
var browserify = require('browserify');
|
||||
var jshint = require('gulp-jshint');
|
||||
var uglify = require('gulp-uglify');
|
||||
var rename = require('gulp-rename');
|
||||
var envify = require('envify/custom');
|
||||
var unreach = require('unreachable-branch-transform');
|
||||
var source = require('vinyl-source-stream');
|
||||
var exorcist = require('exorcist');
|
||||
var bower = require('bower');
|
||||
|
||||
var DEST = './dist/';
|
||||
|
||||
var build = function(src, dst, ugly) {
|
||||
var result = browserify({
|
||||
debug: true,
|
||||
insert_global_vars: false,
|
||||
detectGlobals: false,
|
||||
bundleExternal: false
|
||||
})
|
||||
.require('./' + src + '.js', {expose: 'web3'})
|
||||
.add('./' + src + '.js')
|
||||
.transform('envify', {
|
||||
NODE_ENV: 'build'
|
||||
})
|
||||
.transform('unreachable-branch-transform');
|
||||
|
||||
if (ugly) {
|
||||
result = result.transform('uglifyify', {
|
||||
mangle: false,
|
||||
compress: {
|
||||
dead_code: false,
|
||||
conditionals: true,
|
||||
unused: false,
|
||||
hoist_funs: true,
|
||||
hoist_vars: true,
|
||||
negate_iife: false
|
||||
},
|
||||
beautify: true,
|
||||
warnings: true
|
||||
});
|
||||
}
|
||||
|
||||
return result.bundle()
|
||||
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
|
||||
.pipe(source(dst + '.js'))
|
||||
.pipe(gulp.dest( DEST ));
|
||||
};
|
||||
|
||||
var uglifyFile = function(file) {
|
||||
return gulp.src( DEST + file + '.js')
|
||||
.pipe(uglify())
|
||||
.pipe(rename(file + '.min.js'))
|
||||
.pipe(gulp.dest( DEST ));
|
||||
};
|
||||
|
||||
gulp.task('bower', function(cb){
|
||||
bower.commands.install().on('end', function (installed){
|
||||
console.log(installed);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('clean', ['lint'], function(cb) {
|
||||
del([ DEST ], cb);
|
||||
});
|
||||
|
||||
gulp.task('lint', function(){
|
||||
return gulp.src(['./*.js', './lib/*.js'])
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
});
|
||||
|
||||
gulp.task('build', ['clean'], function () {
|
||||
return build('index', 'ethereum', true);
|
||||
});
|
||||
|
||||
gulp.task('buildDev', ['clean'], function () {
|
||||
return build('index', 'ethereum', false);
|
||||
});
|
||||
|
||||
gulp.task('uglify', ['build'], function(){
|
||||
return uglifyFile('ethereum');
|
||||
});
|
||||
|
||||
gulp.task('uglify', ['buildDev'], function(){
|
||||
return uglifyFile('ethereum');
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
|
||||
});
|
||||
|
||||
gulp.task('release', ['bower', 'lint', 'build', 'uglify']);
|
||||
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']);
|
||||
gulp.task('default', ['dev']);
|
||||
|
8
cmd/mist/assets/ext/ethereum.js/index.js
Normal file
8
cmd/mist/assets/ext/ethereum.js/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
var web3 = require('./lib/web3');
|
||||
web3.providers.WebSocketProvider = require('./lib/websocket');
|
||||
web3.providers.HttpRpcProvider = require('./lib/httprpc');
|
||||
web3.providers.QtProvider = require('./lib/qt');
|
||||
web3.providers.AutoProvider = require('./lib/autoprovider');
|
||||
web3.contract = require('./lib/contract');
|
||||
|
||||
module.exports = web3;
|
267
cmd/mist/assets/ext/ethereum.js/lib/abi.js
Normal file
267
cmd/mist/assets/ext/ethereum.js/lib/abi.js
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file abi.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: is these line is supposed to be here?
|
||||
if (process.env.NODE_ENV !== 'build') {
|
||||
var web3 = require('./web3'); // jshint ignore:line
|
||||
}
|
||||
|
||||
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
|
||||
var hexToDec = function (hex) {
|
||||
return parseInt(hex, 16).toString();
|
||||
};
|
||||
|
||||
var decToHex = function (dec) {
|
||||
return parseInt(dec).toString(16);
|
||||
};
|
||||
|
||||
var findIndex = function (array, callback) {
|
||||
var end = false;
|
||||
var i = 0;
|
||||
for (; i < array.length && !end; i++) {
|
||||
end = callback(array[i]);
|
||||
}
|
||||
return end ? i - 1 : -1;
|
||||
};
|
||||
|
||||
var findMethodIndex = function (json, methodName) {
|
||||
return findIndex(json, function (method) {
|
||||
return method.name === methodName;
|
||||
});
|
||||
};
|
||||
|
||||
var padLeft = function (string, chars) {
|
||||
return new Array(chars - string.length + 1).join("0") + string;
|
||||
};
|
||||
|
||||
var calcBitPadding = function (type, expected) {
|
||||
var value = type.slice(expected.length);
|
||||
if (value === "") {
|
||||
return 32;
|
||||
}
|
||||
return parseInt(value) / 8;
|
||||
};
|
||||
|
||||
var calcBytePadding = function (type, expected) {
|
||||
var value = type.slice(expected.length);
|
||||
if (value === "") {
|
||||
return 32;
|
||||
}
|
||||
return parseInt(value);
|
||||
};
|
||||
|
||||
var calcRealPadding = function (type, expected) {
|
||||
var value = type.slice(expected.length);
|
||||
if (value === "") {
|
||||
return 32;
|
||||
}
|
||||
var sizes = value.split('x');
|
||||
for (var padding = 0, i = 0; i < sizes; i++) {
|
||||
padding += (sizes[i] / 8);
|
||||
}
|
||||
return padding;
|
||||
};
|
||||
|
||||
var setupInputTypes = function () {
|
||||
|
||||
var prefixedType = function (prefix, calcPadding) {
|
||||
return function (type, value) {
|
||||
var expected = prefix;
|
||||
if (type.indexOf(expected) !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var padding = calcPadding(type, expected);
|
||||
if (typeof value === "number")
|
||||
value = value.toString(16);
|
||||
else if (typeof value === "string")
|
||||
value = web3.toHex(value);
|
||||
else if (value.indexOf('0x') === 0)
|
||||
value = value.substr(2);
|
||||
else
|
||||
value = (+value).toString(16);
|
||||
return padLeft(value, padding * 2);
|
||||
};
|
||||
};
|
||||
|
||||
var namedType = function (name, padding, formatter) {
|
||||
return function (type, value) {
|
||||
if (type !== name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return padLeft(formatter ? formatter(value) : value, padding * 2);
|
||||
};
|
||||
};
|
||||
|
||||
var formatBool = function (value) {
|
||||
return value ? '0x1' : '0x0';
|
||||
};
|
||||
|
||||
return [
|
||||
prefixedType('uint', calcBitPadding),
|
||||
prefixedType('int', calcBitPadding),
|
||||
prefixedType('hash', calcBitPadding),
|
||||
prefixedType('string', calcBytePadding),
|
||||
prefixedType('real', calcRealPadding),
|
||||
prefixedType('ureal', calcRealPadding),
|
||||
namedType('address', 20),
|
||||
namedType('bool', 1, formatBool),
|
||||
];
|
||||
};
|
||||
|
||||
var inputTypes = setupInputTypes();
|
||||
|
||||
var toAbiInput = function (json, methodName, params) {
|
||||
var bytes = "";
|
||||
var index = findMethodIndex(json, methodName);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
bytes = "0x" + padLeft(index.toString(16), 2);
|
||||
var method = json[index];
|
||||
|
||||
for (var i = 0; i < method.inputs.length; i++) {
|
||||
var found = false;
|
||||
for (var j = 0; j < inputTypes.length && !found; j++) {
|
||||
found = inputTypes[j](method.inputs[i].type, params[i]);
|
||||
}
|
||||
if (!found) {
|
||||
console.error('unsupported json type: ' + method.inputs[i].type);
|
||||
}
|
||||
bytes += found;
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
|
||||
var setupOutputTypes = function () {
|
||||
|
||||
var prefixedType = function (prefix, calcPadding) {
|
||||
return function (type) {
|
||||
var expected = prefix;
|
||||
if (type.indexOf(expected) !== 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var padding = calcPadding(type, expected);
|
||||
return padding * 2;
|
||||
};
|
||||
};
|
||||
|
||||
var namedType = function (name, padding) {
|
||||
return function (type) {
|
||||
return name === type ? padding * 2 : -1;
|
||||
};
|
||||
};
|
||||
|
||||
var formatInt = function (value) {
|
||||
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
|
||||
};
|
||||
|
||||
var formatHash = function (value) {
|
||||
return "0x" + value;
|
||||
};
|
||||
|
||||
var formatBool = function (value) {
|
||||
return value === '1' ? true : false;
|
||||
};
|
||||
|
||||
var formatString = function (value) {
|
||||
return web3.toAscii(value);
|
||||
};
|
||||
|
||||
return [
|
||||
{ padding: prefixedType('uint', calcBitPadding), format: formatInt },
|
||||
{ padding: prefixedType('int', calcBitPadding), format: formatInt },
|
||||
{ padding: prefixedType('hash', calcBitPadding), format: formatHash },
|
||||
{ padding: prefixedType('string', calcBytePadding), format: formatString },
|
||||
{ padding: prefixedType('real', calcRealPadding), format: formatInt },
|
||||
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt },
|
||||
{ padding: namedType('address', 20) },
|
||||
{ padding: namedType('bool', 1), format: formatBool }
|
||||
];
|
||||
};
|
||||
|
||||
var outputTypes = setupOutputTypes();
|
||||
|
||||
var fromAbiOutput = function (json, methodName, output) {
|
||||
var index = findMethodIndex(json, methodName);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
output = output.slice(2);
|
||||
|
||||
var result = [];
|
||||
var method = json[index];
|
||||
for (var i = 0; i < method.outputs.length; i++) {
|
||||
var padding = -1;
|
||||
for (var j = 0; j < outputTypes.length && padding === -1; j++) {
|
||||
padding = outputTypes[j].padding(method.outputs[i].type);
|
||||
}
|
||||
|
||||
if (padding === -1) {
|
||||
// not found output parsing
|
||||
continue;
|
||||
}
|
||||
var res = output.slice(0, padding);
|
||||
var formatter = outputTypes[j - 1].format;
|
||||
result.push(formatter ? formatter(res) : ("0x" + res));
|
||||
output = output.slice(padding);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var inputParser = function (json) {
|
||||
var parser = {};
|
||||
json.forEach(function (method) {
|
||||
parser[method.name] = function () {
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
return toAbiInput(json, method.name, params);
|
||||
};
|
||||
});
|
||||
|
||||
return parser;
|
||||
};
|
||||
|
||||
var outputParser = function (json) {
|
||||
var parser = {};
|
||||
json.forEach(function (method) {
|
||||
parser[method.name] = function (output) {
|
||||
return fromAbiOutput(json, method.name, output);
|
||||
};
|
||||
});
|
||||
|
||||
return parser;
|
||||
};
|
||||
|
||||
if (typeof(module) !== "undefined") {
|
||||
module.exports = {
|
||||
inputParser: inputParser,
|
||||
outputParser: outputParser
|
||||
};
|
||||
}
|
103
cmd/mist/assets/ext/ethereum.js/lib/autoprovider.js
Normal file
103
cmd/mist/assets/ext/ethereum.js/lib/autoprovider.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file autoprovider.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief if qt object is available, uses QtProvider,
|
||||
* if not tries to connect over websockets
|
||||
* if it fails, it uses HttpRpcProvider
|
||||
*/
|
||||
|
||||
// TODO: is these line is supposed to be here?
|
||||
if (process.env.NODE_ENV !== 'build') {
|
||||
var WebSocket = require('ws'); // jshint ignore:line
|
||||
var web3 = require('./web3'); // jshint ignore:line
|
||||
}
|
||||
|
||||
var AutoProvider = function (userOptions) {
|
||||
if (web3.haveProvider()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// before we determine what provider we are, we have to cache request
|
||||
this.sendQueue = [];
|
||||
this.onmessageQueue = [];
|
||||
|
||||
if (navigator.qt) {
|
||||
this.provider = new web3.providers.QtProvider();
|
||||
return;
|
||||
}
|
||||
|
||||
userOptions = userOptions || {};
|
||||
var options = {
|
||||
httprpc: userOptions.httprpc || 'http://localhost:8080',
|
||||
websockets: userOptions.websockets || 'ws://localhost:40404/eth'
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var closeWithSuccess = function (success) {
|
||||
ws.close();
|
||||
if (success) {
|
||||
self.provider = new web3.providers.WebSocketProvider(options.websockets);
|
||||
} else {
|
||||
self.provider = new web3.providers.HttpRpcProvider(options.httprpc);
|
||||
self.poll = self.provider.poll.bind(self.provider);
|
||||
}
|
||||
self.sendQueue.forEach(function (payload) {
|
||||
self.provider(payload);
|
||||
});
|
||||
self.onmessageQueue.forEach(function (handler) {
|
||||
self.provider.onmessage = handler;
|
||||
});
|
||||
};
|
||||
|
||||
var ws = new WebSocket(options.websockets);
|
||||
|
||||
ws.onopen = function() {
|
||||
closeWithSuccess(true);
|
||||
};
|
||||
|
||||
ws.onerror = function() {
|
||||
closeWithSuccess(false);
|
||||
};
|
||||
};
|
||||
|
||||
AutoProvider.prototype.send = function (payload) {
|
||||
if (this.provider) {
|
||||
this.provider.send(payload);
|
||||
return;
|
||||
}
|
||||
this.sendQueue.push(payload);
|
||||
};
|
||||
|
||||
Object.defineProperty(AutoProvider.prototype, 'onmessage', {
|
||||
set: function (handler) {
|
||||
if (this.provider) {
|
||||
this.provider.onmessage = handler;
|
||||
return;
|
||||
}
|
||||
this.onmessageQueue.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = AutoProvider;
|
66
cmd/mist/assets/ext/ethereum.js/lib/contract.js
Normal file
66
cmd/mist/assets/ext/ethereum.js/lib/contract.js
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file contract.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: is these line is supposed to be here?
|
||||
if (process.env.NODE_ENV !== 'build') {
|
||||
var web3 = require('./web3'); // jshint ignore:line
|
||||
}
|
||||
|
||||
var abi = require('./abi');
|
||||
|
||||
var contract = function (address, desc) {
|
||||
var inputParser = abi.inputParser(desc);
|
||||
var outputParser = abi.outputParser(desc);
|
||||
|
||||
var contract = {};
|
||||
|
||||
desc.forEach(function (method) {
|
||||
contract[method.name] = function () {
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
var parsed = inputParser[method.name].apply(null, params);
|
||||
|
||||
var onSuccess = function (result) {
|
||||
return outputParser[method.name](result);
|
||||
};
|
||||
|
||||
return {
|
||||
call: function (extra) {
|
||||
extra = extra || {};
|
||||
extra.to = address;
|
||||
extra.data = parsed;
|
||||
return web3.eth.call(extra).then(onSuccess);
|
||||
},
|
||||
transact: function (extra) {
|
||||
extra = extra || {};
|
||||
extra.to = address;
|
||||
extra.data = parsed;
|
||||
return web3.eth.transact(extra).then(onSuccess);
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
return contract;
|
||||
};
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = contract;
|
95
cmd/mist/assets/ext/ethereum.js/lib/httprpc.js
Normal file
95
cmd/mist/assets/ext/ethereum.js/lib/httprpc.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file httprpc.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: is these line is supposed to be here?
|
||||
if (process.env.NODE_ENV !== 'build') {
|
||||
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
||||
}
|
||||
|
||||
var HttpRpcProvider = function (host) {
|
||||
this.handlers = [];
|
||||
this.host = host;
|
||||
};
|
||||
|
||||
function formatJsonRpcObject(object) {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
method: object.call,
|
||||
params: object.args,
|
||||
id: object._id
|
||||
};
|
||||
}
|
||||
|
||||
function formatJsonRpcMessage(message) {
|
||||
var object = JSON.parse(message);
|
||||
|
||||
return {
|
||||
_id: object.id,
|
||||
data: object.result,
|
||||
error: object.error
|
||||
};
|
||||
}
|
||||
|
||||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
|
||||
var data = formatJsonRpcObject(payload);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", this.host, true);
|
||||
request.send(JSON.stringify(data));
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === 4 && cb) {
|
||||
cb(request);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.send = function (payload) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, formatJsonRpcMessage(request.responseText));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.poll = function (payload, id) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
var parsed = JSON.parse(request.responseText);
|
||||
if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {
|
||||
return;
|
||||
}
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
|
||||
set: function (handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = HttpRpcProvider;
|
46
cmd/mist/assets/ext/ethereum.js/lib/qt.js
Normal file
46
cmd/mist/assets/ext/ethereum.js/lib/qt.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file qt.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
var QtProvider = function() {
|
||||
this.handlers = [];
|
||||
|
||||
var self = this;
|
||||
navigator.qt.onmessage = function (message) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, JSON.parse(message.data));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
QtProvider.prototype.send = function(payload) {
|
||||
navigator.qt.postMessage(JSON.stringify(payload));
|
||||
};
|
||||
|
||||
Object.defineProperty(QtProvider.prototype, "onmessage", {
|
||||
set: function(handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = QtProvider;
|
509
cmd/mist/assets/ext/ethereum.js/lib/web3.js
Normal file
509
cmd/mist/assets/ext/ethereum.js/lib/web3.js
Normal file
@ -0,0 +1,509 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file main.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
function flattenPromise (obj) {
|
||||
if (obj instanceof Promise) {
|
||||
return Promise.resolve(obj);
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return new Promise(function (resolve) {
|
||||
var promises = obj.map(function (o) {
|
||||
return flattenPromise(o);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
obj[i] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (obj instanceof Object) {
|
||||
return new Promise(function (resolve) {
|
||||
var keys = Object.keys(obj);
|
||||
var promises = keys.map(function (key) {
|
||||
return flattenPromise(obj[key]);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
obj[keys[i]] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(obj);
|
||||
}
|
||||
|
||||
var web3Methods = function () {
|
||||
return [
|
||||
{ name: 'sha3', call: 'web3_sha3' }
|
||||
];
|
||||
};
|
||||
|
||||
var ethMethods = function () {
|
||||
var blockCall = function (args) {
|
||||
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
|
||||
};
|
||||
|
||||
var transactionCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
|
||||
};
|
||||
|
||||
var uncleCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
|
||||
};
|
||||
|
||||
var methods = [
|
||||
{ name: 'balanceAt', call: 'eth_balanceAt' },
|
||||
{ name: 'stateAt', call: 'eth_stateAt' },
|
||||
{ name: 'storageAt', call: 'eth_storageAt' },
|
||||
{ name: 'countAt', call: 'eth_countAt'},
|
||||
{ name: 'codeAt', call: 'eth_codeAt' },
|
||||
{ name: 'transact', call: 'eth_transact' },
|
||||
{ name: 'call', call: 'eth_call' },
|
||||
{ name: 'block', call: blockCall },
|
||||
{ name: 'transaction', call: transactionCall },
|
||||
{ name: 'uncle', call: uncleCall },
|
||||
{ name: 'compilers', call: 'eth_compilers' },
|
||||
{ name: 'lll', call: 'eth_lll' },
|
||||
{ name: 'solidity', call: 'eth_solidity' },
|
||||
{ name: 'serpent', call: 'eth_serpent' },
|
||||
{ name: 'logs', call: 'eth_logs' }
|
||||
];
|
||||
return methods;
|
||||
};
|
||||
|
||||
var ethProperties = function () {
|
||||
return [
|
||||
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
|
||||
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
|
||||
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
|
||||
{ name: 'gasPrice', getter: 'eth_gasPrice' },
|
||||
{ name: 'account', getter: 'eth_account' },
|
||||
{ name: 'accounts', getter: 'eth_accounts' },
|
||||
{ name: 'peerCount', getter: 'eth_peerCount' },
|
||||
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
|
||||
{ name: 'number', getter: 'eth_number'}
|
||||
];
|
||||
};
|
||||
|
||||
var dbMethods = function () {
|
||||
return [
|
||||
{ name: 'put', call: 'db_put' },
|
||||
{ name: 'get', call: 'db_get' },
|
||||
{ name: 'putString', call: 'db_putString' },
|
||||
{ name: 'getString', call: 'db_getString' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhMethods = function () {
|
||||
return [
|
||||
{ name: 'post', call: 'shh_post' },
|
||||
{ name: 'newIdentity', call: 'shh_newIdentity' },
|
||||
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
|
||||
{ name: 'newGroup', call: 'shh_newGroup' },
|
||||
{ name: 'addToGroup', call: 'shh_addToGroup' }
|
||||
];
|
||||
};
|
||||
|
||||
var ethWatchMethods = function () {
|
||||
var newFilter = function (args) {
|
||||
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
|
||||
};
|
||||
|
||||
return [
|
||||
{ name: 'newFilter', call: newFilter },
|
||||
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' },
|
||||
{ name: 'getMessages', call: 'eth_filterLogs' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhWatchMethods = function () {
|
||||
return [
|
||||
{ name: 'newFilter', call: 'shh_newFilter' },
|
||||
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
|
||||
{ name: 'getMessages', call: 'shh_getMessages' }
|
||||
];
|
||||
};
|
||||
|
||||
var setupMethods = function (obj, methods) {
|
||||
methods.forEach(function (method) {
|
||||
obj[method.name] = function () {
|
||||
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
|
||||
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
||||
return {call: call, args: args};
|
||||
}).then(function (request) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
web3.provider.send(request, function (err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}).catch(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var setupProperties = function (obj, properties) {
|
||||
properties.forEach(function (property) {
|
||||
var proto = {};
|
||||
proto.get = function () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
web3.provider.send({call: property.getter}, function(err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
if (property.setter) {
|
||||
proto.set = function (val) {
|
||||
return flattenPromise([val]).then(function (args) {
|
||||
return new Promise(function (resolve) {
|
||||
web3.provider.send({call: property.setter, args: args}, function (err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
Object.defineProperty(obj, property.name, proto);
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: import from a dependency, don't duplicate.
|
||||
var hexToDec = function (hex) {
|
||||
return parseInt(hex, 16).toString();
|
||||
};
|
||||
|
||||
var decToHex = function (dec) {
|
||||
return parseInt(dec).toString(16);
|
||||
};
|
||||
|
||||
|
||||
var web3 = {
|
||||
_callbacks: {},
|
||||
_events: {},
|
||||
providers: {},
|
||||
|
||||
toHex: function(str) {
|
||||
var hex = "";
|
||||
for(var i = 0; i < str.length; i++) {
|
||||
var n = str.charCodeAt(i).toString(16);
|
||||
hex += n.length < 2 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return hex;
|
||||
},
|
||||
|
||||
toAscii: function(hex) {
|
||||
// Find termination
|
||||
var str = "";
|
||||
var i = 0, l = hex.length;
|
||||
if (hex.substring(0, 2) === '0x')
|
||||
i = 2;
|
||||
for(; i < l; i+=2) {
|
||||
var code = hex.charCodeAt(i);
|
||||
if(code === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
fromAscii: function(str, pad) {
|
||||
pad = pad === undefined ? 32 : pad;
|
||||
var hex = this.toHex(str);
|
||||
while(hex.length < pad*2)
|
||||
hex += "00";
|
||||
return "0x" + hex;
|
||||
},
|
||||
|
||||
toDecimal: function (val) {
|
||||
return hexToDec(val.substring(2));
|
||||
},
|
||||
|
||||
fromDecimal: function (val) {
|
||||
return "0x" + decToHex(val);
|
||||
},
|
||||
|
||||
toEth: function(str) {
|
||||
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
|
||||
var unit = 0;
|
||||
var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
|
||||
while (val > 3000 && unit < units.length - 1)
|
||||
{
|
||||
val /= 1000;
|
||||
unit++;
|
||||
}
|
||||
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
|
||||
var replaceFunction = function($0, $1, $2) {
|
||||
return $1 + ',' + $2;
|
||||
};
|
||||
|
||||
while (true) {
|
||||
var o = s;
|
||||
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
|
||||
if (o === s)
|
||||
break;
|
||||
}
|
||||
return s + ' ' + units[unit];
|
||||
},
|
||||
|
||||
eth: {
|
||||
prototype: Object(), // jshint ignore:line
|
||||
watch: function (params) {
|
||||
return new Filter(params, ethWatch);
|
||||
}
|
||||
},
|
||||
|
||||
db: {
|
||||
prototype: Object() // jshint ignore:line
|
||||
},
|
||||
|
||||
shh: {
|
||||
prototype: Object(), // jshint ignore:line
|
||||
watch: function (params) {
|
||||
return new Filter(params, shhWatch);
|
||||
}
|
||||
},
|
||||
|
||||
on: function(event, id, cb) {
|
||||
if(web3._events[event] === undefined) {
|
||||
web3._events[event] = {};
|
||||
}
|
||||
|
||||
web3._events[event][id] = cb;
|
||||
return this;
|
||||
},
|
||||
|
||||
off: function(event, id) {
|
||||
if(web3._events[event] !== undefined) {
|
||||
delete web3._events[event][id];
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
trigger: function(event, id, data) {
|
||||
var callbacks = web3._events[event];
|
||||
if (!callbacks || !callbacks[id]) {
|
||||
return;
|
||||
}
|
||||
var cb = callbacks[id];
|
||||
cb(data);
|
||||
}
|
||||
};
|
||||
|
||||
setupMethods(web3, web3Methods());
|
||||
setupMethods(web3.eth, ethMethods());
|
||||
setupProperties(web3.eth, ethProperties());
|
||||
setupMethods(web3.db, dbMethods());
|
||||
setupMethods(web3.shh, shhMethods());
|
||||
|
||||
var ethWatch = {
|
||||
changed: 'eth_changed'
|
||||
};
|
||||
setupMethods(ethWatch, ethWatchMethods());
|
||||
var shhWatch = {
|
||||
changed: 'shh_changed'
|
||||
};
|
||||
setupMethods(shhWatch, shhWatchMethods());
|
||||
|
||||
var ProviderManager = function() {
|
||||
this.queued = [];
|
||||
this.polls = [];
|
||||
this.ready = false;
|
||||
this.provider = undefined;
|
||||
this.id = 1;
|
||||
|
||||
var self = this;
|
||||
var poll = function () {
|
||||
if (self.provider && self.provider.poll) {
|
||||
self.polls.forEach(function (data) {
|
||||
data.data._id = self.id;
|
||||
self.id++;
|
||||
self.provider.poll(data.data, data.id);
|
||||
});
|
||||
}
|
||||
setTimeout(poll, 12000);
|
||||
};
|
||||
poll();
|
||||
};
|
||||
|
||||
ProviderManager.prototype.send = function(data, cb) {
|
||||
data._id = this.id;
|
||||
if (cb) {
|
||||
web3._callbacks[data._id] = cb;
|
||||
}
|
||||
|
||||
data.args = data.args || [];
|
||||
this.id++;
|
||||
|
||||
if(this.provider !== undefined) {
|
||||
this.provider.send(data);
|
||||
} else {
|
||||
console.warn("provider is not set");
|
||||
this.queued.push(data);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.set = function(provider) {
|
||||
if(this.provider !== undefined && this.provider.unload !== undefined) {
|
||||
this.provider.unload();
|
||||
}
|
||||
|
||||
this.provider = provider;
|
||||
this.ready = true;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.sendQueued = function() {
|
||||
for(var i = 0; this.queued.length; i++) {
|
||||
// Resend
|
||||
this.send(this.queued[i]);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.installed = function() {
|
||||
return this.provider !== undefined;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.startPolling = function (data, pollId) {
|
||||
if (!this.provider || !this.provider.poll) {
|
||||
return;
|
||||
}
|
||||
this.polls.push({data: data, id: pollId});
|
||||
};
|
||||
|
||||
ProviderManager.prototype.stopPolling = function (pollId) {
|
||||
for (var i = this.polls.length; i--;) {
|
||||
var poll = this.polls[i];
|
||||
if (poll.id === pollId) {
|
||||
this.polls.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
web3.provider = new ProviderManager();
|
||||
|
||||
web3.setProvider = function(provider) {
|
||||
provider.onmessage = messageHandler;
|
||||
web3.provider.set(provider);
|
||||
web3.provider.sendQueued();
|
||||
};
|
||||
|
||||
web3.haveProvider = function() {
|
||||
return !!web3.provider.provider;
|
||||
};
|
||||
|
||||
var Filter = function(options, impl) {
|
||||
this.impl = impl;
|
||||
this.callbacks = [];
|
||||
|
||||
var self = this;
|
||||
this.promise = impl.newFilter(options);
|
||||
this.promise.then(function (id) {
|
||||
self.id = id;
|
||||
web3.on(impl.changed, id, self.trigger.bind(self));
|
||||
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.arrived = function(callback) {
|
||||
this.changed(callback);
|
||||
};
|
||||
|
||||
Filter.prototype.changed = function(callback) {
|
||||
var self = this;
|
||||
this.promise.then(function(id) {
|
||||
self.callbacks.push(callback);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.trigger = function(messages) {
|
||||
for(var i = 0; i < this.callbacks.length; i++) {
|
||||
this.callbacks[i].call(this, messages);
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.uninstall = function() {
|
||||
var self = this;
|
||||
this.promise.then(function (id) {
|
||||
self.impl.uninstallFilter(id);
|
||||
web3.provider.stopPolling(id);
|
||||
web3.off(impl.changed, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.messages = function() {
|
||||
var self = this;
|
||||
return this.promise.then(function (id) {
|
||||
return self.impl.getMessages(id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.logs = function () {
|
||||
return this.messages();
|
||||
};
|
||||
|
||||
function messageHandler(data) {
|
||||
if(data._event !== undefined) {
|
||||
web3.trigger(data._event, data._id, data.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if(data._id) {
|
||||
var cb = web3._callbacks[data._id];
|
||||
if (cb) {
|
||||
cb.call(this, data.error, data.data);
|
||||
delete web3._callbacks[data._id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = web3;
|
78
cmd/mist/assets/ext/ethereum.js/lib/websocket.js
Normal file
78
cmd/mist/assets/ext/ethereum.js/lib/websocket.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file websocket.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: is these line is supposed to be here?
|
||||
if (process.env.NODE_ENV !== 'build') {
|
||||
var WebSocket = require('ws'); // jshint ignore:line
|
||||
}
|
||||
|
||||
var WebSocketProvider = function(host) {
|
||||
// onmessage handlers
|
||||
this.handlers = [];
|
||||
// queue will be filled with messages if send is invoked before the ws is ready
|
||||
this.queued = [];
|
||||
this.ready = false;
|
||||
|
||||
this.ws = new WebSocket(host);
|
||||
|
||||
var self = this;
|
||||
this.ws.onmessage = function(event) {
|
||||
for(var i = 0; i < self.handlers.length; i++) {
|
||||
self.handlers[i].call(self, JSON.parse(event.data), event);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onopen = function() {
|
||||
self.ready = true;
|
||||
|
||||
for(var i = 0; i < self.queued.length; i++) {
|
||||
// Resend
|
||||
self.send(self.queued[i]);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.send = function(payload) {
|
||||
if(this.ready) {
|
||||
var data = JSON.stringify(payload);
|
||||
|
||||
this.ws.send(data);
|
||||
} else {
|
||||
this.queued.push(payload);
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.onMessage = function(handler) {
|
||||
this.handlers.push(handler);
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.unload = function() {
|
||||
this.ws.close();
|
||||
};
|
||||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
|
||||
set: function(provider) { this.onMessage(provider); }
|
||||
});
|
||||
|
||||
if (typeof(module) !== "undefined")
|
||||
module.exports = WebSocketProvider;
|
67
cmd/mist/assets/ext/ethereum.js/package.json
Normal file
67
cmd/mist/assets/ext/ethereum.js/package.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "ethereum.js",
|
||||
"namespace": "ethereum",
|
||||
"version": "0.0.6",
|
||||
"description": "Ethereum Compatible JavaScript API",
|
||||
"main": "./index.js",
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"es6-promise": "*",
|
||||
"ws": "*",
|
||||
"xmlhttprequest": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": ">=1.3.0",
|
||||
"browserify": ">=6.0",
|
||||
"del": ">=0.1.1",
|
||||
"envify": "^3.0.0",
|
||||
"exorcist": "^0.1.6",
|
||||
"gulp": ">=3.4.0",
|
||||
"gulp-jshint": ">=1.5.0",
|
||||
"gulp-rename": ">=1.2.0",
|
||||
"gulp-uglify": ">=1.0.0",
|
||||
"jshint": ">=2.5.0",
|
||||
"uglifyify": "^2.6.0",
|
||||
"unreachable-branch-transform": "^0.1.0",
|
||||
"vinyl-source-stream": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "gulp watch",
|
||||
"lint": "gulp lint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||
},
|
||||
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"javascript",
|
||||
"API"
|
||||
],
|
||||
"author": "ethdev.com",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeffery Wilcke",
|
||||
"email": "jeff@ethdev.com",
|
||||
"url": "https://github.com/obscuren"
|
||||
},
|
||||
{
|
||||
"name": "Marek Kotewicz",
|
||||
"email": "marek@ethdev.com",
|
||||
"url": "https://github.com/debris"
|
||||
},
|
||||
{
|
||||
"name": "Marian Oancea",
|
||||
"email": "marian@ethdev.com",
|
||||
"url": "https://github.com/cubedro"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick 2.1
|
||||
import QtWebKit 3.0
|
||||
import QtWebKit.experimental 1.0
|
||||
import QtQuick.Controls 1.0;
|
||||
@ -8,437 +8,484 @@ import QtQuick.Window 2.1;
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: window
|
||||
property var title: "Browser"
|
||||
property var iconSource: "../browser.png"
|
||||
property var menuItem
|
||||
id: window
|
||||
objectName: "browserView"
|
||||
anchors.fill: parent
|
||||
color: "#00000000"
|
||||
|
||||
property alias url: webview.url
|
||||
property alias webView: webview
|
||||
property var title: "Browser"
|
||||
property var iconSource: "../browser.png"
|
||||
property var menuItem
|
||||
|
||||
property var cleanPath: false
|
||||
property var open: function(url) {
|
||||
if(!window.cleanPath) {
|
||||
var uri = url;
|
||||
if(!/.*\:\/\/.*/.test(uri)) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
property alias url: webview.url
|
||||
property alias webView: webview
|
||||
|
||||
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
|
||||
property var cleanPath: false
|
||||
property var open: function(url) {
|
||||
if(!window.cleanPath) {
|
||||
var uri = url;
|
||||
if(!/.*\:\/\/.*/.test(uri)) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
|
||||
if(reg.test(uri)) {
|
||||
uri.replace(reg, function(match, pre, domain, path) {
|
||||
uri = pre;
|
||||
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
|
||||
|
||||
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
||||
var ip = [];
|
||||
for(var i = 0, l = lookup.length; i < l; i++) {
|
||||
ip.push(lookup.charCodeAt(i))
|
||||
}
|
||||
if(reg.test(uri)) {
|
||||
uri.replace(reg, function(match, pre, domain, path) {
|
||||
uri = pre;
|
||||
|
||||
if(ip.length != 0) {
|
||||
uri += lookup;
|
||||
} else {
|
||||
uri += domain;
|
||||
}
|
||||
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
||||
var ip = [];
|
||||
for(var i = 0, l = lookup.length; i < l; i++) {
|
||||
ip.push(lookup.charCodeAt(i))
|
||||
}
|
||||
|
||||
uri += path;
|
||||
});
|
||||
}
|
||||
if(ip.length != 0) {
|
||||
uri += lookup;
|
||||
} else {
|
||||
uri += domain;
|
||||
}
|
||||
|
||||
window.cleanPath = true;
|
||||
uri += path;
|
||||
});
|
||||
}
|
||||
|
||||
webview.url = uri;
|
||||
window.cleanPath = true;
|
||||
|
||||
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
|
||||
uriNav.text = uri;
|
||||
} else {
|
||||
// Prevent inf loop.
|
||||
window.cleanPath = false;
|
||||
}
|
||||
}
|
||||
webview.url = uri;
|
||||
|
||||
Component.onCompleted: {
|
||||
webview.url = "http://etherian.io"
|
||||
}
|
||||
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
|
||||
uriNav.text = uri;
|
||||
} else {
|
||||
// Prevent inf loop.
|
||||
window.cleanPath = false;
|
||||
}
|
||||
}
|
||||
|
||||
signal messages(var messages, int id);
|
||||
onMessages: {
|
||||
// Bit of a cheat to get proper JSON
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
webview.postEvent("messages", id, m);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
webview.url = "http://etherian.io"
|
||||
}
|
||||
|
||||
function onShhMessage(message, id) {
|
||||
webview.postEvent("shhChanged", id, message)
|
||||
}
|
||||
signal messages(var messages, int id);
|
||||
onMessages: {
|
||||
// Bit of a cheat to get proper JSON
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
webview.postEvent("eth_changed", id, m);
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "root"
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
state: "inspectorShown"
|
||||
function onShhMessage(message, id) {
|
||||
webview.postEvent("shh_changed", id, message)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: navBar
|
||||
height: 40
|
||||
Item {
|
||||
objectName: "root"
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
state: "inspectorShown"
|
||||
|
||||
RowLayout {
|
||||
id: navBar
|
||||
height: 40
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 7
|
||||
}
|
||||
|
||||
Button {
|
||||
id: back
|
||||
onClicked: {
|
||||
webview.goBack()
|
||||
}
|
||||
style: ButtonStyle {
|
||||
background: Image {
|
||||
source: "../back.png"
|
||||
width: 30
|
||||
height: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
anchors {
|
||||
left: back.right
|
||||
right: toggleInspector.left
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
text: webview.url;
|
||||
id: uriNav
|
||||
y: parent.height / 2 - this.height / 2
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
webview.url = this.text;
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: toggleInspector
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
iconSource: "../bug.png"
|
||||
onClicked: {
|
||||
if(inspector.visible == true){
|
||||
inspector.visible = false
|
||||
}else{
|
||||
inspector.visible = true
|
||||
inspector.url = webview.experimental.remoteInspectorUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Border
|
||||
Rectangle {
|
||||
id: divider
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 7
|
||||
}
|
||||
|
||||
Button {
|
||||
id: back
|
||||
onClicked: {
|
||||
webview.goBack()
|
||||
}
|
||||
style: ButtonStyle {
|
||||
background: Image {
|
||||
source: "../back.png"
|
||||
width: 30
|
||||
height: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
anchors {
|
||||
left: back.right
|
||||
right: toggleInspector.left
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
text: "http://etherian.io"
|
||||
id: uriNav
|
||||
y: parent.height / 2 - this.height / 2
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
webview.url = this.text;
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: toggleInspector
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
iconSource: "../bug.png"
|
||||
onClicked: {
|
||||
if(inspector.visible == true){
|
||||
inspector.visible = false
|
||||
}else{
|
||||
inspector.visible = true
|
||||
inspector.url = webview.experimental.remoteInspectorUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WebView {
|
||||
objectName: "webView"
|
||||
id: webview
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: navBar.bottom
|
||||
}
|
||||
z: -1
|
||||
height: 1
|
||||
color: "#CCCCCC"
|
||||
}
|
||||
|
||||
//property var cleanPath: false
|
||||
onNavigationRequested: {
|
||||
window.open(request.url.toString());
|
||||
}
|
||||
|
||||
function sendMessage(data) {
|
||||
webview.experimental.postMessage(JSON.stringify(data))
|
||||
}
|
||||
|
||||
|
||||
experimental.preferences.javascriptEnabled: true
|
||||
experimental.preferences.navigatorQtObjectEnabled: true
|
||||
experimental.preferences.developerExtrasEnabled: true
|
||||
//experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
|
||||
experimental.userScripts: ["../ext/q.js", "../ext/eth.js/main.js", "../ext/eth.js/qt.js", "../ext/setup.js"]
|
||||
experimental.onMessageReceived: {
|
||||
console.log("[onMessageReceived]: ", message.data)
|
||||
// TODO move to messaging.js
|
||||
var data = JSON.parse(message.data)
|
||||
|
||||
try {
|
||||
switch(data.call) {
|
||||
case "compile":
|
||||
postData(data._id, eth.compile(data.args[0]))
|
||||
break
|
||||
|
||||
case "coinbase":
|
||||
postData(data._id, eth.coinBase())
|
||||
|
||||
case "account":
|
||||
postData(data._id, eth.key().address);
|
||||
|
||||
case "isListening":
|
||||
postData(data._id, eth.isListening())
|
||||
|
||||
break
|
||||
|
||||
case "isMining":
|
||||
postData(data._id, eth.isMining())
|
||||
|
||||
break
|
||||
|
||||
case "peerCount":
|
||||
postData(data._id, eth.peerCount())
|
||||
|
||||
break
|
||||
|
||||
case "countAt":
|
||||
require(1)
|
||||
postData(data._id, eth.txCountAt(data.args[0]))
|
||||
|
||||
break
|
||||
|
||||
case "codeAt":
|
||||
require(1)
|
||||
var code = eth.codeAt(data.args[0])
|
||||
postData(data._id, code);
|
||||
|
||||
break
|
||||
|
||||
case "blockByNumber":
|
||||
require(1)
|
||||
var block = eth.blockByNumber(data.args[0])
|
||||
postData(data._id, block)
|
||||
break
|
||||
|
||||
case "blockByHash":
|
||||
require(1)
|
||||
var block = eth.blockByHash(data.args[0])
|
||||
postData(data._id, block)
|
||||
break
|
||||
|
||||
require(2)
|
||||
var block = eth.blockByHash(data.args[0])
|
||||
postData(data._id, block.transactions[data.args[1]])
|
||||
break
|
||||
|
||||
case "transactionByHash":
|
||||
case "transactionByNumber":
|
||||
require(2)
|
||||
|
||||
var block;
|
||||
if (data.call === "transactionByHash")
|
||||
block = eth.blockByHash(data.args[0])
|
||||
else
|
||||
block = eth.blockByNumber(data.args[0])
|
||||
|
||||
var tx = block.transactions.get(data.args[1])
|
||||
|
||||
postData(data._id, tx)
|
||||
break
|
||||
|
||||
case "uncleByHash":
|
||||
case "uncleByNumber":
|
||||
require(2)
|
||||
|
||||
var block;
|
||||
if (data.call === "uncleByHash")
|
||||
block = eth.blockByHash(data.args[0])
|
||||
else
|
||||
block = eth.blockByNumber(data.args[0])
|
||||
|
||||
var uncle = block.uncles.get(data.args[1])
|
||||
|
||||
postData(data._id, uncle)
|
||||
|
||||
break
|
||||
|
||||
case "transact":
|
||||
require(5)
|
||||
|
||||
var tx = eth.transact(data.args)
|
||||
postData(data._id, tx)
|
||||
|
||||
break
|
||||
|
||||
case "stateAt":
|
||||
require(2);
|
||||
|
||||
var storage = eth.storageAt(data.args[0], data.args[1]);
|
||||
postData(data._id, storage)
|
||||
|
||||
break
|
||||
|
||||
case "call":
|
||||
require(1);
|
||||
var ret = eth.call(data.args)
|
||||
postData(data._id, ret)
|
||||
break
|
||||
|
||||
case "balanceAt":
|
||||
require(1);
|
||||
|
||||
postData(data._id, eth.balanceAt(data.args[0]));
|
||||
break
|
||||
|
||||
case "watch":
|
||||
require(2)
|
||||
eth.watch(data.args[0], data.args[1])
|
||||
|
||||
case "disconnect":
|
||||
require(1)
|
||||
postData(data._id, null)
|
||||
break;
|
||||
|
||||
case "messages":
|
||||
require(1);
|
||||
|
||||
var messages = JSON.parse(eth.getMessages(data.args[0]))
|
||||
postData(data._id, messages)
|
||||
break
|
||||
|
||||
case "mutan":
|
||||
require(1)
|
||||
|
||||
var code = eth.compileMutan(data.args[0])
|
||||
postData(data._id, "0x"+code)
|
||||
break;
|
||||
|
||||
case "newFilterString":
|
||||
require(1)
|
||||
var id = eth.newFilterString(data.args[0])
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "newFilter":
|
||||
require(1)
|
||||
var id = eth.newFilter(data.args[0])
|
||||
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "getMessages":
|
||||
require(1);
|
||||
|
||||
var messages = eth.messages(data.args[0]);
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
postData(data._id, m);
|
||||
|
||||
break;
|
||||
|
||||
case "deleteFilter":
|
||||
require(1);
|
||||
eth.uninstallFilter(data.args[0])
|
||||
break;
|
||||
|
||||
|
||||
case "shhNewFilter":
|
||||
require(1);
|
||||
var id = shh.watch(data.args[0], window);
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "newIdentity":
|
||||
postData(data._id, shh.newIdentity())
|
||||
break
|
||||
|
||||
case "post":
|
||||
require(1);
|
||||
var params = data.args[0];
|
||||
var fields = ["payload", "to", "from"];
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
params[fields[i]] = params[fields[i]] || "";
|
||||
WebView {
|
||||
objectName: "webView"
|
||||
id: webview
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: divider.bottom
|
||||
}
|
||||
if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
|
||||
params.topics = params.topics || [];
|
||||
params.priority = params.priority || 1000;
|
||||
params.ttl = params.ttl || 100;
|
||||
|
||||
console.log(JSON.stringify(params))
|
||||
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(data.call + ": " + e)
|
||||
function injectJs(js) {
|
||||
webview.experimental.navigatorQtObjectEnabled = true;
|
||||
webview.experimental.evaluateJavaScript(js)
|
||||
webview.experimental.javascriptEnabled = true;
|
||||
}
|
||||
|
||||
postData(data._id, null);
|
||||
}
|
||||
}
|
||||
function sendMessage(data) {
|
||||
webview.experimental.postMessage(JSON.stringify(data))
|
||||
}
|
||||
|
||||
|
||||
function post(seed, data) {
|
||||
postData(data._id, data)
|
||||
}
|
||||
experimental.preferences.javascriptEnabled: true
|
||||
experimental.preferences.webGLEnabled: true
|
||||
experimental.itemSelector: MouseArea {
|
||||
// To avoid conflicting with ListView.model when inside Initiator context.
|
||||
property QtObject selectorModel: model
|
||||
anchors.fill: parent
|
||||
onClicked: selectorModel.reject()
|
||||
|
||||
function require(args, num) {
|
||||
if(args.length < num) {
|
||||
throw("required argument count of "+num+" got "+args.length);
|
||||
}
|
||||
}
|
||||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||
}
|
||||
function postEvent(event, id, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
|
||||
}
|
||||
Menu {
|
||||
visible: true
|
||||
id: itemSelector
|
||||
|
||||
function onWatchedCb(data, id) {
|
||||
var messages = JSON.parse(data)
|
||||
postEvent("watched:"+id, messages)
|
||||
}
|
||||
Instantiator {
|
||||
model: selectorModel.items
|
||||
delegate: MenuItem {
|
||||
text: model.text
|
||||
onTriggered: {
|
||||
selectorModel.accept(index)
|
||||
}
|
||||
}
|
||||
onObjectAdded: itemSelector.insertItem(index, object)
|
||||
onObjectRemoved: itemSelector.removeItem(object)
|
||||
}
|
||||
}
|
||||
|
||||
function onNewBlockCb(block) {
|
||||
postEvent("block:new", block)
|
||||
}
|
||||
function onObjectChangeCb(stateObject) {
|
||||
postEvent("object:"+stateObject.address(), stateObject)
|
||||
}
|
||||
function onStorageChangeCb(storageObject) {
|
||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
||||
postEvent(ev, [storageObject.address, storageObject.value])
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
itemSelector.popup()
|
||||
}
|
||||
}
|
||||
experimental.preferences.webAudioEnabled: true
|
||||
experimental.preferences.navigatorQtObjectEnabled: true
|
||||
experimental.preferences.developerExtrasEnabled: true
|
||||
experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"]
|
||||
experimental.onMessageReceived: {
|
||||
console.log("[onMessageReceived]: ", message.data)
|
||||
// TODO move to messaging.js
|
||||
var data = JSON.parse(message.data)
|
||||
|
||||
try {
|
||||
switch(data.call) {
|
||||
case "eth_compile":
|
||||
postData(data._id, eth.compile(data.args[0]))
|
||||
break
|
||||
|
||||
case "eth_coinbase":
|
||||
postData(data._id, eth.coinBase())
|
||||
|
||||
case "eth_account":
|
||||
postData(data._id, eth.key().address);
|
||||
|
||||
case "eth_istening":
|
||||
postData(data._id, eth.isListening())
|
||||
|
||||
break
|
||||
|
||||
case "eth_mining":
|
||||
postData(data._id, eth.isMining())
|
||||
|
||||
break
|
||||
|
||||
case "eth_peerCount":
|
||||
postData(data._id, eth.peerCount())
|
||||
|
||||
break
|
||||
|
||||
case "eth_countAt":
|
||||
require(1)
|
||||
postData(data._id, eth.txCountAt(data.args[0]))
|
||||
|
||||
break
|
||||
|
||||
case "eth_codeAt":
|
||||
require(1)
|
||||
var code = eth.codeAt(data.args[0])
|
||||
postData(data._id, code);
|
||||
|
||||
break
|
||||
|
||||
case "eth_blockByNumber":
|
||||
require(1)
|
||||
var block = eth.blockByNumber(data.args[0])
|
||||
postData(data._id, block)
|
||||
break
|
||||
|
||||
case "eth_blockByHash":
|
||||
require(1)
|
||||
var block = eth.blockByHash(data.args[0])
|
||||
postData(data._id, block)
|
||||
break
|
||||
|
||||
require(2)
|
||||
var block = eth.blockByHash(data.args[0])
|
||||
postData(data._id, block.transactions[data.args[1]])
|
||||
break
|
||||
|
||||
case "eth_transactionByHash":
|
||||
case "eth_transactionByNumber":
|
||||
require(2)
|
||||
|
||||
var block;
|
||||
if (data.call === "transactionByHash")
|
||||
block = eth.blockByHash(data.args[0])
|
||||
else
|
||||
block = eth.blockByNumber(data.args[0])
|
||||
|
||||
var tx = block.transactions.get(data.args[1])
|
||||
|
||||
postData(data._id, tx)
|
||||
break
|
||||
|
||||
case "eth_uncleByHash":
|
||||
case "eth_uncleByNumber":
|
||||
require(2)
|
||||
|
||||
var block;
|
||||
if (data.call === "uncleByHash")
|
||||
block = eth.blockByHash(data.args[0])
|
||||
else
|
||||
block = eth.blockByNumber(data.args[0])
|
||||
|
||||
var uncle = block.uncles.get(data.args[1])
|
||||
|
||||
postData(data._id, uncle)
|
||||
|
||||
break
|
||||
|
||||
case "transact":
|
||||
require(5)
|
||||
|
||||
var tx = eth.transact(data.args)
|
||||
postData(data._id, tx)
|
||||
|
||||
break
|
||||
|
||||
case "eth_stateAt":
|
||||
require(2);
|
||||
|
||||
var storage = eth.storageAt(data.args[0], data.args[1]);
|
||||
postData(data._id, storage)
|
||||
|
||||
break
|
||||
|
||||
case "eth_call":
|
||||
require(1);
|
||||
var ret = eth.call(data.args)
|
||||
postData(data._id, ret)
|
||||
break
|
||||
|
||||
case "eth_balanceAt":
|
||||
require(1);
|
||||
|
||||
postData(data._id, eth.balanceAt(data.args[0]));
|
||||
break
|
||||
|
||||
case "eth_watch":
|
||||
require(2)
|
||||
eth.watch(data.args[0], data.args[1])
|
||||
|
||||
case "eth_disconnect":
|
||||
require(1)
|
||||
postData(data._id, null)
|
||||
break;
|
||||
|
||||
case "eth_newFilterString":
|
||||
require(1)
|
||||
var id = eth.newFilterString(data.args[0])
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "eth_newFilter":
|
||||
require(1)
|
||||
var id = eth.newFilter(data.args[0])
|
||||
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "eth_filterLogs":
|
||||
require(1);
|
||||
|
||||
var messages = eth.messages(data.args[0]);
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
postData(data._id, m);
|
||||
|
||||
break;
|
||||
|
||||
case "eth_deleteFilter":
|
||||
require(1);
|
||||
eth.uninstallFilter(data.args[0])
|
||||
break;
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: sizeGrip
|
||||
color: "gray"
|
||||
visible: false
|
||||
height: 10
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
}
|
||||
y: Math.round(root.height * 2 / 3)
|
||||
case "shh_newFilter":
|
||||
require(1);
|
||||
var id = shh.watch(data.args[0], window);
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: sizeGrip
|
||||
drag.minimumY: 0
|
||||
drag.maximumY: root.height
|
||||
drag.axis: Drag.YAxis
|
||||
}
|
||||
}
|
||||
case "shh_newIdentity":
|
||||
var id = shh.newIdentity()
|
||||
postData(data._id, id)
|
||||
|
||||
WebView {
|
||||
id: inspector
|
||||
visible: false
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
top: sizeGrip.bottom
|
||||
bottom: root.bottom
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "inspectorShown"
|
||||
PropertyChanges {
|
||||
target: inspector
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
case "shh_post":
|
||||
require(1);
|
||||
|
||||
var params = data.args[0];
|
||||
var fields = ["payload", "to", "from"];
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
params[fields[i]] = params[fields[i]] || "";
|
||||
}
|
||||
if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
|
||||
params.topics = params.topics || [];
|
||||
params.priority = params.priority || 1000;
|
||||
params.ttl = params.ttl || 100;
|
||||
|
||||
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
|
||||
|
||||
break;
|
||||
|
||||
case "shh_getMessages":
|
||||
require(1);
|
||||
|
||||
var m = shh.messages(data.args[0]);
|
||||
var messages = JSON.parse(JSON.parse(JSON.stringify(m)));
|
||||
postData(data._id, messages);
|
||||
|
||||
break;
|
||||
|
||||
case "ssh_newGroup":
|
||||
postData(data._id, "");
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(data.call + ": " + e)
|
||||
|
||||
postData(data._id, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function post(seed, data) {
|
||||
postData(data._id, data)
|
||||
}
|
||||
|
||||
function require(args, num) {
|
||||
if(args.length < num) {
|
||||
throw("required argument count of "+num+" got "+args.length);
|
||||
}
|
||||
}
|
||||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||
}
|
||||
function postEvent(event, id, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
|
||||
}
|
||||
|
||||
function onWatchedCb(data, id) {
|
||||
var messages = JSON.parse(data)
|
||||
postEvent("watched:"+id, messages)
|
||||
}
|
||||
|
||||
function onNewBlockCb(block) {
|
||||
postEvent("block:new", block)
|
||||
}
|
||||
function onObjectChangeCb(stateObject) {
|
||||
postEvent("object:"+stateObject.address(), stateObject)
|
||||
}
|
||||
function onStorageChangeCb(storageObject) {
|
||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
||||
postEvent(ev, [storageObject.address, storageObject.value])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: sizeGrip
|
||||
color: "gray"
|
||||
visible: false
|
||||
height: 10
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
}
|
||||
y: Math.round(root.height * 2 / 3)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: sizeGrip
|
||||
drag.minimumY: 0
|
||||
drag.maximumY: root.height
|
||||
drag.axis: Drag.YAxis
|
||||
}
|
||||
}
|
||||
|
||||
WebView {
|
||||
id: inspector
|
||||
visible: false
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
top: sizeGrip.bottom
|
||||
bottom: root.bottom
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "inspectorShown"
|
||||
PropertyChanges {
|
||||
target: inspector
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +59,19 @@ ApplicationWindow {
|
||||
|
||||
mainSplit.setView(wallet.view, wallet.menuItem);
|
||||
|
||||
// Call the ready handler
|
||||
gui.done();
|
||||
// Command setup
|
||||
gui.sendCommand(0)
|
||||
}
|
||||
|
||||
function activeView(view, menuItem) {
|
||||
mainSplit.setView(view, menuItem)
|
||||
if (view.objectName === "browserView") {
|
||||
urlPane.visible = false;
|
||||
mainView.anchors.top = rootView.top
|
||||
} else {
|
||||
urlPane.visible = true;
|
||||
mainView.anchors.top = divider.bottom
|
||||
}
|
||||
}
|
||||
|
||||
function addViews(view, path, options) {
|
||||
@ -284,6 +295,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
visible: false
|
||||
id: downloadIndicator
|
||||
value: 0
|
||||
objectName: "downloadIndicator"
|
||||
@ -293,6 +305,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: false
|
||||
objectName: "downloadLabel"
|
||||
//y: 7
|
||||
anchors.left: downloadIndicator.right
|
||||
@ -445,7 +458,7 @@ ApplicationWindow {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
mainSplit.setView(view, menuItem)
|
||||
activeView(view, menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,14 +525,14 @@ ApplicationWindow {
|
||||
var section;
|
||||
switch(options.section) {
|
||||
case "ethereum":
|
||||
section = menuDefault;
|
||||
break;
|
||||
section = menuDefault;
|
||||
break;
|
||||
case "legacy":
|
||||
section = menuLegacy;
|
||||
break;
|
||||
section = menuLegacy;
|
||||
break;
|
||||
default:
|
||||
section = menuApps;
|
||||
break;
|
||||
section = menuApps;
|
||||
break;
|
||||
}
|
||||
|
||||
var comp = menuItemTemplate.createObject(section)
|
||||
@ -606,6 +619,7 @@ ApplicationWindow {
|
||||
* Main view
|
||||
********************/
|
||||
Rectangle {
|
||||
id: rootView
|
||||
anchors.right: parent.right
|
||||
anchors.left: menu.right
|
||||
anchors.bottom: parent.bottom
|
||||
@ -639,8 +653,7 @@ ApplicationWindow {
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if(/^https?/.test(this.text)) {
|
||||
root.browser.view.open(this.text);
|
||||
mainSplit.setView(root.browser.view, root.browser.menuItem);
|
||||
activeView(root.browser.view, root.browser.menuItem);
|
||||
} else {
|
||||
addPlugin(this.text, {close: true, section: "apps"})
|
||||
}
|
||||
@ -864,13 +877,13 @@ ApplicationWindow {
|
||||
Component.onCompleted: {
|
||||
pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"})
|
||||
/*
|
||||
var ips = eth.pastPeers()
|
||||
for(var i = 0; i < ips.length; i++) {
|
||||
pastPeers.append({text: ips.get(i)})
|
||||
}
|
||||
var ips = eth.pastPeers()
|
||||
for(var i = 0; i < ips.length; i++) {
|
||||
pastPeers.append({text: ips.get(i)})
|
||||
}
|
||||
|
||||
pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"})
|
||||
*/
|
||||
pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"})
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ Rectangle {
|
||||
text: "Start"
|
||||
onClicked: {
|
||||
eth.setGasPrice(minGasPrice.text || "10000000000000");
|
||||
eth.setExtra(blockExtra.text)
|
||||
if (eth.toggleMining()) {
|
||||
this.text = "Stop";
|
||||
} else {
|
||||
@ -55,6 +56,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: minGasPriceRect
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
width: 200
|
||||
@ -65,6 +67,23 @@ Rectangle {
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 300
|
||||
anchors {
|
||||
left: minGasPriceRect.right
|
||||
leftMargin: 5
|
||||
top: parent.top
|
||||
topMargin: 2
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: blockExtra
|
||||
placeholderText: "Extra"
|
||||
width: parent.width
|
||||
maximumLength: 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -26,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
@ -118,7 +122,7 @@ func (self *Gui) DumpState(hash, path string) {
|
||||
return
|
||||
}
|
||||
|
||||
stateDump = block.State().Dump()
|
||||
stateDump = state.New(block.Root(), self.eth.Db()).Dump()
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -37,7 +40,7 @@ type DebuggerWindow struct {
|
||||
engine *qml.Engine
|
||||
lib *UiLib
|
||||
|
||||
vm *vm.DebugVm
|
||||
vm *vm.Vm
|
||||
Db *Debugger
|
||||
|
||||
state *state.StateDB
|
||||
@ -54,7 +57,7 @@ func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
||||
|
||||
win := component.CreateWindow(nil)
|
||||
|
||||
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.DebugVm{}}
|
||||
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.Vm{}}
|
||||
w.Db = NewDebugger(w)
|
||||
|
||||
return w
|
||||
@ -264,6 +267,9 @@ type storeVal struct {
|
||||
Key, Value string
|
||||
}
|
||||
|
||||
func (self *Debugger) Step(evm vm.VirtualMachine, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, context *vm.Context) {
|
||||
}
|
||||
|
||||
func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
|
||||
self.main.Logln("break on instr:", pc)
|
||||
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -89,7 +92,7 @@ func defaultAssetPath() string {
|
||||
}
|
||||
func defaultDataDir() string {
|
||||
usr, _ := user.Current()
|
||||
return path.Join(usr.HomeDir, ".mist")
|
||||
return path.Join(usr.HomeDir, ".ethereum")
|
||||
}
|
||||
|
||||
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
|
||||
|
296
cmd/mist/gui.go
296
cmd/mist/gui.go
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import "C"
|
||||
@ -23,7 +26,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@ -45,15 +50,22 @@ import (
|
||||
|
||||
var guilogger = logger.NewLogger("GUI")
|
||||
|
||||
type ServEv byte
|
||||
|
||||
const (
|
||||
setup ServEv = iota
|
||||
update
|
||||
)
|
||||
|
||||
type Gui struct {
|
||||
// The main application window
|
||||
win *qml.Window
|
||||
// QML Engine
|
||||
engine *qml.Engine
|
||||
component *qml.Common
|
||||
qmlDone bool
|
||||
// The ethereum interface
|
||||
eth *eth.Ethereum
|
||||
eth *eth.Ethereum
|
||||
serviceEvents chan ServEv
|
||||
|
||||
// The public Ethereum library
|
||||
uiLib *UiLib
|
||||
@ -83,7 +95,17 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
|
||||
}
|
||||
|
||||
xeth := xeth.NewJSXEth(ethereum)
|
||||
gui := &Gui{eth: ethereum, txDb: db, xeth: xeth, logLevel: logger.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
|
||||
gui := &Gui{eth: ethereum,
|
||||
txDb: db,
|
||||
xeth: xeth,
|
||||
logLevel: logger.LogLevel(logLevel),
|
||||
Session: session,
|
||||
open: false,
|
||||
clientIdentity: clientIdentity,
|
||||
config: config,
|
||||
plugins: make(map[string]plugin),
|
||||
serviceEvents: make(chan ServEv, 1),
|
||||
}
|
||||
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
|
||||
json.Unmarshal([]byte(data), &gui.plugins)
|
||||
|
||||
@ -95,6 +117,8 @@ func (gui *Gui) Start(assetPath string) {
|
||||
|
||||
guilogger.Infoln("Starting GUI")
|
||||
|
||||
go gui.service()
|
||||
|
||||
// Register ethereum functions
|
||||
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||
Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
||||
@ -114,18 +138,7 @@ func (gui *Gui) Start(assetPath string) {
|
||||
context.SetVar("eth", gui.uiLib)
|
||||
context.SetVar("shh", gui.whisper)
|
||||
|
||||
// Load the main QML interface
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
|
||||
var win *qml.Window
|
||||
var err error
|
||||
var addlog = false
|
||||
if len(data) == 0 {
|
||||
win, err = gui.showKeyImport(context)
|
||||
} else {
|
||||
win, err = gui.showWallet(context)
|
||||
addlog = true
|
||||
}
|
||||
win, err := gui.showWallet(context)
|
||||
if err != nil {
|
||||
guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err)
|
||||
|
||||
@ -136,9 +149,7 @@ func (gui *Gui) Start(assetPath string) {
|
||||
win.Show()
|
||||
|
||||
// only add the gui guilogger after window is shown otherwise slider wont be shown
|
||||
if addlog {
|
||||
logger.AddLogSystem(gui)
|
||||
}
|
||||
logger.AddLogSystem(gui)
|
||||
win.Wait()
|
||||
|
||||
// need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
|
||||
@ -164,18 +175,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gui.win = gui.createWindow(component)
|
||||
|
||||
gui.update()
|
||||
gui.createWindow(component)
|
||||
|
||||
return gui.win, nil
|
||||
}
|
||||
|
||||
// The done handler will be called by QML when all views have been loaded
|
||||
func (gui *Gui) Done() {
|
||||
gui.qmlDone = true
|
||||
}
|
||||
|
||||
func (gui *Gui) ImportKey(filePath string) {
|
||||
}
|
||||
|
||||
@ -189,10 +193,8 @@ func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
|
||||
}
|
||||
|
||||
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
|
||||
win := comp.CreateWindow(nil)
|
||||
|
||||
gui.win = win
|
||||
gui.uiLib.win = win
|
||||
gui.win = comp.CreateWindow(nil)
|
||||
gui.uiLib.win = gui.win
|
||||
|
||||
return gui.win
|
||||
}
|
||||
@ -272,7 +274,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
||||
}
|
||||
|
||||
var (
|
||||
ptx = xeth.NewJSTx(tx, gui.xeth.World().State())
|
||||
ptx = xeth.NewJSTx(tx)
|
||||
send = nameReg.Storage(tx.From())
|
||||
rec = nameReg.Storage(tx.To())
|
||||
s, r string
|
||||
@ -345,11 +347,48 @@ func (self *Gui) getObjectByName(objectName string) qml.Object {
|
||||
return self.win.Root().ObjectByName(objectName)
|
||||
}
|
||||
|
||||
// Simple go routine function that updates the list of peers in the GUI
|
||||
func (gui *Gui) update() {
|
||||
// We have to wait for qml to be done loading all the windows.
|
||||
for !gui.qmlDone {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
func loadJavascriptAssets(gui *Gui) (jsfiles string) {
|
||||
for _, fn := range []string{"ext/q.js", "ext/eth.js/main.js", "ext/eth.js/qt.js", "ext/setup.js"} {
|
||||
f, err := os.Open(gui.uiLib.AssetPath(fn))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
jsfiles += string(content)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (gui *Gui) SendCommand(cmd ServEv) {
|
||||
gui.serviceEvents <- cmd
|
||||
}
|
||||
|
||||
func (gui *Gui) service() {
|
||||
for ev := range gui.serviceEvents {
|
||||
switch ev {
|
||||
case setup:
|
||||
go gui.setup()
|
||||
case update:
|
||||
go gui.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) setup() {
|
||||
for gui.win == nil {
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
for _, plugin := range gui.plugins {
|
||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||
gui.win.Root().Call("addPlugin", plugin.Path, "")
|
||||
}
|
||||
|
||||
go func() {
|
||||
@ -359,14 +398,21 @@ func (gui *Gui) update() {
|
||||
gui.setPeerInfo()
|
||||
}()
|
||||
|
||||
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
|
||||
// Inject javascript files each time navigation is requested.
|
||||
// Unfortunately webview.experimental.userScripts injects _after_
|
||||
// the page has loaded which kind of renders it useless...
|
||||
//jsfiles := loadJavascriptAssets(gui)
|
||||
gui.getObjectByName("webView").On("navigationRequested", func() {
|
||||
//gui.getObjectByName("webView").Call("injectJs", jsfiles)
|
||||
})
|
||||
|
||||
for _, plugin := range gui.plugins {
|
||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||
gui.whisper.SetView(gui.getObjectByName("whisperView"))
|
||||
|
||||
gui.win.Root().Call("addPlugin", plugin.Path, "")
|
||||
}
|
||||
gui.SendCommand(update)
|
||||
}
|
||||
|
||||
// Simple go routine function that updates the list of peers in the GUI
|
||||
func (gui *Gui) update() {
|
||||
peerUpdateTicker := time.NewTicker(5 * time.Second)
|
||||
generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||
statsUpdateTicker := time.NewTicker(5 * time.Second)
|
||||
@ -385,77 +431,75 @@ func (gui *Gui) update() {
|
||||
core.TxPostEvent{},
|
||||
)
|
||||
|
||||
go func() {
|
||||
defer events.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case ev, isopen := <-events.Chan():
|
||||
if !isopen {
|
||||
return
|
||||
}
|
||||
switch ev := ev.(type) {
|
||||
case core.NewBlockEvent:
|
||||
gui.processBlock(ev.Block, false)
|
||||
if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
|
||||
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||
}
|
||||
|
||||
case core.TxPreEvent:
|
||||
tx := ev.Tx
|
||||
|
||||
tstate := gui.eth.ChainManager().TransState()
|
||||
cstate := gui.eth.ChainManager().State()
|
||||
|
||||
taccount := tstate.GetAccount(gui.address())
|
||||
caccount := cstate.GetAccount(gui.address())
|
||||
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
|
||||
|
||||
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
|
||||
gui.insertTransaction("pre", tx)
|
||||
|
||||
case core.TxPostEvent:
|
||||
tx := ev.Tx
|
||||
object := state.GetAccount(gui.address())
|
||||
|
||||
if bytes.Compare(tx.From(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Balance(), nil)
|
||||
state.UpdateStateObject(object)
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
gui.setPeerInfo()
|
||||
case <-generalUpdateTicker.C:
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||
|
||||
/*
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
|
||||
var (
|
||||
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
|
||||
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
|
||||
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
|
||||
)
|
||||
dlWidget.Set("value", pct)
|
||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||
*/
|
||||
|
||||
case <-statsUpdateTicker.C:
|
||||
gui.setStatsPane()
|
||||
defer events.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case ev, isopen := <-events.Chan():
|
||||
if !isopen {
|
||||
return
|
||||
}
|
||||
switch ev := ev.(type) {
|
||||
case core.NewBlockEvent:
|
||||
gui.processBlock(ev.Block, false)
|
||||
if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
|
||||
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||
}
|
||||
|
||||
case core.TxPreEvent:
|
||||
tx := ev.Tx
|
||||
|
||||
tstate := gui.eth.ChainManager().TransState()
|
||||
cstate := gui.eth.ChainManager().State()
|
||||
|
||||
taccount := tstate.GetAccount(gui.address())
|
||||
caccount := cstate.GetAccount(gui.address())
|
||||
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
|
||||
|
||||
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
|
||||
gui.insertTransaction("pre", tx)
|
||||
|
||||
case core.TxPostEvent:
|
||||
tx := ev.Tx
|
||||
object := state.GetAccount(gui.address())
|
||||
|
||||
if bytes.Compare(tx.From(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Balance(), nil)
|
||||
state.UpdateStateObject(object)
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
gui.setPeerInfo()
|
||||
case <-generalUpdateTicker.C:
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||
|
||||
/*
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
|
||||
var (
|
||||
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
|
||||
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
|
||||
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
|
||||
)
|
||||
dlWidget.Set("value", pct)
|
||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||
*/
|
||||
|
||||
case <-statsUpdateTicker.C:
|
||||
gui.setStatsPane()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) setStatsPane() {
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -1,19 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
|
@ -1,20 +1,23 @@
|
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -67,6 +70,7 @@ func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||
lib.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
||||
go lib.filterManager.Start()
|
||||
|
||||
return lib
|
||||
}
|
||||
@ -208,16 +212,16 @@ func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
|
||||
if len(object.Code) > 0 {
|
||||
dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code))
|
||||
dbWindow.SetCode(ethutil.Bytes2Hex(object.Code))
|
||||
}
|
||||
dbWindow.SetData("0x" + data)
|
||||
dbWindow.SetData(data)
|
||||
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func (self *UiLib) StartDbWithCode(code string) {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
dbWindow.SetCode("0x" + code)
|
||||
dbWindow.SetCode(code)
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
@ -279,6 +283,10 @@ func (self *UiLib) SetGasPrice(price string) {
|
||||
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
|
||||
}
|
||||
|
||||
func (self *UiLib) SetExtra(extra string) {
|
||||
self.miner.Extra = extra
|
||||
}
|
||||
|
||||
func (self *UiLib) ToggleMining() bool {
|
||||
if !self.miner.Mining() {
|
||||
self.miner.Start()
|
||||
|
@ -1,10 +1,25 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@ -12,29 +27,32 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
var (
|
||||
natType = flag.String("nat", "", "NAT traversal implementation")
|
||||
pmpGateway = flag.String("gateway", "", "gateway address for NAT-PMP")
|
||||
listenAddr = flag.String("addr", ":30301", "listen address")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
nat, err := p2p.ParseNAT(*natType, *pmpGateway)
|
||||
if err != nil {
|
||||
log.Fatal("invalid nat:", err)
|
||||
}
|
||||
|
||||
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel))
|
||||
key, _ := crypto.GenerateKey()
|
||||
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y)
|
||||
|
||||
srv := p2p.Server{
|
||||
MaxPeers: 100,
|
||||
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)),
|
||||
ListenAddr: ":30301",
|
||||
NAT: p2p.UPNP(),
|
||||
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", marshaled),
|
||||
ListenAddr: *listenAddr,
|
||||
NAT: nat,
|
||||
NoDial: true,
|
||||
}
|
||||
if err := srv.Start(); err != nil {
|
||||
fmt.Println("could not start server:", err)
|
||||
os.Exit(1)
|
||||
log.Fatal("could not start server:", err)
|
||||
}
|
||||
|
||||
// add seed peers
|
||||
seed, err := net.ResolveTCPAddr("tcp", "poc-8.ethdev.com:30303")
|
||||
if err != nil {
|
||||
fmt.Println("couldn't resolve:", err)
|
||||
} else {
|
||||
srv.SuggestPeer(seed.IP, seed.Port, nil)
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
||||
|
@ -14,6 +14,10 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Felix Lange <felix@ethdev.com>
|
||||
*/
|
||||
|
||||
// rlpdump is a pretty-printer for RLP data.
|
||||
package main
|
||||
@ -106,8 +110,7 @@ func dump(s *rlp.Stream, depth int) error {
|
||||
s.List()
|
||||
defer s.ListEnd()
|
||||
if size == 0 {
|
||||
fmt.Printf(ws(depth) + "[]")
|
||||
return nil
|
||||
fmt.Print(ws(depth) + "[]")
|
||||
} else {
|
||||
fmt.Println(ws(depth) + "[")
|
||||
for i := 0; ; i++ {
|
||||
|
@ -1,3 +1,24 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
* Viktor Tron <viktor@ethdev.com>
|
||||
*/
|
||||
package utils
|
||||
|
||||
import (
|
||||
@ -18,6 +39,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
@ -238,7 +260,8 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
||||
|
||||
parent := ethereum.ChainManager().GetBlock(block.ParentHash())
|
||||
|
||||
_, err := ethereum.BlockProcessor().TransitionState(parent.State(), parent, block)
|
||||
statedb := state.New(parent.Root(), ethereum.Db())
|
||||
_, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package utils
|
||||
|
||||
import (
|
||||
|
@ -1,9 +1,34 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Jeffrey Wilcke <i@jev.io>
|
||||
*/
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event/filter"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/ui"
|
||||
"github.com/ethereum/go-ethereum/websocket"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
@ -15,16 +40,19 @@ func args(v ...interface{}) []interface{} {
|
||||
}
|
||||
|
||||
type WebSocketServer struct {
|
||||
ethereum *eth.Ethereum
|
||||
filterCallbacks map[int][]int
|
||||
eth *eth.Ethereum
|
||||
filterManager *filter.FilterManager
|
||||
}
|
||||
|
||||
func NewWebSocketServer(eth *eth.Ethereum) *WebSocketServer {
|
||||
return &WebSocketServer{eth, make(map[int][]int)}
|
||||
filterManager := filter.NewFilterManager(eth.EventMux())
|
||||
go filterManager.Start()
|
||||
|
||||
return &WebSocketServer{eth, filterManager}
|
||||
}
|
||||
|
||||
func (self *WebSocketServer) Serv() {
|
||||
pipe := xeth.NewJSXEth(self.ethereum)
|
||||
pipe := xeth.NewJSXEth(self.eth)
|
||||
|
||||
wsServ := websocket.NewServer("/eth", ":40404")
|
||||
wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) {
|
||||
@ -33,74 +61,91 @@ func (self *WebSocketServer) Serv() {
|
||||
data := ethutil.NewValue(msg.Args)
|
||||
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
|
||||
if err != nil {
|
||||
c.Write(args(nil, err.Error()), msg.Seed)
|
||||
c.Write(args(nil, err.Error()), msg.Id)
|
||||
}
|
||||
|
||||
code := ethutil.Bytes2Hex(bcode)
|
||||
c.Write(args(code, nil), msg.Seed)
|
||||
case "getBlockByNumber":
|
||||
c.Write(args(code, nil), msg.Id)
|
||||
case "eth_blockByNumber":
|
||||
args := msg.Arguments()
|
||||
|
||||
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
|
||||
c.Write(block, msg.Seed)
|
||||
c.Write(block, msg.Id)
|
||||
|
||||
case "getKey":
|
||||
c.Write(pipe.Key().PrivateKey, msg.Seed)
|
||||
case "transact":
|
||||
case "eth_blockByHash":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Id)
|
||||
|
||||
case "eth_transact":
|
||||
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
||||
object := mapToTxParams(mp)
|
||||
c.Write(
|
||||
args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
|
||||
msg.Seed,
|
||||
args(pipe.Transact(pipe.Key().PrivateKey, object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
|
||||
msg.Id,
|
||||
)
|
||||
|
||||
}
|
||||
case "getCoinBase":
|
||||
c.Write(pipe.CoinBase(), msg.Seed)
|
||||
case "eth_gasPrice":
|
||||
c.Write("10000000000000", msg.Id)
|
||||
case "eth_coinbase":
|
||||
c.Write(pipe.CoinBase(), msg.Id)
|
||||
|
||||
case "getIsListening":
|
||||
c.Write(pipe.IsListening(), msg.Seed)
|
||||
case "eth_listening":
|
||||
c.Write(pipe.IsListening(), msg.Id)
|
||||
|
||||
case "getIsMining":
|
||||
c.Write(pipe.IsMining(), msg.Seed)
|
||||
case "eth_mining":
|
||||
c.Write(pipe.IsMining(), msg.Id)
|
||||
|
||||
case "getPeerCoint":
|
||||
c.Write(pipe.PeerCount(), msg.Seed)
|
||||
case "eth_peerCount":
|
||||
c.Write(pipe.PeerCount(), msg.Id)
|
||||
|
||||
case "getCountAt":
|
||||
case "eth_countAt":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Seed)
|
||||
c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Id)
|
||||
|
||||
case "getCodeAt":
|
||||
case "eth_codeAt":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Seed)
|
||||
c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Id)
|
||||
|
||||
case "getBlockByHash":
|
||||
case "eth_storageAt":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Seed)
|
||||
c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Id)
|
||||
|
||||
case "getStorageAt":
|
||||
case "eth_balanceAt":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Seed)
|
||||
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Id)
|
||||
|
||||
case "getBalanceAt":
|
||||
args := msg.Arguments()
|
||||
case "eth_accounts":
|
||||
c.Write(pipe.Accounts(), msg.Id)
|
||||
|
||||
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed)
|
||||
|
||||
case "getSecretToAddress":
|
||||
args := msg.Arguments()
|
||||
|
||||
c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed)
|
||||
|
||||
case "newFilter":
|
||||
case "newFilterString":
|
||||
case "messages":
|
||||
// TODO
|
||||
case "eth_newFilter":
|
||||
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
||||
var id int
|
||||
filter := ui.NewFilterFromMap(mp, self.eth)
|
||||
filter.MessageCallback = func(messages state.Messages) {
|
||||
c.Event(toMessages(messages), "eth_changed", id)
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
c.Write(id, msg.Id)
|
||||
}
|
||||
case "eth_newFilterString":
|
||||
var id int
|
||||
filter := core.NewFilter(self.eth)
|
||||
filter.BlockCallback = func(block *types.Block) {
|
||||
c.Event(nil, "eth_changed", id)
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
c.Write(id, msg.Id)
|
||||
case "eth_filterLogs":
|
||||
filter := self.filterManager.GetFilter(int(msg.Arguments().Get(0).Uint()))
|
||||
if filter != nil {
|
||||
c.Write(toMessages(filter.Find()), msg.Id)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
@ -108,6 +153,15 @@ func (self *WebSocketServer) Serv() {
|
||||
wsServ.Listen()
|
||||
}
|
||||
|
||||
func toMessages(messages state.Messages) (msgs []xeth.JSMessage) {
|
||||
msgs = make([]xeth.JSMessage, len(messages))
|
||||
for i, msg := range messages {
|
||||
msgs[i] = xeth.NewJSMessage(msg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func StartWebSockets(eth *eth.Ethereum) {
|
||||
wslogger.Infoln("Starting WebSockets")
|
||||
|
||||
|
@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
@ -36,6 +35,7 @@ type EthManager interface {
|
||||
}
|
||||
|
||||
type BlockProcessor struct {
|
||||
db ethutil.Database
|
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex
|
||||
// Canonical block chain
|
||||
@ -57,8 +57,9 @@ type BlockProcessor struct {
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewBlockProcessor(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
|
||||
func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
|
||||
sm := &BlockProcessor{
|
||||
db: db,
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: ezp.New(),
|
||||
bc: chainManager,
|
||||
@ -170,7 +171,8 @@ func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, msgs state.M
|
||||
func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
state := state.New(parent.Trie().Copy())
|
||||
state := state.New(parent.Root(), sm.db)
|
||||
//state := state.New(parent.Trie().Copy())
|
||||
|
||||
// Block validation
|
||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||
@ -214,52 +216,33 @@ func (sm *BlockProcessor) ProcessWithParent(block, parent *types.Block) (td *big
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if td, ok := sm.CalculateTD(block); ok {
|
||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||
state.Sync()
|
||||
// Calculate the td for this block
|
||||
td = CalculateTD(block, parent)
|
||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||
state.Sync()
|
||||
// Set the block hashes for the current messages
|
||||
state.Manifest().SetHash(block.Hash())
|
||||
messages = state.Manifest().Messages
|
||||
// Reset the manifest XXX We need this?
|
||||
state.Manifest().Reset()
|
||||
// Remove transactions from the pool
|
||||
sm.txpool.RemoveSet(block.Transactions())
|
||||
|
||||
state.Manifest().SetHash(block.Hash())
|
||||
chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])
|
||||
|
||||
messages := state.Manifest().Messages
|
||||
state.Manifest().Reset()
|
||||
|
||||
chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])
|
||||
|
||||
sm.txpool.RemoveSet(block.Transactions())
|
||||
|
||||
return td, messages, nil
|
||||
} else {
|
||||
return nil, nil, errors.New("total diff failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *BlockProcessor) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(sm.bc.Td(), uncleDiff)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(sm.bc.Td()) > 0 {
|
||||
return td, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return td, messages, nil
|
||||
}
|
||||
|
||||
// Validates the current block. Returns an error if the block was invalid,
|
||||
// an uncle or anything that isn't on the current block chain.
|
||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
|
||||
if len(block.Header().Extra) > 1024 {
|
||||
return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra))
|
||||
}
|
||||
|
||||
expd := CalcDifficulty(block, parent)
|
||||
if expd.Cmp(block.Header().Difficulty) < 0 {
|
||||
if expd.Cmp(block.Header().Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
|
||||
}
|
||||
|
||||
@ -286,32 +269,38 @@ func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
|
||||
func (sm *BlockProcessor) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
|
||||
knownUncles := set.New()
|
||||
for _, uncle := range parent.Uncles() {
|
||||
knownUncles.Add(string(uncle.Hash()))
|
||||
ancestors := set.New()
|
||||
for _, ancestor := range sm.bc.GetAncestors(block, 7) {
|
||||
ancestors.Add(string(ancestor.Hash()))
|
||||
}
|
||||
|
||||
nonces := ethutil.NewSet(block.Header().Nonce)
|
||||
uncles := set.New()
|
||||
uncles.Add(string(block.Hash()))
|
||||
for _, uncle := range block.Uncles() {
|
||||
if nonces.Include(uncle.Nonce) {
|
||||
if uncles.Has(string(uncle.Hash())) {
|
||||
// Error not unique
|
||||
return UncleError("Uncle not unique")
|
||||
}
|
||||
uncles.Add(string(uncle.Hash()))
|
||||
|
||||
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
||||
if uncleParent == nil {
|
||||
if !ancestors.Has(string(uncle.ParentHash)) {
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
|
||||
}
|
||||
|
||||
if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 {
|
||||
return UncleError("Uncle too old")
|
||||
}
|
||||
/*
|
||||
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
||||
if uncleParent == nil {
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
|
||||
}
|
||||
|
||||
if knownUncles.Has(string(uncle.Hash())) {
|
||||
return UncleError("Uncle in chain")
|
||||
}
|
||||
if uncleParent.Number().Cmp(new(big.Int).Sub(parent.Number(), big.NewInt(6))) < 0 {
|
||||
return UncleError("Uncle too old")
|
||||
}
|
||||
|
||||
nonces.Insert(uncle.Nonce)
|
||||
if knownUncles.Has(string(uncle.Hash())) {
|
||||
return UncleError("Uncle in chain")
|
||||
}
|
||||
*/
|
||||
|
||||
r := new(big.Int)
|
||||
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
|
||||
@ -347,7 +336,8 @@ func (sm *BlockProcessor) GetMessages(block *types.Block) (messages []*state.Mes
|
||||
|
||||
var (
|
||||
parent = sm.bc.GetBlock(block.Header().ParentHash)
|
||||
state = state.New(parent.Trie().Copy())
|
||||
//state = state.New(parent.Trie().Copy())
|
||||
state = state.New(parent.Root(), sm.db)
|
||||
)
|
||||
|
||||
defer state.Reset()
|
||||
|
@ -23,17 +23,30 @@ type StateQuery interface {
|
||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||
diff := new(big.Int)
|
||||
|
||||
bh, ph := block.Header(), parent.Header()
|
||||
adjust := new(big.Int).Rsh(ph.Difficulty, 10)
|
||||
if bh.Time >= ph.Time+5 {
|
||||
diff.Sub(ph.Difficulty, adjust)
|
||||
adjust := new(big.Int).Rsh(parent.Difficulty(), 10)
|
||||
if block.Time() >= parent.Time()+8 {
|
||||
diff.Sub(parent.Difficulty(), adjust)
|
||||
} else {
|
||||
diff.Add(ph.Difficulty, adjust)
|
||||
diff.Add(parent.Difficulty(), adjust)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func CalculateTD(block, parent *types.Block) *big.Int {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(parent.Td, uncleDiff)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
return td
|
||||
}
|
||||
|
||||
func CalcGasLimit(parent, block *types.Block) *big.Int {
|
||||
if block.Number().Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
@ -55,6 +68,7 @@ func CalcGasLimit(parent, block *types.Block) *big.Int {
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
db ethutil.Database
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
@ -96,13 +110,9 @@ func (self *ChainManager) CurrentBlock() *types.Block {
|
||||
return self.currentBlock
|
||||
}
|
||||
|
||||
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{}
|
||||
bc.genesisBlock = GenesisBlock()
|
||||
bc.eventMux = mux
|
||||
|
||||
func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
|
||||
bc.setLastBlock()
|
||||
|
||||
bc.transState = bc.State().Copy()
|
||||
|
||||
return bc
|
||||
@ -120,7 +130,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
}
|
||||
|
||||
func (self *ChainManager) State() *state.StateDB {
|
||||
return state.New(self.CurrentBlock().Trie())
|
||||
return state.New(self.CurrentBlock().Root(), self.db)
|
||||
}
|
||||
|
||||
func (self *ChainManager) TransState() *state.StateDB {
|
||||
@ -128,7 +138,7 @@ func (self *ChainManager) TransState() *state.StateDB {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setLastBlock() {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||
data, _ := bc.db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
var block types.Block
|
||||
rlp.Decode(bytes.NewReader(data), &block)
|
||||
@ -137,12 +147,12 @@ func (bc *ChainManager) setLastBlock() {
|
||||
bc.lastBlockNumber = block.Header().Number.Uint64()
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
bc.td = ethutil.BigD(bc.db.LastKnownTD())
|
||||
} else {
|
||||
bc.Reset()
|
||||
}
|
||||
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
|
||||
chainlogger.Infof("Last block (#%d) %x TD=%v\n", bc.lastBlockNumber, bc.currentBlock.Hash(), bc.td)
|
||||
}
|
||||
|
||||
// Block creation & chain handling
|
||||
@ -183,7 +193,7 @@ func (bc *ChainManager) Reset() {
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
|
||||
ethutil.Config.Db.Delete(block.Hash())
|
||||
bc.db.Delete(block.Hash())
|
||||
}
|
||||
|
||||
// Prepare the genesis block
|
||||
@ -210,7 +220,7 @@ func (self *ChainManager) Export() []byte {
|
||||
|
||||
func (bc *ChainManager) insert(block *types.Block) {
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||
bc.db.Put([]byte("LastBlock"), encodedBlock)
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
}
|
||||
@ -218,8 +228,8 @@ func (bc *ChainManager) insert(block *types.Block) {
|
||||
func (bc *ChainManager) write(block *types.Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||
encodedBlock := ethutil.Encode(block.RlpDataForStorage())
|
||||
bc.db.Put(block.Hash(), encodedBlock)
|
||||
}
|
||||
|
||||
// Accessors
|
||||
@ -229,7 +239,7 @@ func (bc *ChainManager) Genesis() *types.Block {
|
||||
|
||||
// Block fetching methods
|
||||
func (bc *ChainManager) HasBlock(hash []byte) bool {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
data, _ := bc.db.Get(hash)
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
@ -241,20 +251,18 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain
|
||||
|
||||
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||
for i := uint64(0); i < max; i++ {
|
||||
block = self.GetBlock(block.Header().ParentHash)
|
||||
chain = append(chain, block.Hash())
|
||||
|
||||
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
block = self.GetBlock(block.Header().ParentHash)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
data, _ := self.db.Get(hash)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -267,6 +275,28 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||
return &block
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
|
||||
for i := 0; block != nil && i < length; i++ {
|
||||
uncles = append(uncles, block.Uncles()...)
|
||||
block = self.GetBlock(block.ParentHash())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) {
|
||||
for i := 0; i < length; i++ {
|
||||
block = self.GetBlock(block.ParentHash())
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
@ -286,7 +316,7 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||
bc.db.Put([]byte("LTD"), td.Bytes())
|
||||
bc.td = td
|
||||
}
|
||||
|
||||
@ -343,12 +373,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
cblock := self.currentBlock
|
||||
if td.Cmp(self.td) > 0 {
|
||||
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
|
||||
chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td)
|
||||
}
|
||||
|
||||
self.setTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
self.transState = state.New(cblock.Trie().Copy())
|
||||
self.transState = state.New(cblock.Root(), self.db) //state.New(cblock.Trie().Copy())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
@ -21,14 +21,6 @@ func init() {
|
||||
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
|
||||
}
|
||||
|
||||
func reset() {
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
panic("Could not create mem-db, failing")
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
}
|
||||
|
||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
||||
fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
@ -54,7 +46,7 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
|
||||
}
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
reset()
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
chain1, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
@ -69,9 +61,9 @@ func TestChainInsertions(t *testing.T) {
|
||||
}
|
||||
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
chainMan := NewChainManager(db, &eventMux)
|
||||
txPool := NewTxPool(&eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
|
||||
const max = 2
|
||||
@ -84,17 +76,17 @@ func TestChainInsertions(t *testing.T) {
|
||||
<-done
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(chain2[len(chain2)-1], chainMan.CurrentBlock()) {
|
||||
if bytes.Equal(chain2[len(chain2)-1].Hash(), chainMan.CurrentBlock().Hash()) {
|
||||
t.Error("chain2 is canonical and shouldn't be")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chain1[len(chain1)-1], chainMan.CurrentBlock()) {
|
||||
if !bytes.Equal(chain1[len(chain1)-1].Hash(), chainMan.CurrentBlock().Hash()) {
|
||||
t.Error("chain1 isn't canonical and should be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainMultipleInsertions(t *testing.T) {
|
||||
reset()
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
const max = 4
|
||||
chains := make([]types.Blocks, max)
|
||||
@ -113,9 +105,9 @@ func TestChainMultipleInsertions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
chainMan := NewChainManager(db, &eventMux)
|
||||
txPool := NewTxPool(&eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
done := make(chan bool, max)
|
||||
for i, chain := range chains {
|
||||
@ -132,7 +124,25 @@ func TestChainMultipleInsertions(t *testing.T) {
|
||||
<-done
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chains[longest][len(chains[longest])-1], chainMan.CurrentBlock()) {
|
||||
if !bytes.Equal(chains[longest][len(chains[longest])-1].Hash(), chainMan.CurrentBlock().Hash()) {
|
||||
t.Error("Invalid canonical chain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAncestors(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(db, &eventMux)
|
||||
chain, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for _, block := range chain {
|
||||
chainMan.write(block)
|
||||
}
|
||||
|
||||
ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4)
|
||||
fmt.Println(ancestors)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
@ -13,7 +14,6 @@ type Execution struct {
|
||||
env vm.Environment
|
||||
address, input []byte
|
||||
Gas, price, value *big.Int
|
||||
SkipTransfer bool
|
||||
}
|
||||
|
||||
func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
@ -33,7 +33,7 @@ func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]byte, erro
|
||||
|
||||
func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret []byte, err error) {
|
||||
env := self.env
|
||||
evm := vm.New(env, vm.DebugVmTy)
|
||||
evm := vm.New(env)
|
||||
|
||||
if env.Depth() == vm.MaxCallDepth {
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
@ -41,16 +41,22 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ContextRef) (ret
|
||||
return nil, vm.DepthError{}
|
||||
}
|
||||
|
||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||
// Skipping transfer is used on testing for the initial call
|
||||
if !self.SkipTransfer {
|
||||
err = env.Transfer(from, to, self.value)
|
||||
if err != nil {
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
vsnapshot := env.State().Copy()
|
||||
if len(self.address) == 0 {
|
||||
// Generate a new address
|
||||
nonce := env.State().GetNonce(caller.Address())
|
||||
self.address = crypto.CreateAddress(caller.Address(), nonce)
|
||||
env.State().SetNonce(caller.Address(), nonce+1)
|
||||
}
|
||||
|
||||
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance)
|
||||
return
|
||||
}
|
||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||
err = env.Transfer(from, to, self.value)
|
||||
if err != nil {
|
||||
env.State().Set(vsnapshot)
|
||||
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
|
||||
return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance())
|
||||
}
|
||||
|
||||
snapshot := env.State().Copy()
|
||||
|
@ -19,18 +19,20 @@ var ZeroHash512 = make([]byte, 64)
|
||||
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
||||
|
||||
func GenesisBlock() *types.Block {
|
||||
func GenesisBlock(db ethutil.Database) *types.Block {
|
||||
genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
|
||||
genesis.Header().Number = ethutil.Big0
|
||||
genesis.Header().GasLimit = big.NewInt(1000000)
|
||||
genesis.Header().GasUsed = ethutil.Big0
|
||||
genesis.Header().Time = 0
|
||||
genesis.Td = ethutil.Big0
|
||||
|
||||
genesis.SetUncles([]*types.Header{})
|
||||
genesis.SetTransactions(types.Transactions{})
|
||||
genesis.SetReceipts(types.Receipts{})
|
||||
|
||||
statedb := state.New(genesis.Trie())
|
||||
statedb := state.New(genesis.Root(), db)
|
||||
//statedb := state.New(genesis.Trie())
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||
|
@ -77,7 +77,6 @@ func NewTestManager() *TestManager {
|
||||
fmt.Println("Could not create mem-db, failing")
|
||||
return nil
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
|
||||
testManager := &TestManager{}
|
||||
testManager.eventMux = new(event.TypeMux)
|
||||
|
@ -192,8 +192,7 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
||||
if err == nil {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, vm.GasCreateByte)
|
||||
if err = self.UseGas(dataGas); err == nil {
|
||||
//self.state.SetCode(ref.Address(), ret)
|
||||
if err := self.UseGas(dataGas); err == nil {
|
||||
ref.SetCode(ret)
|
||||
}
|
||||
}
|
||||
|
@ -56,11 +56,6 @@ func NewTxPool(eventMux *event.TypeMux) *TxPool {
|
||||
}
|
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||
hash := tx.Hash()
|
||||
if pool.txs[string(hash)] != nil {
|
||||
return fmt.Errorf("Known transaction (%x)", hash[0:4])
|
||||
}
|
||||
|
||||
if len(tx.To()) != 0 && len(tx.To()) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
|
||||
}
|
||||
@ -97,6 +92,10 @@ func (self *TxPool) addTx(tx *types.Transaction) {
|
||||
}
|
||||
|
||||
func (self *TxPool) Add(tx *types.Transaction) error {
|
||||
if self.txs[string(tx.Hash())] != nil {
|
||||
return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4])
|
||||
}
|
||||
|
||||
err := self.ValidateTransaction(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -149,6 +148,7 @@ func (pool *TxPool) RemoveInvalid(query StateQuery) {
|
||||
for _, tx := range pool.txs {
|
||||
sender := query.GetAccount(tx.From())
|
||||
err := pool.ValidateTransaction(tx)
|
||||
fmt.Println(err, sender.Nonce, tx.Nonce())
|
||||
if err != nil || sender.Nonce >= tx.Nonce() {
|
||||
removedTxs = append(removedTxs, tx)
|
||||
}
|
||||
|
@ -6,16 +6,22 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
// State query interface
|
||||
type stateQuery struct{}
|
||||
type stateQuery struct{ db ethutil.Database }
|
||||
|
||||
func SQ() stateQuery {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
return stateQuery{db: db}
|
||||
}
|
||||
|
||||
func (self stateQuery) GetAccount(addr []byte) *state.StateObject {
|
||||
return state.NewStateObject(addr)
|
||||
return state.NewStateObject(addr, self.db)
|
||||
}
|
||||
|
||||
func transaction() *types.Transaction {
|
||||
@ -55,7 +61,7 @@ func TestAddInvalidTx(t *testing.T) {
|
||||
func TestRemoveSet(t *testing.T) {
|
||||
pool, _ := setup()
|
||||
tx1 := transaction()
|
||||
pool.pool.Add(tx1)
|
||||
pool.addTx(tx1)
|
||||
pool.RemoveSet(types.Transactions{tx1})
|
||||
if pool.Size() > 0 {
|
||||
t.Error("expected pool size to be 0")
|
||||
@ -65,16 +71,16 @@ func TestRemoveSet(t *testing.T) {
|
||||
func TestRemoveInvalid(t *testing.T) {
|
||||
pool, key := setup()
|
||||
tx1 := transaction()
|
||||
pool.pool.Add(tx1)
|
||||
pool.RemoveInvalid(stateQuery{})
|
||||
pool.addTx(tx1)
|
||||
pool.RemoveInvalid(SQ())
|
||||
if pool.Size() > 0 {
|
||||
t.Error("expected pool size to be 0")
|
||||
}
|
||||
|
||||
tx1.SetNonce(1)
|
||||
tx1.SignECDSA(key)
|
||||
pool.pool.Add(tx1)
|
||||
pool.RemoveInvalid(stateQuery{})
|
||||
pool.addTx(tx1)
|
||||
pool.RemoveInvalid(SQ())
|
||||
if pool.Size() != 1 {
|
||||
t.Error("expected pool size to be 1, is", pool.Size())
|
||||
}
|
||||
|
@ -9,9 +9,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
@ -168,16 +166,18 @@ func (self *Block) RlpDataForStorage() interface{} {
|
||||
}
|
||||
|
||||
// Header accessors (add as you need them)
|
||||
func (self *Block) Number() *big.Int { return self.header.Number }
|
||||
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
||||
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
||||
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
||||
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
||||
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
|
||||
func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
|
||||
func (self *Block) Number() *big.Int { return self.header.Number }
|
||||
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
||||
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
||||
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
||||
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
||||
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
|
||||
|
||||
//func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
|
||||
//func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
|
||||
func (self *Block) Root() []byte { return self.header.Root }
|
||||
func (self *Block) SetRoot(root []byte) { self.header.Root = root }
|
||||
func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
|
||||
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
|
||||
|
||||
// Implement pow.Block
|
||||
|
@ -1,8 +1,9 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type DerivableList interface {
|
||||
@ -11,7 +12,8 @@ type DerivableList interface {
|
||||
}
|
||||
|
||||
func DeriveSha(list DerivableList) []byte {
|
||||
trie := ptrie.New(nil, ethutil.Config.Db)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
trie := trie.New(nil, db)
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
trie.Update(ethutil.Encode(i), list.GetRlp(i))
|
||||
}
|
||||
|
@ -3,7 +3,12 @@ package crypto
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
)
|
||||
|
||||
// These tests are sanity checks.
|
||||
@ -34,3 +39,24 @@ func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte
|
||||
t.Errorf("hash %s returned wrong result.\ngot: %x\nwant: %x", name, sum, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha3(b *testing.B) {
|
||||
a := []byte("hello world")
|
||||
amount := 1000000
|
||||
start := time.Now()
|
||||
for i := 0; i < amount; i++ {
|
||||
Sha3(a)
|
||||
}
|
||||
|
||||
fmt.Println(amount, ":", time.Since(start))
|
||||
}
|
||||
|
||||
func Test0Key(t *testing.T) {
|
||||
|
||||
key := ethutil.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111")
|
||||
|
||||
p, err := secp256k1.GeneratePubKey(key)
|
||||
addr := Sha3(p[1:])[12:]
|
||||
fmt.Printf("%x\n", p)
|
||||
fmt.Printf("%v %x\n", err, addr)
|
||||
}
|
||||
|
107
crypto/key.go
Normal file
107
crypto/key.go
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||
* @date 2015
|
||||
*
|
||||
*/
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
Id *uuid.UUID // Version 4 "random" for unique id not derived from key data
|
||||
// we only store privkey as pubkey/address can be derived from it
|
||||
// privkey in this struct is always in plaintext
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
type plainKeyJSON struct {
|
||||
Id []byte
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
type cipherJSON struct {
|
||||
Salt []byte
|
||||
IV []byte
|
||||
CipherText []byte
|
||||
}
|
||||
|
||||
type encryptedKeyJSON struct {
|
||||
Id []byte
|
||||
Crypto cipherJSON
|
||||
}
|
||||
|
||||
func (k *Key) Address() []byte {
|
||||
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
|
||||
return Sha3(pubBytes)[12:]
|
||||
}
|
||||
|
||||
func (k *Key) MarshalJSON() (j []byte, err error) {
|
||||
jStruct := plainKeyJSON{
|
||||
*k.Id,
|
||||
FromECDSA(k.PrivateKey),
|
||||
}
|
||||
j, err = json.Marshal(jStruct)
|
||||
return j, err
|
||||
}
|
||||
|
||||
func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
||||
keyJSON := new(plainKeyJSON)
|
||||
err = json.Unmarshal(j, &keyJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u := new(uuid.UUID)
|
||||
*u = keyJSON.Id
|
||||
k.Id = u
|
||||
|
||||
k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func NewKey(rand io.Reader) *Key {
|
||||
randBytes := make([]byte, 32)
|
||||
_, err := rand.Read(randBytes)
|
||||
if err != nil {
|
||||
panic("key generation: could not read from random source: " + err.Error())
|
||||
}
|
||||
reader := bytes.NewReader(randBytes)
|
||||
_, x, y, err := elliptic.GenerateKey(S256(), reader)
|
||||
if err != nil {
|
||||
panic("key generation: elliptic.GenerateKey failed: " + err.Error())
|
||||
}
|
||||
privateKeyMarshalled := elliptic.Marshal(S256(), x, y)
|
||||
privateKeyECDSA := ToECDSA(privateKeyMarshalled)
|
||||
|
||||
key := new(Key)
|
||||
id := uuid.NewRandom()
|
||||
key.Id = &id
|
||||
key.PrivateKey = privateKeyECDSA
|
||||
return key
|
||||
}
|
245
crypto/key_store_passphrase.go
Normal file
245
crypto/key_store_passphrase.go
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||
* @date 2015
|
||||
*
|
||||
*/
|
||||
/*
|
||||
|
||||
This key store behaves as KeyStorePlain with the difference that
|
||||
the private key is encrypted and on disk uses another JSON encoding.
|
||||
|
||||
Cryptography:
|
||||
|
||||
1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters
|
||||
(work factors) [1][2] are defined as constants below.
|
||||
2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext.
|
||||
3. Checksum is SHA3 of the private key bytes.
|
||||
4. Plaintext is concatenation of private key bytes and checksum.
|
||||
5. Encryption algo is AES 256 CBC [3][4]
|
||||
6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext.
|
||||
7. Plaintext padding is PKCS #7 [5][6]
|
||||
|
||||
Encoding:
|
||||
|
||||
1. On disk, ciphertext, salt and IV are encoded in a nested JSON object.
|
||||
cat a key file to see the structure.
|
||||
2. byte arrays are base64 JSON strings.
|
||||
3. The EC private key bytes are in uncompressed form [7].
|
||||
They are a big-endian byte slice of the absolute value of D [8][9].
|
||||
4. The checksum is the last 32 bytes of the plaintext byte array and the
|
||||
private key is the preceeding bytes.
|
||||
|
||||
References:
|
||||
|
||||
1. http://www.tarsnap.com/scrypt/scrypt-slides.pdf
|
||||
2. http://stackoverflow.com/questions/11126315/what-are-optimal-scrypt-work-factors
|
||||
3. http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||
4. http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
|
||||
5. https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||
6. http://tools.ietf.org/html/rfc2315
|
||||
7. http://bitcoin.stackexchange.com/questions/3059/what-is-a-compressed-bitcoin-key
|
||||
8. http://golang.org/pkg/crypto/ecdsa/#PrivateKey
|
||||
9. https://golang.org/pkg/math/big/#Int.Bytes
|
||||
|
||||
*/
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"code.google.com/p/go.crypto/scrypt"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
crand "crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
const (
|
||||
// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
|
||||
scryptN = 1 << 18
|
||||
scryptr = 8
|
||||
scryptp = 1
|
||||
scryptdkLen = 32
|
||||
)
|
||||
|
||||
type keyStorePassphrase struct {
|
||||
keysDirPath string
|
||||
}
|
||||
|
||||
func NewKeyStorePassphrase(path string) KeyStore2 {
|
||||
return &keyStorePassphrase{path}
|
||||
}
|
||||
|
||||
func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
|
||||
return GenerateNewKeyDefault(ks, rand, auth)
|
||||
}
|
||||
|
||||
func (ks keyStorePassphrase) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) {
|
||||
keyBytes, err := DecryptKey(ks, keyId, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = &Key{
|
||||
Id: keyId,
|
||||
PrivateKey: ToECDSA(keyBytes),
|
||||
}
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||
authArray := []byte(auth)
|
||||
salt := getEntropyCSPRNG(32)
|
||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyBytes := FromECDSA(key.PrivateKey)
|
||||
keyBytesHash := Sha3(keyBytes)
|
||||
toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...))
|
||||
|
||||
AES256Block, err := aes.NewCipher(derivedKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iv := getEntropyCSPRNG(aes.BlockSize) // 16
|
||||
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
|
||||
cipherText := make([]byte, len(toEncrypt))
|
||||
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
|
||||
|
||||
cipherStruct := cipherJSON{
|
||||
salt,
|
||||
iv,
|
||||
cipherText,
|
||||
}
|
||||
keyStruct := encryptedKeyJSON{
|
||||
*key.Id,
|
||||
cipherStruct,
|
||||
}
|
||||
keyJSON, err := json.Marshal(keyStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON)
|
||||
}
|
||||
|
||||
func (ks keyStorePassphrase) DeleteKey(keyId *uuid.UUID, auth string) (err error) {
|
||||
// only delete if correct passphrase is given
|
||||
_, err = DecryptKey(ks, keyId, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyDirPath := path.Join(ks.keysDirPath, keyId.String())
|
||||
return os.RemoveAll(keyDirPath)
|
||||
}
|
||||
|
||||
func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes []byte, err error) {
|
||||
fileContent, err := GetKeyFile(ks.keysDirPath, keyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyProtected := new(encryptedKeyJSON)
|
||||
err = json.Unmarshal(fileContent, keyProtected)
|
||||
|
||||
salt := keyProtected.Crypto.Salt
|
||||
|
||||
iv := keyProtected.Crypto.IV
|
||||
|
||||
cipherText := keyProtected.Crypto.CipherText
|
||||
|
||||
authArray := []byte(auth)
|
||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
AES256Block, err := aes.NewCipher(derivedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv)
|
||||
paddedPlainText := make([]byte, len(cipherText))
|
||||
AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText)
|
||||
|
||||
plainText := PKCS7Unpad(paddedPlainText)
|
||||
if plainText == nil {
|
||||
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes = plainText[:len(plainText)-32]
|
||||
keyBytesHash := plainText[len(plainText)-32:]
|
||||
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
|
||||
err = errors.New("Decryption failed: checksum mismatch")
|
||||
return nil, err
|
||||
}
|
||||
return keyBytes, err
|
||||
}
|
||||
|
||||
func getEntropyCSPRNG(n int) []byte {
|
||||
mainBuff := make([]byte, n)
|
||||
_, err := io.ReadFull(crand.Reader, mainBuff)
|
||||
if err != nil {
|
||||
panic("key generation: reading from crypto/rand failed: " + err.Error())
|
||||
}
|
||||
return mainBuff
|
||||
}
|
||||
|
||||
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
|
||||
func PKCS7Pad(in []byte) []byte {
|
||||
padding := 16 - (len(in) % 16)
|
||||
if padding == 0 {
|
||||
padding = 16
|
||||
}
|
||||
for i := 0; i < padding; i++ {
|
||||
in = append(in, byte(padding))
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func PKCS7Unpad(in []byte) []byte {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
padding := in[len(in)-1]
|
||||
if int(padding) > len(in) || padding > aes.BlockSize {
|
||||
return nil
|
||||
} else if padding == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
|
||||
if in[i] != padding {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return in[:len(in)-int(padding)]
|
||||
}
|
114
crypto/key_store_plain.go
Normal file
114
crypto/key_store_plain.go
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
This file is part of go-ethereum
|
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
go-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @authors
|
||||
* Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||
* @date 2015
|
||||
*
|
||||
*/
|
||||
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
)
|
||||
|
||||
// TODO: rename to KeyStore when replacing existing KeyStore
|
||||
type KeyStore2 interface {
|
||||
// create new key using io.Reader entropy source and optionally using auth string
|
||||
GenerateNewKey(io.Reader, string) (*Key, error)
|
||||
GetKey(*uuid.UUID, string) (*Key, error) // key from id and auth string
|
||||
StoreKey(*Key, string) error // store key optionally using auth string
|
||||
DeleteKey(*uuid.UUID, string) error // delete key by id and auth string
|
||||
}
|
||||
|
||||
type keyStorePlain struct {
|
||||
keysDirPath string
|
||||
}
|
||||
|
||||
// TODO: copied from cmd/ethereum/flags.go
|
||||
func DefaultDataDir() string {
|
||||
usr, _ := user.Current()
|
||||
return path.Join(usr.HomeDir, ".ethereum")
|
||||
}
|
||||
|
||||
func NewKeyStorePlain(path string) KeyStore2 {
|
||||
return &keyStorePlain{path}
|
||||
}
|
||||
|
||||
func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
|
||||
return GenerateNewKeyDefault(ks, rand, auth)
|
||||
}
|
||||
|
||||
func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("GenerateNewKey error: %v", r)
|
||||
}
|
||||
}()
|
||||
key = NewKey(rand)
|
||||
err = ks.StoreKey(key, auth)
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (ks keyStorePlain) GetKey(keyId *uuid.UUID, auth string) (key *Key, err error) {
|
||||
fileContent, err := GetKeyFile(ks.keysDirPath, keyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key = new(Key)
|
||||
err = json.Unmarshal(fileContent, key)
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
|
||||
keyJSON, err := json.Marshal(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = WriteKeyFile(key.Id.String(), ks.keysDirPath, keyJSON)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ks keyStorePlain) DeleteKey(keyId *uuid.UUID, auth string) (err error) {
|
||||
keyDirPath := path.Join(ks.keysDirPath, keyId.String())
|
||||
err = os.RemoveAll(keyDirPath)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetKeyFile(keysDirPath string, keyId *uuid.UUID) (fileContent []byte, err error) {
|
||||
id := keyId.String()
|
||||
return ioutil.ReadFile(path.Join(keysDirPath, id, id))
|
||||
}
|
||||
|
||||
func WriteKeyFile(id string, keysDirPath string, content []byte) (err error) {
|
||||
keyDirPath := path.Join(keysDirPath, id)
|
||||
keyFilePath := path.Join(keyDirPath, id)
|
||||
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
|
||||
}
|
85
crypto/key_store_test.go
Normal file
85
crypto/key_store_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyStorePlain(t *testing.T) {
|
||||
ks := NewKeyStorePlain(DefaultDataDir())
|
||||
pass := "" // not used but required by API
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
k2 := new(Key)
|
||||
k2, err = ks.GetKey(k1.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k2.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphrase(t *testing.T) {
|
||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||
pass := "foo"
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
k2 := new(Key)
|
||||
k2, err = ks.GetKey(k1.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k2.Id, pass) // also to clean up created files
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||
pass := "foo"
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k1.Id, pass) // to clean up
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
seedNodeAddress = "poc-7.ethdev.com:30300"
|
||||
seedNodeAddress = "poc-8.ethdev.com:30303"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@ -81,7 +81,7 @@ type Ethereum struct {
|
||||
func New(config *Config) (*Ethereum, error) {
|
||||
// Boostrap database
|
||||
logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel)
|
||||
db, err := ethdb.NewLDBDatabase("database")
|
||||
db, err := ethdb.NewLDBDatabase("blockchain")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -110,7 +110,7 @@ func New(config *Config) (*Ethereum, error) {
|
||||
clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey())
|
||||
|
||||
saveProtocolVersion(db)
|
||||
ethutil.Config.Db = db
|
||||
//ethutil.Config.Db = db
|
||||
|
||||
eth := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
@ -123,9 +123,9 @@ func New(config *Config) (*Ethereum, error) {
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
eth.chainManager = core.NewChainManager(eth.EventMux())
|
||||
eth.chainManager = core.NewChainManager(db, eth.EventMux())
|
||||
eth.txPool = core.NewTxPool(eth.EventMux())
|
||||
eth.blockProcessor = core.NewBlockProcessor(eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.chainManager.SetProcessor(eth.blockProcessor)
|
||||
eth.whisper = whisper.New()
|
||||
|
||||
@ -134,24 +134,20 @@ func New(config *Config) (*Ethereum, error) {
|
||||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
||||
|
||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||
protocols := []p2p.Protocol{ethProto}
|
||||
|
||||
if config.Shh {
|
||||
eth.whisper = whisper.New()
|
||||
protocols = append(protocols, eth.whisper.Protocol())
|
||||
}
|
||||
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||
|
||||
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(nat)
|
||||
|
||||
eth.net = &p2p.Server{
|
||||
Identity: clientId,
|
||||
MaxPeers: config.MaxPeers,
|
||||
Protocols: protocols,
|
||||
Blacklist: eth.blacklist,
|
||||
NAT: nat,
|
||||
NAT: p2p.UPNP(),
|
||||
NoDial: !config.Dial,
|
||||
}
|
||||
|
||||
@ -249,7 +245,7 @@ func (s *Ethereum) Start(seed bool) error {
|
||||
if seed {
|
||||
logger.Infof("Connect to seed node %v", seedNodeAddress)
|
||||
if err := s.SuggestPeer(seedNodeAddress); err != nil {
|
||||
return err
|
||||
logger.Infoln(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
@ -24,8 +25,8 @@ const (
|
||||
blocksRequestRepetition = 1
|
||||
blockHashesRequestInterval = 500 // ms
|
||||
blocksRequestMaxIdleRounds = 100
|
||||
cacheTimeout = 3 // minutes
|
||||
blockTimeout = 5 // minutes
|
||||
blockHashesTimeout = 60 // seconds
|
||||
blocksTimeout = 120 // seconds
|
||||
)
|
||||
|
||||
type poolNode struct {
|
||||
@ -70,9 +71,14 @@ type BlockPool struct {
|
||||
type peerInfo struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
td *big.Int
|
||||
currentBlock []byte
|
||||
id string
|
||||
td *big.Int
|
||||
currentBlockHash []byte
|
||||
currentBlock *types.Block
|
||||
currentBlockC chan *types.Block
|
||||
parentHash []byte
|
||||
headSection *section
|
||||
headSectionC chan *section
|
||||
id string
|
||||
|
||||
requestBlockHashes func([]byte) error
|
||||
requestBlocks func([][]byte) error
|
||||
@ -203,30 +209,39 @@ func (self *BlockPool) Wait(t time.Duration) {
|
||||
// AddPeer is called by the eth protocol instance running on the peer after
|
||||
// the status message has been received with total difficulty and current block hash
|
||||
// AddPeer can only be used once, RemovePeer needs to be called when the peer disconnects
|
||||
func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) bool {
|
||||
func (self *BlockPool) AddPeer(td *big.Int, currentBlockHash []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool) {
|
||||
|
||||
self.peersLock.Lock()
|
||||
defer self.peersLock.Unlock()
|
||||
peer, ok := self.peers[peerId]
|
||||
if ok {
|
||||
poolLogger.Debugf("Update peer %v with td %v and current block %x", peerId, td, currentBlock[:4])
|
||||
peer.td = td
|
||||
peer.currentBlock = currentBlock
|
||||
if bytes.Compare(peer.currentBlockHash, currentBlockHash) != 0 {
|
||||
poolLogger.Debugf("Update peer %v with td %v and current block %s", peerId, td, name(currentBlockHash))
|
||||
peer.lock.Lock()
|
||||
peer.td = td
|
||||
peer.currentBlockHash = currentBlockHash
|
||||
peer.currentBlock = nil
|
||||
peer.parentHash = nil
|
||||
peer.headSection = nil
|
||||
peer.lock.Unlock()
|
||||
}
|
||||
} else {
|
||||
peer = &peerInfo{
|
||||
td: td,
|
||||
currentBlock: currentBlock,
|
||||
currentBlockHash: currentBlockHash,
|
||||
id: peerId, //peer.Identity().Pubkey()
|
||||
requestBlockHashes: requestBlockHashes,
|
||||
requestBlocks: requestBlocks,
|
||||
peerError: peerError,
|
||||
sections: make(map[string]*section),
|
||||
currentBlockC: make(chan *types.Block),
|
||||
headSectionC: make(chan *section),
|
||||
}
|
||||
self.peers[peerId] = peer
|
||||
poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlock[:4])
|
||||
poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlockHash[:4])
|
||||
}
|
||||
// check peer current head
|
||||
if self.hasBlock(currentBlock) {
|
||||
if self.hasBlock(currentBlockHash) {
|
||||
// peer not ahead
|
||||
return false
|
||||
}
|
||||
@ -234,22 +249,135 @@ func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string,
|
||||
if self.peer == peer {
|
||||
// new block update
|
||||
// peer is already active best peer, request hashes
|
||||
poolLogger.Debugf("[%s] already the best peer. request hashes from %s", peerId, name(currentBlock))
|
||||
peer.requestBlockHashes(currentBlock)
|
||||
return true
|
||||
poolLogger.Debugf("[%s] already the best peer. Request new head section info from %s", peerId, name(currentBlockHash))
|
||||
peer.headSectionC <- nil
|
||||
best = true
|
||||
} else {
|
||||
currentTD := ethutil.Big0
|
||||
if self.peer != nil {
|
||||
currentTD = self.peer.td
|
||||
}
|
||||
if td.Cmp(currentTD) > 0 {
|
||||
poolLogger.Debugf("peer %v promoted best peer", peerId)
|
||||
self.switchPeer(self.peer, peer)
|
||||
self.peer = peer
|
||||
best = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
currentTD := ethutil.Big0
|
||||
if self.peer != nil {
|
||||
currentTD = self.peer.td
|
||||
}
|
||||
if td.Cmp(currentTD) > 0 {
|
||||
poolLogger.Debugf("peer %v promoted best peer", peerId)
|
||||
self.switchPeer(self.peer, peer)
|
||||
self.peer = peer
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func (self *BlockPool) requestHeadSection(peer *peerInfo) {
|
||||
self.wg.Add(1)
|
||||
self.procWg.Add(1)
|
||||
poolLogger.Debugf("[%s] head section at [%s] requesting info", peer.id, name(peer.currentBlockHash))
|
||||
|
||||
go func() {
|
||||
var idle bool
|
||||
peer.lock.RLock()
|
||||
quitC := peer.quitC
|
||||
currentBlockHash := peer.currentBlockHash
|
||||
peer.lock.RUnlock()
|
||||
blockHashesRequestTimer := time.NewTimer(0)
|
||||
blocksRequestTimer := time.NewTimer(0)
|
||||
suicide := time.NewTimer(blockHashesTimeout * time.Second)
|
||||
blockHashesRequestTimer.Stop()
|
||||
defer blockHashesRequestTimer.Stop()
|
||||
defer blocksRequestTimer.Stop()
|
||||
|
||||
entry := self.get(currentBlockHash)
|
||||
if entry != nil {
|
||||
entry.node.lock.RLock()
|
||||
currentBlock := entry.node.block
|
||||
entry.node.lock.RUnlock()
|
||||
if currentBlock != nil {
|
||||
peer.lock.Lock()
|
||||
peer.currentBlock = currentBlock
|
||||
peer.parentHash = currentBlock.ParentHash()
|
||||
poolLogger.Debugf("[%s] head block [%s] found", peer.id, name(currentBlockHash))
|
||||
peer.lock.Unlock()
|
||||
blockHashesRequestTimer.Reset(0)
|
||||
blocksRequestTimer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
|
||||
select {
|
||||
case <-self.quit:
|
||||
break LOOP
|
||||
|
||||
case <-quitC:
|
||||
poolLogger.Debugf("[%s] head section at [%s] incomplete - quit request loop", peer.id, name(currentBlockHash))
|
||||
break LOOP
|
||||
|
||||
case headSection := <-peer.headSectionC:
|
||||
peer.lock.Lock()
|
||||
peer.headSection = headSection
|
||||
if headSection == nil {
|
||||
oldBlockHash := currentBlockHash
|
||||
currentBlockHash = peer.currentBlockHash
|
||||
poolLogger.Debugf("[%s] head section changed [%s] -> [%s]", peer.id, name(oldBlockHash), name(currentBlockHash))
|
||||
if idle {
|
||||
idle = false
|
||||
suicide.Reset(blockHashesTimeout * time.Second)
|
||||
self.procWg.Add(1)
|
||||
}
|
||||
blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond)
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s] head section at [%s] created", peer.id, name(currentBlockHash))
|
||||
if !idle {
|
||||
idle = true
|
||||
suicide.Stop()
|
||||
self.procWg.Done()
|
||||
}
|
||||
}
|
||||
peer.lock.Unlock()
|
||||
blockHashesRequestTimer.Stop()
|
||||
|
||||
case <-blockHashesRequestTimer.C:
|
||||
poolLogger.DebugDetailf("[%s] head section at [%s] not found, requesting block hashes", peer.id, name(currentBlockHash))
|
||||
peer.requestBlockHashes(currentBlockHash)
|
||||
blockHashesRequestTimer.Reset(blockHashesRequestInterval * time.Millisecond)
|
||||
|
||||
case currentBlock := <-peer.currentBlockC:
|
||||
peer.lock.Lock()
|
||||
peer.currentBlock = currentBlock
|
||||
peer.parentHash = currentBlock.ParentHash()
|
||||
poolLogger.DebugDetailf("[%s] head block [%s] found", peer.id, name(currentBlockHash))
|
||||
peer.lock.Unlock()
|
||||
if self.hasBlock(currentBlock.ParentHash()) {
|
||||
if err := self.insertChain(types.Blocks([]*types.Block{currentBlock})); err != nil {
|
||||
peer.peerError(ErrInvalidBlock, "%v", err)
|
||||
}
|
||||
if !idle {
|
||||
idle = true
|
||||
suicide.Stop()
|
||||
self.procWg.Done()
|
||||
}
|
||||
} else {
|
||||
blockHashesRequestTimer.Reset(0)
|
||||
}
|
||||
blocksRequestTimer.Stop()
|
||||
|
||||
case <-blocksRequestTimer.C:
|
||||
peer.lock.RLock()
|
||||
poolLogger.DebugDetailf("[%s] head block [%s] not found, requesting", peer.id, name(currentBlockHash))
|
||||
peer.requestBlocks([][]byte{peer.currentBlockHash})
|
||||
peer.lock.RUnlock()
|
||||
blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond)
|
||||
|
||||
case <-suicide.C:
|
||||
peer.peerError(ErrInsufficientChainInfo, "peer failed to provide block hashes or head block for block hash %x", currentBlockHash)
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
self.wg.Done()
|
||||
if !idle {
|
||||
self.procWg.Done()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// RemovePeer is called by the eth protocol when the peer disconnects
|
||||
@ -274,13 +402,13 @@ func (self *BlockPool) RemovePeer(peerId string) {
|
||||
newPeer = info
|
||||
}
|
||||
}
|
||||
self.peer = newPeer
|
||||
self.switchPeer(peer, newPeer)
|
||||
if newPeer != nil {
|
||||
poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id, newPeer.td)
|
||||
} else {
|
||||
poolLogger.Warnln("no peers")
|
||||
}
|
||||
self.peer = newPeer
|
||||
self.switchPeer(peer, newPeer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,25 +427,56 @@ func (self *BlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string)
|
||||
return
|
||||
}
|
||||
// peer is still the best
|
||||
poolLogger.Debugf("adding hashes for best peer %s", peerId)
|
||||
|
||||
var size, n int
|
||||
var hash []byte
|
||||
var ok bool
|
||||
var section, child, parent *section
|
||||
var ok, headSection bool
|
||||
var sec, child, parent *section
|
||||
var entry *poolEntry
|
||||
var nodes []*poolNode
|
||||
bestPeer := peer
|
||||
|
||||
hash, ok = next()
|
||||
peer.lock.Lock()
|
||||
if bytes.Compare(peer.parentHash, hash) == 0 {
|
||||
if self.hasBlock(peer.currentBlockHash) {
|
||||
return
|
||||
}
|
||||
poolLogger.Debugf("adding hashes at chain head for best peer %s starting from [%s]", peerId, name(peer.currentBlockHash))
|
||||
headSection = true
|
||||
|
||||
if entry := self.get(peer.currentBlockHash); entry == nil {
|
||||
node := &poolNode{
|
||||
hash: peer.currentBlockHash,
|
||||
block: peer.currentBlock,
|
||||
peer: peerId,
|
||||
blockBy: peerId,
|
||||
}
|
||||
if size == 0 {
|
||||
sec = newSection()
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
size++
|
||||
n++
|
||||
} else {
|
||||
child = entry.section
|
||||
}
|
||||
} else {
|
||||
poolLogger.Debugf("adding hashes for best peer %s starting from [%s]", peerId, name(hash))
|
||||
}
|
||||
quitC := peer.quitC
|
||||
peer.lock.Unlock()
|
||||
|
||||
LOOP:
|
||||
// iterate using next (rlp stream lazy decoder) feeding hashesC
|
||||
for hash, ok = next(); ok; hash, ok = next() {
|
||||
for ; ok; hash, ok = next() {
|
||||
n++
|
||||
select {
|
||||
case <-self.quit:
|
||||
return
|
||||
case <-peer.quitC:
|
||||
case <-quitC:
|
||||
// if the peer is demoted, no more hashes taken
|
||||
peer = nil
|
||||
bestPeer = nil
|
||||
break LOOP
|
||||
default:
|
||||
}
|
||||
@ -325,8 +484,8 @@ LOOP:
|
||||
// check if known block connecting the downloaded chain to our blockchain
|
||||
poolLogger.DebugDetailf("[%s] known block", name(hash))
|
||||
// mark child as absolute pool root with parent known to blockchain
|
||||
if section != nil {
|
||||
self.connectToBlockChain(section)
|
||||
if sec != nil {
|
||||
self.connectToBlockChain(sec)
|
||||
} else {
|
||||
if child != nil {
|
||||
self.connectToBlockChain(child)
|
||||
@ -340,6 +499,7 @@ LOOP:
|
||||
// reached a known chain in the pool
|
||||
if entry.node == entry.section.bottom && n == 1 {
|
||||
// the first block hash received is an orphan in the pool, so rejoice and continue
|
||||
poolLogger.DebugDetailf("[%s] connecting child section", sectionName(entry.section))
|
||||
child = entry.section
|
||||
continue LOOP
|
||||
}
|
||||
@ -353,7 +513,7 @@ LOOP:
|
||||
peer: peerId,
|
||||
}
|
||||
if size == 0 {
|
||||
section = newSection()
|
||||
sec = newSection()
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
size++
|
||||
@ -379,10 +539,10 @@ LOOP:
|
||||
}
|
||||
|
||||
if size > 0 {
|
||||
self.processSection(section, nodes)
|
||||
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(section), size, sectionName(child))
|
||||
self.link(parent, section)
|
||||
self.link(section, child)
|
||||
self.processSection(sec, nodes)
|
||||
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(sec), size, sectionName(child))
|
||||
self.link(parent, sec)
|
||||
self.link(sec, child)
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s]->[%s] connecting known sections", sectionName(parent), sectionName(child))
|
||||
self.link(parent, child)
|
||||
@ -390,15 +550,31 @@ LOOP:
|
||||
|
||||
self.chainLock.Unlock()
|
||||
|
||||
if parent != nil && peer != nil {
|
||||
if parent != nil && bestPeer != nil {
|
||||
self.activateChain(parent, peer)
|
||||
poolLogger.Debugf("[%s] activate parent section [%s]", name(parent.top.hash), sectionName(parent))
|
||||
}
|
||||
|
||||
if section != nil {
|
||||
peer.addSection(section.top.hash, section)
|
||||
section.controlC <- peer
|
||||
poolLogger.Debugf("[%s] activate new section", sectionName(section))
|
||||
if sec != nil {
|
||||
peer.addSection(sec.top.hash, sec)
|
||||
// request next section here once, only repeat if bottom block arrives,
|
||||
// otherwise no way to check if it arrived
|
||||
peer.requestBlockHashes(sec.bottom.hash)
|
||||
sec.controlC <- bestPeer
|
||||
poolLogger.Debugf("[%s] activate new section", sectionName(sec))
|
||||
}
|
||||
|
||||
if headSection {
|
||||
var headSec *section
|
||||
switch {
|
||||
case sec != nil:
|
||||
headSec = sec
|
||||
case child != nil:
|
||||
headSec = child
|
||||
default:
|
||||
headSec = parent
|
||||
}
|
||||
peer.headSectionC <- headSec
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,14 +602,21 @@ func sectionName(section *section) (name string) {
|
||||
// only the first PoW-valid block for a hash is considered legit
|
||||
func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
||||
hash := block.Hash()
|
||||
if self.hasBlock(hash) {
|
||||
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
||||
return
|
||||
}
|
||||
self.peersLock.Lock()
|
||||
peer := self.peer
|
||||
self.peersLock.Unlock()
|
||||
|
||||
entry := self.get(hash)
|
||||
if bytes.Compare(hash, peer.currentBlockHash) == 0 {
|
||||
poolLogger.Debugf("add head block [%s] for peer %s", name(hash), peerId)
|
||||
peer.currentBlockC <- block
|
||||
} else {
|
||||
if entry == nil {
|
||||
poolLogger.Warnf("unrequested block [%s] by peer %s", name(hash), peerId)
|
||||
self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
|
||||
}
|
||||
}
|
||||
if entry == nil {
|
||||
poolLogger.Warnf("unrequested block [%x] by peer %s", hash, peerId)
|
||||
self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
|
||||
return
|
||||
}
|
||||
|
||||
@ -443,17 +626,21 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
||||
|
||||
// check if block already present
|
||||
if node.block != nil {
|
||||
poolLogger.DebugDetailf("block [%x] already sent by %s", name(hash), node.blockBy)
|
||||
poolLogger.DebugDetailf("block [%s] already sent by %s", name(hash), node.blockBy)
|
||||
return
|
||||
}
|
||||
|
||||
// validate block for PoW
|
||||
if !self.verifyPoW(block) {
|
||||
poolLogger.Warnf("invalid pow on block [%x] by peer %s", hash, peerId)
|
||||
self.peerError(peerId, ErrInvalidPoW, "%x", hash)
|
||||
return
|
||||
}
|
||||
if self.hasBlock(hash) {
|
||||
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
||||
} else {
|
||||
|
||||
// validate block for PoW
|
||||
if !self.verifyPoW(block) {
|
||||
poolLogger.Warnf("invalid pow on block [%s] by peer %s", name(hash), peerId)
|
||||
self.peerError(peerId, ErrInvalidPoW, "%x", hash)
|
||||
return
|
||||
}
|
||||
}
|
||||
poolLogger.Debugf("added block [%s] sent by peer %s", name(hash), peerId)
|
||||
node.block = block
|
||||
node.blockBy = peerId
|
||||
@ -544,23 +731,23 @@ LOOP:
|
||||
// - when turned off (if peer disconnects and new peer connects with alternative chain), no blockrequests are made but absolute expiry timer is ticking
|
||||
// - when turned back on it recursively calls itself on the root of the next chain section
|
||||
// - when exits, signals to
|
||||
func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
func (self *BlockPool) processSection(sec *section, nodes []*poolNode) {
|
||||
|
||||
for i, node := range nodes {
|
||||
entry := &poolEntry{node: node, section: section, index: i}
|
||||
entry := &poolEntry{node: node, section: sec, index: i}
|
||||
self.set(node.hash, entry)
|
||||
}
|
||||
|
||||
section.bottom = nodes[len(nodes)-1]
|
||||
section.top = nodes[0]
|
||||
section.nodes = nodes
|
||||
poolLogger.DebugDetailf("[%s] setup section process", sectionName(section))
|
||||
sec.bottom = nodes[len(nodes)-1]
|
||||
sec.top = nodes[0]
|
||||
sec.nodes = nodes
|
||||
poolLogger.DebugDetailf("[%s] setup section process", sectionName(sec))
|
||||
|
||||
self.wg.Add(1)
|
||||
go func() {
|
||||
|
||||
// absolute time after which sub-chain is killed if not complete (some blocks are missing)
|
||||
suicideTimer := time.After(blockTimeout * time.Minute)
|
||||
suicideTimer := time.After(blocksTimeout * time.Second)
|
||||
|
||||
var peer, newPeer *peerInfo
|
||||
|
||||
@ -580,21 +767,23 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
var insertChain bool
|
||||
var quitC chan bool
|
||||
|
||||
var blockChainC = section.blockChainC
|
||||
var blockChainC = sec.blockChainC
|
||||
|
||||
var parentHash []byte
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
|
||||
if insertChain {
|
||||
insertChain = false
|
||||
rest, err := self.addSectionToBlockChain(section)
|
||||
rest, err := self.addSectionToBlockChain(sec)
|
||||
if err != nil {
|
||||
close(section.suicideC)
|
||||
close(sec.suicideC)
|
||||
continue LOOP
|
||||
}
|
||||
if rest == 0 {
|
||||
blocksRequestsComplete = true
|
||||
child := self.getChild(section)
|
||||
child := self.getChild(sec)
|
||||
if child != nil {
|
||||
self.connectToBlockChain(child)
|
||||
}
|
||||
@ -603,7 +792,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
|
||||
if blockHashesRequestsComplete && blocksRequestsComplete {
|
||||
// not waiting for hashes any more
|
||||
poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(section), depth, blocksRequests, blockHashesRequests)
|
||||
poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(sec), depth, blocksRequests, blockHashesRequests)
|
||||
break LOOP
|
||||
} // otherwise suicide if no hashes coming
|
||||
|
||||
@ -611,11 +800,12 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
// went through all blocks in section
|
||||
if missing == 0 {
|
||||
// no missing blocks
|
||||
poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
blocksRequestsComplete = true
|
||||
blocksRequestTimer = nil
|
||||
blocksRequestTime = false
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s] section checked: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth)
|
||||
// some missing blocks
|
||||
blocksRequests++
|
||||
if len(hashes) > 0 {
|
||||
@ -630,8 +820,8 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
idle++
|
||||
// too many idle rounds
|
||||
if idle >= blocksRequestMaxIdleRounds {
|
||||
poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(section), idle, blocksRequests, missing, lastMissing, depth)
|
||||
close(section.suicideC)
|
||||
poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(sec), idle, blocksRequests, missing, lastMissing, depth)
|
||||
close(sec.suicideC)
|
||||
}
|
||||
} else {
|
||||
idle = 0
|
||||
@ -653,22 +843,39 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
//
|
||||
|
||||
if ready && blocksRequestTime && !blocksRequestsComplete {
|
||||
poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
blocksRequestTimer = time.After(blocksRequestInterval * time.Millisecond)
|
||||
blocksRequestTime = false
|
||||
processC = offC
|
||||
}
|
||||
|
||||
if blockHashesRequestTime {
|
||||
if self.getParent(section) != nil {
|
||||
var parentSection = self.getParent(sec)
|
||||
if parentSection == nil {
|
||||
if parent := self.get(parentHash); parent != nil {
|
||||
parentSection = parent.section
|
||||
self.chainLock.Lock()
|
||||
self.link(parentSection, sec)
|
||||
self.chainLock.Unlock()
|
||||
} else {
|
||||
if self.hasBlock(parentHash) {
|
||||
insertChain = true
|
||||
blockHashesRequestTime = false
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
if parentSection != nil {
|
||||
// if not root of chain, switch off
|
||||
poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(section), blockHashesRequests)
|
||||
poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(sec), blockHashesRequests)
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
} else {
|
||||
blockHashesRequests++
|
||||
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(section), blockHashesRequests)
|
||||
peer.requestBlockHashes(section.bottom.hash)
|
||||
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(sec), blockHashesRequests)
|
||||
peer.requestBlockHashes(sec.bottom.hash)
|
||||
blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Millisecond)
|
||||
}
|
||||
blockHashesRequestTime = false
|
||||
@ -682,27 +889,27 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
// peer quit or demoted, put section in idle mode
|
||||
quitC = nil
|
||||
go func() {
|
||||
section.controlC <- nil
|
||||
sec.controlC <- nil
|
||||
}()
|
||||
|
||||
case <-self.purgeC:
|
||||
suicideTimer = time.After(0)
|
||||
|
||||
case <-suicideTimer:
|
||||
close(section.suicideC)
|
||||
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
close(sec.suicideC)
|
||||
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
|
||||
case <-section.suicideC:
|
||||
poolLogger.Debugf("[%s] suicide", sectionName(section))
|
||||
case <-sec.suicideC:
|
||||
poolLogger.Debugf("[%s] suicide", sectionName(sec))
|
||||
|
||||
// first delink from child and parent under chainlock
|
||||
self.chainLock.Lock()
|
||||
self.link(nil, section)
|
||||
self.link(section, nil)
|
||||
self.link(nil, sec)
|
||||
self.link(sec, nil)
|
||||
self.chainLock.Unlock()
|
||||
// delete node entries from pool index under pool lock
|
||||
self.lock.Lock()
|
||||
for _, node := range section.nodes {
|
||||
for _, node := range sec.nodes {
|
||||
delete(self.pool, string(node.hash))
|
||||
}
|
||||
self.lock.Unlock()
|
||||
@ -710,20 +917,20 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
break LOOP
|
||||
|
||||
case <-blocksRequestTimer:
|
||||
poolLogger.DebugDetailf("[%s] block request time", sectionName(section))
|
||||
poolLogger.DebugDetailf("[%s] block request time", sectionName(sec))
|
||||
blocksRequestTime = true
|
||||
|
||||
case <-blockHashesRequestTimer:
|
||||
poolLogger.DebugDetailf("[%s] hash request time", sectionName(section))
|
||||
poolLogger.DebugDetailf("[%s] hash request time", sectionName(sec))
|
||||
blockHashesRequestTime = true
|
||||
|
||||
case newPeer = <-section.controlC:
|
||||
case newPeer = <-sec.controlC:
|
||||
|
||||
// active -> idle
|
||||
if peer != nil && newPeer == nil {
|
||||
self.procWg.Done()
|
||||
if init {
|
||||
poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
}
|
||||
blocksRequestTime = false
|
||||
blocksRequestTimer = nil
|
||||
@ -739,11 +946,11 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
if peer == nil && newPeer != nil {
|
||||
self.procWg.Add(1)
|
||||
|
||||
poolLogger.Debugf("[%s] active mode", sectionName(section))
|
||||
poolLogger.Debugf("[%s] active mode", sectionName(sec))
|
||||
if !blocksRequestsComplete {
|
||||
blocksRequestTime = true
|
||||
}
|
||||
if !blockHashesRequestsComplete {
|
||||
if !blockHashesRequestsComplete && parentHash != nil {
|
||||
blockHashesRequestTime = true
|
||||
}
|
||||
if !init {
|
||||
@ -753,13 +960,13 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
missing = 0
|
||||
self.wg.Add(1)
|
||||
self.procWg.Add(1)
|
||||
depth = len(section.nodes)
|
||||
depth = len(sec.nodes)
|
||||
lastMissing = depth
|
||||
// if not run at least once fully, launch iterator
|
||||
go func() {
|
||||
var node *poolNode
|
||||
IT:
|
||||
for _, node = range section.nodes {
|
||||
for _, node = range sec.nodes {
|
||||
select {
|
||||
case processC <- node:
|
||||
case <-self.quit:
|
||||
@ -771,7 +978,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
self.procWg.Done()
|
||||
}()
|
||||
} else {
|
||||
poolLogger.Debugf("[%s] restore earlier state", sectionName(section))
|
||||
poolLogger.Debugf("[%s] restore earlier state", sectionName(sec))
|
||||
processC = offC
|
||||
}
|
||||
}
|
||||
@ -781,7 +988,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
}
|
||||
peer = newPeer
|
||||
|
||||
case waiter := <-section.forkC:
|
||||
case waiter := <-sec.forkC:
|
||||
// this case just blocks the process until section is split at the fork
|
||||
<-waiter
|
||||
init = false
|
||||
@ -794,7 +1001,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
init = true
|
||||
done = true
|
||||
processC = make(chan *poolNode, missing)
|
||||
poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(section), missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth)
|
||||
continue LOOP
|
||||
}
|
||||
if ready {
|
||||
@ -811,17 +1018,24 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
missing++
|
||||
hashes = append(hashes, node.hash)
|
||||
if len(hashes) == blockBatchSize {
|
||||
poolLogger.Debugf("[%s] request %v missing blocks", sectionName(section), len(hashes))
|
||||
poolLogger.Debugf("[%s] request %v missing blocks", sectionName(sec), len(hashes))
|
||||
self.requestBlocks(blocksRequests, hashes)
|
||||
hashes = nil
|
||||
}
|
||||
missingC <- node
|
||||
} else {
|
||||
if blockChainC == nil && i == lastMissing {
|
||||
insertChain = true
|
||||
if i == lastMissing {
|
||||
if blockChainC == nil {
|
||||
insertChain = true
|
||||
} else {
|
||||
if parentHash == nil {
|
||||
parentHash = block.ParentHash()
|
||||
poolLogger.Debugf("[%s] found root block [%s]", sectionName(sec), name(parentHash))
|
||||
blockHashesRequestTime = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
poolLogger.Debugf("[%s] %v/%v/%v/%v", sectionName(section), i, missing, lastMissing, depth)
|
||||
if i == lastMissing && init {
|
||||
done = true
|
||||
}
|
||||
@ -829,23 +1043,22 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
case <-blockChainC:
|
||||
// closed blockChain channel indicates that the blockpool is reached
|
||||
// connected to the blockchain, insert the longest chain of blocks
|
||||
poolLogger.Debugf("[%s] reached blockchain", sectionName(section))
|
||||
poolLogger.Debugf("[%s] reached blockchain", sectionName(sec))
|
||||
blockChainC = nil
|
||||
// switch off hash requests in case they were on
|
||||
blockHashesRequestTime = false
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
// section root has block
|
||||
if len(section.nodes) > 0 && section.nodes[len(section.nodes)-1].block != nil {
|
||||
if len(sec.nodes) > 0 && sec.nodes[len(sec.nodes)-1].block != nil {
|
||||
insertChain = true
|
||||
}
|
||||
continue LOOP
|
||||
|
||||
} // select
|
||||
} // for
|
||||
poolLogger.Debugf("[%s] section complete: %v block hashes requests - %v block requests - missing %v/%v/%v", sectionName(section), blockHashesRequests, blocksRequests, missing, lastMissing, depth)
|
||||
|
||||
close(section.offC)
|
||||
close(sec.offC)
|
||||
|
||||
self.wg.Done()
|
||||
if peer != nil {
|
||||
@ -917,22 +1130,28 @@ func (self *peerInfo) addSection(hash []byte, section *section) (found *section)
|
||||
defer self.lock.Unlock()
|
||||
key := string(hash)
|
||||
found = self.sections[key]
|
||||
poolLogger.DebugDetailf("[%s] section process %s registered", sectionName(section), self.id)
|
||||
poolLogger.DebugDetailf("[%s] section process stored for %s", sectionName(section), self.id)
|
||||
self.sections[key] = section
|
||||
return
|
||||
}
|
||||
|
||||
func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
||||
if newPeer != nil {
|
||||
entry := self.get(newPeer.currentBlock)
|
||||
if entry == nil {
|
||||
poolLogger.Debugf("[%s] head block [%s] not found, requesting hashes", newPeer.id, name(newPeer.currentBlock))
|
||||
newPeer.requestBlockHashes(newPeer.currentBlock)
|
||||
} else {
|
||||
poolLogger.Debugf("[%s] head block [%s] found, activate chain at section [%s]", newPeer.id, name(newPeer.currentBlock), sectionName(entry.section))
|
||||
self.activateChain(entry.section, newPeer)
|
||||
}
|
||||
newPeer.quitC = make(chan bool)
|
||||
poolLogger.DebugDetailf("[%s] activate section processes", newPeer.id)
|
||||
var addSections []*section
|
||||
for hash, section := range newPeer.sections {
|
||||
// split sections get reorganised here
|
||||
if string(section.top.hash) != hash {
|
||||
addSections = append(addSections, section)
|
||||
if entry := self.get([]byte(hash)); entry != nil {
|
||||
addSections = append(addSections, entry.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, section := range addSections {
|
||||
newPeer.sections[string(section.top.hash)] = section
|
||||
}
|
||||
for hash, section := range newPeer.sections {
|
||||
// this will block if section process is waiting for peer lock
|
||||
select {
|
||||
@ -940,12 +1159,26 @@ func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
||||
poolLogger.DebugDetailf("[%s][%x] section process complete - remove", newPeer.id, hash[:4])
|
||||
delete(newPeer.sections, hash)
|
||||
case section.controlC <- newPeer:
|
||||
poolLogger.DebugDetailf("[%s][%x] registered peer with section", newPeer.id, hash[:4])
|
||||
poolLogger.DebugDetailf("[%s][%x] activates section [%s]", newPeer.id, hash[:4], sectionName(section))
|
||||
}
|
||||
}
|
||||
newPeer.quitC = make(chan bool)
|
||||
newPeer.lock.Lock()
|
||||
headSection := newPeer.headSection
|
||||
currentBlockHash := newPeer.currentBlockHash
|
||||
newPeer.lock.Unlock()
|
||||
if headSection == nil {
|
||||
poolLogger.DebugDetailf("[%s] head section for [%s] not created, requesting info", newPeer.id, name(currentBlockHash))
|
||||
self.requestHeadSection(newPeer)
|
||||
} else {
|
||||
if entry := self.get(currentBlockHash); entry != nil {
|
||||
headSection = entry.section
|
||||
}
|
||||
poolLogger.DebugDetailf("[%s] activate chain at head section [%s] for current head [%s]", newPeer.id, sectionName(headSection), name(currentBlockHash))
|
||||
self.activateChain(headSection, newPeer)
|
||||
}
|
||||
}
|
||||
if oldPeer != nil {
|
||||
poolLogger.DebugDetailf("[%s] quit section processes", oldPeer.id)
|
||||
close(oldPeer.quitC)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
|
||||
const waitTimeout = 60 // seconds
|
||||
|
||||
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugLevel))
|
||||
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
||||
|
||||
var ini = false
|
||||
|
||||
@ -336,12 +336,12 @@ func (self *peerTester) AddPeer() bool {
|
||||
|
||||
// peer sends blockhashes if and when gets a request
|
||||
func (self *peerTester) AddBlockHashes(indexes ...int) {
|
||||
i := 0
|
||||
fmt.Printf("ready to add block hashes %v\n", indexes)
|
||||
|
||||
self.waitBlockHashesRequests(indexes[0])
|
||||
fmt.Printf("adding block hashes %v\n", indexes)
|
||||
hashes := self.hashPool.indexesToHashes(indexes)
|
||||
i := 1
|
||||
next := func() (hash []byte, ok bool) {
|
||||
if i < len(hashes) {
|
||||
hash = hashes[i]
|
||||
@ -415,7 +415,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("peer0 (TD=1) not set as best")
|
||||
}
|
||||
peer0.checkBlockHashesRequests(0)
|
||||
// peer0.checkBlockHashesRequests(0)
|
||||
|
||||
best = peer2.AddPeer()
|
||||
if !best {
|
||||
@ -424,7 +424,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.id != "peer2" {
|
||||
t.Errorf("peer2 (TD=3) not set as best")
|
||||
}
|
||||
peer2.checkBlockHashesRequests(2)
|
||||
peer2.waitBlocksRequests(2)
|
||||
|
||||
best = peer1.AddPeer()
|
||||
if best {
|
||||
@ -449,7 +449,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 {
|
||||
t.Errorf("peer2 TD not updated")
|
||||
}
|
||||
peer2.checkBlockHashesRequests(2, 3)
|
||||
peer2.waitBlocksRequests(3)
|
||||
|
||||
peer1.td = 3
|
||||
peer1.currentBlock = 2
|
||||
@ -474,7 +474,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.id != "peer1" {
|
||||
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
||||
}
|
||||
peer1.checkBlockHashesRequests(2)
|
||||
peer1.waitBlocksRequests(2)
|
||||
|
||||
blockPool.RemovePeer("peer1")
|
||||
peer, best = blockPool.getPeer("peer1")
|
||||
@ -485,6 +485,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
||||
}
|
||||
peer0.waitBlocksRequests(0)
|
||||
|
||||
blockPool.RemovePeer("peer0")
|
||||
peer, best = blockPool.getPeer("peer0")
|
||||
@ -502,7 +503,7 @@ func TestAddPeer(t *testing.T) {
|
||||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("peer0 (TD=1) should be set as best")
|
||||
}
|
||||
peer0.checkBlockHashesRequests(0, 0, 3)
|
||||
peer0.waitBlocksRequests(3)
|
||||
|
||||
blockPool.Stop()
|
||||
|
||||
@ -513,17 +514,36 @@ func TestPeerWithKnownBlock(t *testing.T) {
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.refBlockChain[0] = nil
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
// hashPool, blockPool, blockPoolTester := newTestBlockPool()
|
||||
blockPool.Start()
|
||||
|
||||
peer0 := blockPoolTester.newPeer("0", 1, 0)
|
||||
peer0.AddPeer()
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
// no request on known block
|
||||
peer0.checkBlockHashesRequests()
|
||||
}
|
||||
|
||||
func TestPeerWithKnownParentBlock(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.initRefBlockChain(1)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPool.Start()
|
||||
|
||||
peer0 := blockPoolTester.newPeer("0", 1, 1)
|
||||
peer0.AddPeer()
|
||||
peer0.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
peer0.checkBlocksRequests([]int{1})
|
||||
peer0.checkBlockHashesRequests()
|
||||
blockPoolTester.refBlockChain[1] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestSimpleChain(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
@ -534,8 +554,9 @@ func TestSimpleChain(t *testing.T) {
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||
peer1.AddPeer()
|
||||
peer1.AddBlocks(1, 2)
|
||||
go peer1.AddBlockHashes(2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
@ -543,6 +564,26 @@ func TestSimpleChain(t *testing.T) {
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestChainConnectingWithParentHash(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(3)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[3] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestInvalidBlock(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
@ -554,8 +595,9 @@ func TestInvalidBlock(t *testing.T) {
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
@ -566,7 +608,7 @@ func TestInvalidBlock(t *testing.T) {
|
||||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expected invalid block error, got nothing")
|
||||
t.Errorf("expected invalid block error, got nothing %v", peer1.peerErrors)
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,7 +621,7 @@ func TestVerifyPoW(t *testing.T) {
|
||||
blockPoolTester.blockPool.verifyPoW = func(b pow.Block) bool {
|
||||
bb, _ := b.(*types.Block)
|
||||
indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()})
|
||||
if indexes[0] == 1 && !first {
|
||||
if indexes[0] == 2 && !first {
|
||||
first = true
|
||||
return false
|
||||
} else {
|
||||
@ -590,15 +632,17 @@ func TestVerifyPoW(t *testing.T) {
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(2, 1, 0)
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
// blockPool.Wait(waitTimeout * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[2] = []int{}
|
||||
blockPoolTester.refBlockChain[1] = []int{}
|
||||
delete(blockPoolTester.refBlockChain, 2)
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
if len(peer1.peerErrors) == 1 {
|
||||
if peer1.peerErrors[0] != ErrInvalidPoW {
|
||||
@ -620,8 +664,9 @@ func TestMultiSectionChain(t *testing.T) {
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(4, 5)
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
go peer1.AddBlocks(2, 3, 4, 5)
|
||||
go peer1.AddBlocks(2, 3, 4)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
@ -641,14 +686,17 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(4, 5) // partially complete section
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
peer1.AddBlocks(2, 3) // partially complete section
|
||||
peer1.AddBlocks(3, 4) // partially complete section
|
||||
// peer1 found new blocks
|
||||
peer1.td = 2
|
||||
peer1.currentBlock = 7
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(6, 7)
|
||||
go peer1.AddBlockHashes(7, 6, 5)
|
||||
go peer1.AddBlocks(3, 4, 5, 6, 7)
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlocks(5, 6)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0) // tests that hash request from known chain root is remembered
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
@ -658,35 +706,37 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerSwitch(t *testing.T) {
|
||||
func TestPeerSwitchUp(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPoolTester.initRefBlockChain(7)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 6)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 7)
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
go peer1.AddBlocks(5, 6)
|
||||
go peer1.AddBlockHashes(6, 5, 4, 3) //
|
||||
peer1.AddBlocks(2, 3) // section partially complete, block 3 will be preserved after peer demoted
|
||||
peer2.AddPeer() // peer2 is promoted as best peer, peer1 is demoted
|
||||
go peer2.AddBlockHashes(6, 5) //
|
||||
go peer2.AddBlocks(4, 5, 6) // tests that block request for earlier section is remembered
|
||||
go peer2.AddBlocks(6, 7)
|
||||
go peer2.AddBlockHashes(7, 6) //
|
||||
go peer2.AddBlocks(4, 5) // tests that block request for earlier section is remembered
|
||||
go peer1.AddBlocks(3, 4) // tests that connecting section by demoted peer is remembered and blocks are accepted from demoted peer
|
||||
go peer2.AddBlockHashes(3, 2, 1, 0) // tests that known chain section is activated, hash requests from 3 is remembered
|
||||
peer2.AddBlocks(0, 1, 2) // final blocks linking to blockchain sent
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[6] = []int{}
|
||||
blockPoolTester.refBlockChain[7] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerDownSwitch(t *testing.T) {
|
||||
func TestPeerSwitchDown(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
@ -698,12 +748,39 @@ func TestPeerDownSwitch(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlockHashes(6, 5, 4)
|
||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
||||
go peer2.AddBlockHashes(6, 5, 4) //
|
||||
peer2.AddBlocks(4, 5) //
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
||||
go peer1.AddBlockHashes(4, 3, 2, 1, 0) //
|
||||
go peer1.AddBlocks(3, 4, 5) // tests that section set by demoted peer is remembered and blocks are accepted
|
||||
go peer1.AddBlocks(3, 4) // tests that section set by demoted peer is remembered and blocks are accepted , this connects the chain sections together
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[6] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerCompleteSectionSwitchDown(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
||||
go peer2.AddBlockHashes(6, 5, 4) //
|
||||
peer2.AddBlocks(3, 4, 5) // complete section
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
||||
peer1.AddBlockHashes(4, 3, 2, 1, 0) // tests that hash request are directly connecting if the head block exists
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
@ -725,11 +802,13 @@ func TestPeerSwitchBack(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlocks(7, 8)
|
||||
go peer2.AddBlockHashes(8, 7, 6)
|
||||
go peer2.AddBlockHashes(6, 5, 4)
|
||||
peer2.AddBlocks(4, 5) // section partially complete
|
||||
peer1.AddPeer() // peer1 is promoted as best peer
|
||||
go peer1.AddBlockHashes(11, 10) // only gives useless results
|
||||
go peer1.AddBlocks(10, 11) //
|
||||
peer1.AddBlockHashes(11, 10) // only gives useless results
|
||||
blockPool.RemovePeer("peer1") // peer1 disconnects
|
||||
go peer2.AddBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
|
||||
go peer2.AddBlocks(3, 4, 5, 6, 7, 8) // tests that section 4, 5, 6 and 7, 8 are remembered for missing blocks
|
||||
@ -756,11 +835,13 @@ func TestForkSimple(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(1, 2, 3, 7, 8, 9)
|
||||
peer1.AddBlocks(1, 2, 3, 7, 8)
|
||||
peer2.AddPeer() // peer2 is promoted as best peer
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // fork on 3 -> 4 (earlier child: 7)
|
||||
go peer2.AddBlocks(1, 2, 3, 4, 5, 6)
|
||||
go peer2.AddBlocks(1, 2, 3, 4, 5)
|
||||
go peer2.AddBlockHashes(2, 1, 0)
|
||||
peer2.AddBlocks(0, 1, 2)
|
||||
|
||||
@ -790,23 +871,24 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(8, 9) // partial section
|
||||
peer1.AddBlocks(8, 9) //
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2) //
|
||||
peer1.AddBlocks(7, 8) // partial section
|
||||
peer2.AddPeer() //
|
||||
peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(1, 2, 3, 4, 5, 6) //
|
||||
peer2.AddBlocks(1, 2, 3, 4, 5) //
|
||||
|
||||
// peer1 finds new blocks
|
||||
peer1.td = 3
|
||||
peer1.currentBlock = 11
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(10, 11)
|
||||
go peer1.AddBlockHashes(11, 10, 9)
|
||||
peer1.AddBlocks(7, 8, 9, 10, 11)
|
||||
go peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
||||
// go peer1.AddBlockHashes(1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
||||
peer1.AddBlocks(9, 10)
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
||||
go peer1.AddBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
@ -834,16 +916,18 @@ func TestForkSwitchBackByPeerSwitchBack(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(8, 9)
|
||||
peer2.AddPeer() //
|
||||
peer1.AddBlocks(7, 8)
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(2, 3, 4, 5, 6) //
|
||||
peer2.AddBlocks(2, 3, 4, 5) //
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
||||
peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered
|
||||
go peer1.AddBlocks(3, 7, 8) // tests that block requests on earlier fork are remembered
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
|
||||
go peer1.AddBlocks(1, 2) //
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
@ -871,17 +955,19 @@ func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) {
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7)
|
||||
peer1.AddBlocks(3, 7, 8, 9) // make sure this section is complete
|
||||
peer1.AddBlocks(3, 7, 8) // make sure this section is complete
|
||||
time.Sleep(1 * time.Second)
|
||||
go peer1.AddBlockHashes(7, 3, 2) // block 3/7 is section boundary
|
||||
peer1.AddBlocks(2, 3) // partially complete sections
|
||||
peer1.AddBlocks(2, 3) // partially complete sections block 2 missing
|
||||
peer2.AddPeer() //
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(2, 3, 4, 5, 6) // block 2 still missing.
|
||||
peer2.AddBlocks(2, 3, 4, 5) // block 2 still missing.
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
||||
peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
// peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
|
@ -16,6 +16,7 @@ const (
|
||||
ErrInvalidBlock
|
||||
ErrInvalidPoW
|
||||
ErrUnrequestedBlock
|
||||
ErrInsufficientChainInfo
|
||||
)
|
||||
|
||||
var errorToString = map[int]string{
|
||||
@ -30,6 +31,7 @@ var errorToString = map[int]string{
|
||||
ErrInvalidBlock: "Invalid block",
|
||||
ErrInvalidPoW: "Invalid PoW",
|
||||
ErrUnrequestedBlock: "Unrequested block",
|
||||
ErrInsufficientChainInfo: "Insufficient chain info",
|
||||
}
|
||||
|
||||
type protocolError struct {
|
||||
|
@ -67,6 +67,8 @@ type newBlockMsgData struct {
|
||||
TD *big.Int
|
||||
}
|
||||
|
||||
const maxHashes = 255
|
||||
|
||||
type getBlockHashesMsgData struct {
|
||||
Hash []byte
|
||||
Amount uint64
|
||||
@ -122,7 +124,7 @@ func (self *ethProtocol) handle() error {
|
||||
defer msg.Discard()
|
||||
|
||||
switch msg.Code {
|
||||
|
||||
case GetTxMsg: // ignore
|
||||
case StatusMsg:
|
||||
return self.protoError(ErrExtraStatusMsg, "")
|
||||
|
||||
@ -139,8 +141,13 @@ func (self *ethProtocol) handle() error {
|
||||
if err := msg.Decode(&request); err != nil {
|
||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||
}
|
||||
|
||||
//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
|
||||
if request.Amount > maxHashes {
|
||||
request.Amount = maxHashes
|
||||
}
|
||||
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
|
||||
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
|
||||
case BlockHashesMsg:
|
||||
// TODO: redo using lazy decode , this way very inefficient on known chains
|
||||
@ -185,7 +192,7 @@ func (self *ethProtocol) handle() error {
|
||||
break
|
||||
}
|
||||
}
|
||||
return self.rw.EncodeMsg(BlocksMsg, blocks...)
|
||||
return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...)
|
||||
|
||||
case BlocksMsg:
|
||||
msgStream := rlp.NewStream(msg.Payload)
|
||||
@ -211,16 +218,6 @@ func (self *ethProtocol) handle() error {
|
||||
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
|
||||
// (or selected as new best peer)
|
||||
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
||||
called := true
|
||||
iter := func() ([]byte, bool) {
|
||||
if called {
|
||||
called = false
|
||||
return hash, true
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
self.blockPool.AddBlockHashes(iter, self.id)
|
||||
self.blockPool.AddBlock(request.Block, self.id)
|
||||
}
|
||||
|
||||
@ -298,12 +295,12 @@ func (self *ethProtocol) handleStatus() error {
|
||||
|
||||
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
||||
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
|
||||
return self.rw.EncodeMsg(GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
|
||||
return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
|
||||
}
|
||||
|
||||
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
||||
self.peer.Debugf("fetching %v blocks", len(hashes))
|
||||
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
}
|
||||
|
||||
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
|
||||
|
@ -41,10 +41,6 @@ func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return self.WriteMsg(p2p.NewMsg(code, data...))
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||
msg, ok := <-self.in
|
||||
if !ok {
|
||||
|
@ -12,8 +12,8 @@ EOF
|
||||
|
||||
peer 11 01
|
||||
peer 12 02
|
||||
P13ID=$PID
|
||||
P12ID=$PID
|
||||
test_node $NAME "" -loglevel 5 $JSFILE
|
||||
sleep 0.5
|
||||
kill $P13ID
|
||||
sleep 0.3
|
||||
kill $P12ID
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
TIMEOUT=35
|
||||
TIMEOUT=12
|
||||
|
||||
cat >> $JSFILE <<EOF
|
||||
eth.addPeer("localhost:30311");
|
||||
sleep(30000);
|
||||
sleep(10000);
|
||||
eth.export("$CHAIN_TEST");
|
||||
EOF
|
||||
|
||||
|
@ -3,14 +3,14 @@
|
||||
# launched by run.sh
|
||||
function test_node {
|
||||
rm -rf $DIR/$1
|
||||
ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1"
|
||||
ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1 -port 303$1"
|
||||
if [ "" != "$2" ]; then
|
||||
chain="chains/$2.chain"
|
||||
echo "import chain $chain"
|
||||
$ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import
|
||||
fi
|
||||
echo "starting test node $1 with extra args ${@:3}"
|
||||
$ETH $ARGS -port 303$1 ${@:3} &
|
||||
echo "starting test node $1 with args $ARGS ${@:3}"
|
||||
$ETH $ARGS ${@:3} &
|
||||
PID=$!
|
||||
PIDS="$PIDS $PID"
|
||||
}
|
||||
|
@ -84,4 +84,5 @@ var (
|
||||
BigFalse = Big0
|
||||
Big32 = big.NewInt(32)
|
||||
Big256 = big.NewInt(0xff)
|
||||
Big257 = big.NewInt(257)
|
||||
)
|
||||
|
@ -10,8 +10,6 @@ import (
|
||||
|
||||
// Config struct
|
||||
type ConfigManager struct {
|
||||
Db Database
|
||||
|
||||
ExecPath string
|
||||
Debug bool
|
||||
Diff bool
|
||||
|
@ -12,7 +12,7 @@ func ExpandHomePath(p string) (path string) {
|
||||
path = p
|
||||
|
||||
// Check in case of paths like "/something/~/something/"
|
||||
if path[:2] == "~/" {
|
||||
if len(path) > 1 && path[:2] == "~/" {
|
||||
usr, _ := user.Current()
|
||||
dir := usr.HomeDir
|
||||
|
||||
|
@ -137,7 +137,7 @@ func Encode(object interface{}) []byte {
|
||||
case byte:
|
||||
buff.Write(Encode(big.NewInt(int64(t))))
|
||||
case *big.Int:
|
||||
// Not sure how this is possible while we check for
|
||||
// Not sure how this is possible while we check for nil
|
||||
if t == nil {
|
||||
buff.WriteByte(0xc0)
|
||||
} else {
|
||||
|
@ -68,3 +68,11 @@ out:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Filters) Match(a, b Filter) bool {
|
||||
return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b)
|
||||
}
|
||||
|
||||
func (self *Filters) Get(i int) Filter {
|
||||
return self.watchers[i]
|
||||
}
|
||||
|
@ -129,10 +129,9 @@ func (self *JSRE) initStdFuncs() {
|
||||
*/
|
||||
|
||||
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
||||
var state *state.StateDB
|
||||
var block *types.Block
|
||||
|
||||
if len(call.ArgumentList) > 0 {
|
||||
var block *types.Block
|
||||
if call.Argument(0).IsNumber() {
|
||||
num, _ := call.Argument(0).ToInteger()
|
||||
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
|
||||
@ -149,12 +148,12 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
state = block.State()
|
||||
} else {
|
||||
state = self.ethereum.ChainManager().State()
|
||||
block = self.ethereum.ChainManager().CurrentBlock()
|
||||
}
|
||||
|
||||
v, _ := self.Vm.ToValue(state.Dump())
|
||||
statedb := state.New(block.Root(), self.ethereum.Db())
|
||||
v, _ := self.Vm.ToValue(statedb.Dump())
|
||||
|
||||
return v
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -69,6 +70,7 @@ type Miner struct {
|
||||
mining bool
|
||||
|
||||
MinAcceptedGasPrice *big.Int
|
||||
Extra string
|
||||
}
|
||||
|
||||
func New(coinbase []byte, eth *eth.Ethereum) *Miner {
|
||||
@ -177,7 +179,9 @@ func (self *Miner) mine() {
|
||||
blockProcessor = self.eth.BlockProcessor()
|
||||
chainMan = self.eth.ChainManager()
|
||||
block = chainMan.NewBlock(self.Coinbase)
|
||||
state = state.New(block.Root(), self.eth.Db())
|
||||
)
|
||||
block.Header().Extra = self.Extra
|
||||
|
||||
// Apply uncles
|
||||
if len(self.uncles) > 0 {
|
||||
@ -185,13 +189,11 @@ func (self *Miner) mine() {
|
||||
}
|
||||
|
||||
parent := chainMan.GetBlock(block.ParentHash())
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
||||
coinbase := state.GetOrNewStateObject(block.Coinbase())
|
||||
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||
|
||||
transactions := self.finiliseTxs()
|
||||
|
||||
state := block.State()
|
||||
|
||||
// Accumulate all valid transactions and apply them to the new state
|
||||
// Error may be ignored. It's not important during mining
|
||||
receipts, txs, _, erroneous, err := blockProcessor.ApplyTransactions(coinbase, state, block, transactions, true)
|
||||
|
@ -71,14 +71,11 @@ type MsgReader interface {
|
||||
}
|
||||
|
||||
type MsgWriter interface {
|
||||
// WriteMsg sends an existing message.
|
||||
// The Payload reader of the message is consumed.
|
||||
// WriteMsg sends a message. It will block until the message's
|
||||
// Payload has been consumed by the other end.
|
||||
//
|
||||
// Note that messages can be sent only once.
|
||||
WriteMsg(Msg) error
|
||||
|
||||
// EncodeMsg writes an RLP-encoded message with the given
|
||||
// code and data elements.
|
||||
EncodeMsg(code uint64, data ...interface{}) error
|
||||
}
|
||||
|
||||
// MsgReadWriter provides reading and writing of encoded messages.
|
||||
@ -87,6 +84,12 @@ type MsgReadWriter interface {
|
||||
MsgWriter
|
||||
}
|
||||
|
||||
// EncodeMsg writes an RLP-encoded message with the given code and
|
||||
// data elements.
|
||||
func EncodeMsg(w MsgWriter, code uint64, data ...interface{}) error {
|
||||
return w.WriteMsg(NewMsg(code, data...))
|
||||
}
|
||||
|
||||
var magicToken = []byte{34, 64, 8, 145}
|
||||
|
||||
func writeMsg(w io.Writer, msg Msg) error {
|
||||
@ -209,11 +212,6 @@ func (p *MsgPipeRW) WriteMsg(msg Msg) error {
|
||||
return ErrPipeClosed
|
||||
}
|
||||
|
||||
// EncodeMsg is a convenient shorthand for sending an RLP-encoded message.
|
||||
func (p *MsgPipeRW) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return p.WriteMsg(NewMsg(code, data...))
|
||||
}
|
||||
|
||||
// ReadMsg returns a message sent on the other end of the pipe.
|
||||
func (p *MsgPipeRW) ReadMsg() (Msg, error) {
|
||||
if atomic.LoadInt32(p.closed) == 0 {
|
||||
|
@ -75,8 +75,8 @@ func TestDecodeRealMsg(t *testing.T) {
|
||||
func ExampleMsgPipe() {
|
||||
rw1, rw2 := MsgPipe()
|
||||
go func() {
|
||||
rw1.EncodeMsg(8, []byte{0, 0})
|
||||
rw1.EncodeMsg(5, []byte{1, 1})
|
||||
EncodeMsg(rw1, 8, []byte{0, 0})
|
||||
EncodeMsg(rw1, 5, []byte{1, 1})
|
||||
rw1.Close()
|
||||
}()
|
||||
|
||||
@ -100,7 +100,7 @@ loop:
|
||||
rw1, rw2 := MsgPipe()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
if err := rw1.EncodeMsg(1); err == nil {
|
||||
if err := EncodeMsg(rw1, 1); err == nil {
|
||||
t.Error("EncodeMsg returned nil error")
|
||||
} else if err != ErrPipeClosed {
|
||||
t.Error("EncodeMsg returned wrong error: got %v, want %v", err, ErrPipeClosed)
|
||||
|
22
p2p/peer.go
22
p2p/peer.go
@ -460,25 +460,3 @@ func (r *eofSignal) Read(buf []byte) (int, error) {
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (peer *Peer) PeerList() []interface{} {
|
||||
peers := peer.otherPeers()
|
||||
ds := make([]interface{}, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
p.infolock.Lock()
|
||||
addr := p.listenAddr
|
||||
p.infolock.Unlock()
|
||||
// filter out this peer and peers that are not listening or
|
||||
// have not completed the handshake.
|
||||
// TODO: track previously sent peers and exclude them as well.
|
||||
if p == peer || addr == nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, addr)
|
||||
}
|
||||
ourAddr := peer.ourListenAddr
|
||||
if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() {
|
||||
ds = append(ds, ourAddr)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
@ -126,10 +126,10 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
|
||||
Name: "a",
|
||||
Length: 2,
|
||||
Run: func(peer *Peer, rw MsgReadWriter) error {
|
||||
if err := rw.EncodeMsg(2); err == nil {
|
||||
if err := EncodeMsg(rw, 2); err == nil {
|
||||
t.Error("expected error for out-of-range msg code, got nil")
|
||||
}
|
||||
if err := rw.EncodeMsg(1, "foo", "bar"); err != nil {
|
||||
if err := EncodeMsg(rw, 1, "foo", "bar"); err != nil {
|
||||
t.Errorf("write error: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
@ -119,14 +119,14 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
||||
|
||||
getPeersTick := time.NewTicker(10 * time.Second)
|
||||
defer getPeersTick.Stop()
|
||||
err := bp.rw.EncodeMsg(getPeersMsg)
|
||||
err := EncodeMsg(bp.rw, getPeersMsg)
|
||||
|
||||
for err == nil {
|
||||
select {
|
||||
case err = <-quit:
|
||||
return err
|
||||
case <-getPeersTick.C:
|
||||
err = bp.rw.EncodeMsg(getPeersMsg)
|
||||
err = EncodeMsg(bp.rw, getPeersMsg)
|
||||
case event := <-activity.Chan():
|
||||
ping.Reset(pingTimeout)
|
||||
lastActive = event.(time.Time)
|
||||
@ -134,7 +134,7 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
||||
if lastActive.Add(pingTimeout * 2).Before(t) {
|
||||
err = newPeerError(errPingTimeout, "")
|
||||
} else if lastActive.Add(pingTimeout).Before(t) {
|
||||
err = bp.rw.EncodeMsg(pingMsg)
|
||||
err = EncodeMsg(bp.rw, pingMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,12 +164,12 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error {
|
||||
return discRequestedError(reason[0])
|
||||
|
||||
case pingMsg:
|
||||
return bp.rw.EncodeMsg(pongMsg)
|
||||
return EncodeMsg(bp.rw, pongMsg)
|
||||
|
||||
case pongMsg:
|
||||
|
||||
case getPeersMsg:
|
||||
peers := bp.peer.PeerList()
|
||||
peers := bp.peerList()
|
||||
// this is dangerous. the spec says that we should _delay_
|
||||
// sending the response if no new information is available.
|
||||
// this means that would need to send a response later when
|
||||
@ -177,7 +177,7 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error {
|
||||
//
|
||||
// TODO: add event mechanism to notify baseProtocol for new peers
|
||||
if len(peers) > 0 {
|
||||
return bp.rw.EncodeMsg(peersMsg, peers...)
|
||||
return EncodeMsg(bp.rw, peersMsg, peers...)
|
||||
}
|
||||
|
||||
case peersMsg:
|
||||
@ -264,3 +264,25 @@ func (bp *baseProtocol) handshakeMsg() Msg {
|
||||
bp.peer.ourID.Pubkey()[1:],
|
||||
)
|
||||
}
|
||||
|
||||
func (bp *baseProtocol) peerList() []interface{} {
|
||||
peers := bp.peer.otherPeers()
|
||||
ds := make([]interface{}, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
p.infolock.Lock()
|
||||
addr := p.listenAddr
|
||||
p.infolock.Unlock()
|
||||
// filter out this peer and peers that are not listening or
|
||||
// have not completed the handshake.
|
||||
// TODO: track previously sent peers and exclude them as well.
|
||||
if p == bp.peer || addr == nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, addr)
|
||||
}
|
||||
ourAddr := bp.peer.ourListenAddr
|
||||
if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() {
|
||||
ds = append(ds, ourAddr)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@ -36,50 +37,71 @@ func newTestPeer() (peer *Peer) {
|
||||
}
|
||||
|
||||
func TestBaseProtocolPeers(t *testing.T) {
|
||||
cannedPeerList := []*peerAddr{
|
||||
peerList := []*peerAddr{
|
||||
{IP: net.ParseIP("1.2.3.4"), Port: 2222, Pubkey: []byte{}},
|
||||
{IP: net.ParseIP("5.6.7.8"), Port: 3333, Pubkey: []byte{}},
|
||||
}
|
||||
var ownAddr *peerAddr = &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}}
|
||||
listenAddr := &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}}
|
||||
rw1, rw2 := MsgPipe()
|
||||
defer rw1.Close()
|
||||
wg := new(sync.WaitGroup)
|
||||
|
||||
// run matcher, close pipe when addresses have arrived
|
||||
addrChan := make(chan *peerAddr, len(cannedPeerList))
|
||||
numPeers := len(peerList) + 1
|
||||
addrChan := make(chan *peerAddr)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for _, want := range cannedPeerList {
|
||||
got := <-addrChan
|
||||
t.Logf("got peer: %+v", got)
|
||||
i := 0
|
||||
for got := range addrChan {
|
||||
var want *peerAddr
|
||||
switch {
|
||||
case i < len(peerList):
|
||||
want = peerList[i]
|
||||
case i == len(peerList):
|
||||
want = listenAddr // listenAddr should be the last thing sent
|
||||
}
|
||||
t.Logf("got peer %d/%d: %v", i+1, numPeers, got)
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("mismatch: got %#v, want %#v", got, want)
|
||||
t.Errorf("mismatch: got %+v, want %+v", got, want)
|
||||
}
|
||||
i++
|
||||
if i == numPeers {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(addrChan)
|
||||
var own []*peerAddr
|
||||
var got *peerAddr
|
||||
for got = range addrChan {
|
||||
own = append(own, got)
|
||||
if i != numPeers {
|
||||
t.Errorf("wrong number of peers received: got %d, want %d", i, numPeers)
|
||||
}
|
||||
if len(own) != 1 || !reflect.DeepEqual(ownAddr, own[0]) {
|
||||
t.Errorf("mismatch: peers own address is incorrectly or not given, got %v, want %#v", ownAddr)
|
||||
}
|
||||
rw2.Close()
|
||||
rw1.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
// run first peer
|
||||
|
||||
// run first peer (in background)
|
||||
peer1 := newTestPeer()
|
||||
peer1.ourListenAddr = ownAddr
|
||||
peer1.ourListenAddr = listenAddr
|
||||
peer1.otherPeers = func() []*Peer {
|
||||
pl := make([]*Peer, len(cannedPeerList))
|
||||
for i, addr := range cannedPeerList {
|
||||
pl := make([]*Peer, len(peerList))
|
||||
for i, addr := range peerList {
|
||||
pl[i] = &Peer{listenAddr: addr}
|
||||
}
|
||||
return pl
|
||||
}
|
||||
go runBaseProtocol(peer1, rw1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
runBaseProtocol(peer1, rw1)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// run second peer
|
||||
peer2 := newTestPeer()
|
||||
peer2.newPeerAddr = addrChan // feed peer suggestions into matcher
|
||||
if err := runBaseProtocol(peer2, rw2); err != ErrPipeClosed {
|
||||
t.Errorf("peer2 terminated with unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// terminate matcher
|
||||
close(addrChan)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBaseProtocolDisconnect(t *testing.T) {
|
||||
@ -93,7 +115,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
||||
if err := expectMsg(rw2, handshakeMsg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err := rw2.EncodeMsg(handshakeMsg,
|
||||
err := EncodeMsg(rw2, handshakeMsg,
|
||||
baseProtocolVersion,
|
||||
"",
|
||||
[]interface{}{},
|
||||
@ -106,7 +128,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
||||
if err := expectMsg(rw2, getPeersMsg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := rw2.EncodeMsg(discMsg, DiscQuitting); err != nil {
|
||||
if err := EncodeMsg(rw2, discMsg, DiscQuitting); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ func (srv *Server) dialLoop() {
|
||||
case desc := <-suggest:
|
||||
// candidate peer found, will dial out asyncronously
|
||||
// if connection fails slot will be released
|
||||
srvlog.Infof("dial %v (%v)", desc, *slot)
|
||||
srvlog.DebugDetailf("dial %v (%v)", desc, *slot)
|
||||
go srv.dialPeer(desc, *slot)
|
||||
// we can watch if more peers needed in the next loop
|
||||
slots = srv.peerSlots
|
||||
@ -355,7 +355,7 @@ func (srv *Server) dialPeer(desc *peerAddr, slot int) {
|
||||
srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot)
|
||||
conn, err := srv.Dialer.Dial(desc.Network(), desc.String())
|
||||
if err != nil {
|
||||
srvlog.Errorf("Dial error: %v", err)
|
||||
srvlog.DebugDetailf("dial error: %v", err)
|
||||
srv.peerSlots <- slot
|
||||
return
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package ar
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type Block interface {
|
||||
Trie() *trie.Trie
|
||||
Diff() *big.Int
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package ar
|
||||
|
||||
import "math/big"
|
||||
|
||||
const lenops int64 = 9
|
||||
|
||||
type OpsFunc func(a, b *big.Int) *big.Int
|
||||
|
||||
var ops [lenops]OpsFunc
|
||||
|
||||
func init() {
|
||||
ops[0] = Add
|
||||
ops[1] = Mul
|
||||
ops[2] = Mod
|
||||
ops[3] = Xor
|
||||
ops[4] = And
|
||||
ops[5] = Or
|
||||
ops[6] = Sub1
|
||||
ops[7] = XorSub
|
||||
ops[8] = Rsh
|
||||
}
|
||||
|
||||
func Add(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Add(x, y)
|
||||
}
|
||||
func Mul(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Mul(x, y)
|
||||
}
|
||||
func Mod(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Mod(x, y)
|
||||
}
|
||||
func Xor(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Xor(x, y)
|
||||
}
|
||||
func And(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).And(x, y)
|
||||
}
|
||||
func Or(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Or(x, y)
|
||||
}
|
||||
func Sub1(x, y *big.Int) *big.Int {
|
||||
a := big.NewInt(-1)
|
||||
a.Sub(a, x)
|
||||
|
||||
return a
|
||||
}
|
||||
func XorSub(x, y *big.Int) *big.Int {
|
||||
t := Sub1(x, nil)
|
||||
|
||||
return t.Xor(t, y)
|
||||
}
|
||||
func Rsh(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Rsh(x, uint(y.Uint64()%64))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user