Merge branch 'develop' into jsonrpc
This commit is contained in:
commit
d92fde6980
|
@ -10,3 +10,8 @@
|
|||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
.ethtest
|
||||
|
||||
#*
|
||||
.#*
|
||||
*#
|
||||
*~
|
||||
|
|
|
@ -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() {
|
||||
})
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"directory": "example/js/",
|
||||
"cwd": "./",
|
||||
"analytics": false
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
example/js
|
||||
node_modules
|
||||
test
|
||||
.gitignore
|
||||
.editorconfig
|
||||
.travis.yml
|
||||
.npmignore
|
||||
component.json
|
||||
testling.html
|
|
@ -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
|
|
@ -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/>.
|
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
});
|
|
@ -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']);
|
||||
|
|
@ -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;
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyStorePlain(t *testing.T) {
|
||||
ks := NewKeyStorePlain(DefaultDataDir())
|
||||
pass := "" // not used but required by API
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
k2 := new(Key)
|
||||
k2, err = ks.GetKey(k1.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k2.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphrase(t *testing.T) {
|
||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||
pass := "foo"
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
k2 := new(Key)
|
||||
k2, err = ks.GetKey(k1.Id, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(k1.Id, k2.Id) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k2.Id, pass) // also to clean up created files
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||
pass := "foo"
|
||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ks.GetKey(k1.Id, "bar") // wrong passphrase
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k1.Id, "bar") // wrong passphrase
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ks.DeleteKey(k1.Id, pass) // to clean up
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
seedNodeAddress = "poc-7.ethdev.com:30300"
|
||||
seedNodeAddress = "poc-8.ethdev.com:30303"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -81,7 +81,7 @@ type Ethereum struct {
|
|||
func New(config *Config) (*Ethereum, error) {
|
||||
// Boostrap database
|
||||
logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel)
|
||||
db, err := ethdb.NewLDBDatabase("database")
|
||||
db, err := ethdb.NewLDBDatabase("blockchain")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||
clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey())
|
||||
|
||||
saveProtocolVersion(db)
|
||||
ethutil.Config.Db = db
|
||||
//ethutil.Config.Db = db
|
||||
|
||||
eth := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
|
@ -123,9 +123,9 @@ func New(config *Config) (*Ethereum, error) {
|
|||
logger: logger,
|
||||
}
|
||||
|
||||
eth.chainManager = core.NewChainManager(eth.EventMux())
|
||||
eth.chainManager = core.NewChainManager(db, eth.EventMux())
|
||||
eth.txPool = core.NewTxPool(eth.EventMux())
|
||||
eth.blockProcessor = core.NewBlockProcessor(eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.chainManager.SetProcessor(eth.blockProcessor)
|
||||
eth.whisper = whisper.New()
|
||||
|
||||
|
@ -134,24 +134,20 @@ func New(config *Config) (*Ethereum, error) {
|
|||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
||||
|
||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||
protocols := []p2p.Protocol{ethProto}
|
||||
|
||||
if config.Shh {
|
||||
eth.whisper = whisper.New()
|
||||
protocols = append(protocols, eth.whisper.Protocol())
|
||||
}
|
||||
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||
|
||||
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(nat)
|
||||
|
||||
eth.net = &p2p.Server{
|
||||
Identity: clientId,
|
||||
MaxPeers: config.MaxPeers,
|
||||
Protocols: protocols,
|
||||
Blacklist: eth.blacklist,
|
||||
NAT: nat,
|
||||
NAT: p2p.UPNP(),
|
||||
NoDial: !config.Dial,
|
||||
}
|
||||
|
||||
|
@ -249,7 +245,7 @@ func (s *Ethereum) Start(seed bool) error {
|
|||
if seed {
|
||||
logger.Infof("Connect to seed node %v", seedNodeAddress)
|
||||
if err := s.SuggestPeer(seedNodeAddress); err != nil {
|
||||
return err
|
||||
logger.Infoln(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
@ -24,8 +25,8 @@ const (
|
|||
blocksRequestRepetition = 1
|
||||
blockHashesRequestInterval = 500 // ms
|
||||
blocksRequestMaxIdleRounds = 100
|
||||
cacheTimeout = 3 // minutes
|
||||
blockTimeout = 5 // minutes
|
||||
blockHashesTimeout = 60 // seconds
|
||||
blocksTimeout = 120 // seconds
|
||||
)
|
||||
|
||||
type poolNode struct {
|
||||
|
@ -70,9 +71,14 @@ type BlockPool struct {
|
|||
type peerInfo struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
td *big.Int
|
||||
currentBlock []byte
|
||||
id string
|
||||
td *big.Int
|
||||
currentBlockHash []byte
|
||||
currentBlock *types.Block
|
||||
currentBlockC chan *types.Block
|
||||
parentHash []byte
|
||||
headSection *section
|
||||
headSectionC chan *section
|
||||
id string
|
||||
|
||||
requestBlockHashes func([]byte) error
|
||||
requestBlocks func([][]byte) error
|
||||
|
@ -203,30 +209,39 @@ func (self *BlockPool) Wait(t time.Duration) {
|
|||
// AddPeer is called by the eth protocol instance running on the peer after
|
||||
// the status message has been received with total difficulty and current block hash
|
||||
// AddPeer can only be used once, RemovePeer needs to be called when the peer disconnects
|
||||
func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) bool {
|
||||
func (self *BlockPool) AddPeer(td *big.Int, currentBlockHash []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool) {
|
||||
|
||||
self.peersLock.Lock()
|
||||
defer self.peersLock.Unlock()
|
||||
peer, ok := self.peers[peerId]
|
||||
if ok {
|
||||
poolLogger.Debugf("Update peer %v with td %v and current block %x", peerId, td, currentBlock[:4])
|
||||
peer.td = td
|
||||
peer.currentBlock = currentBlock
|
||||
if bytes.Compare(peer.currentBlockHash, currentBlockHash) != 0 {
|
||||
poolLogger.Debugf("Update peer %v with td %v and current block %s", peerId, td, name(currentBlockHash))
|
||||
peer.lock.Lock()
|
||||
peer.td = td
|
||||
peer.currentBlockHash = currentBlockHash
|
||||
peer.currentBlock = nil
|
||||
peer.parentHash = nil
|
||||
peer.headSection = nil
|
||||
peer.lock.Unlock()
|
||||
}
|
||||
} else {
|
||||
peer = &peerInfo{
|
||||
td: td,
|
||||
currentBlock: currentBlock,
|
||||
currentBlockHash: currentBlockHash,
|
||||
id: peerId, //peer.Identity().Pubkey()
|
||||
requestBlockHashes: requestBlockHashes,
|
||||
requestBlocks: requestBlocks,
|
||||
peerError: peerError,
|
||||
sections: make(map[string]*section),
|
||||
currentBlockC: make(chan *types.Block),
|
||||
headSectionC: make(chan *section),
|
||||
}
|
||||
self.peers[peerId] = peer
|
||||
poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlock[:4])
|
||||
poolLogger.Debugf("add new peer %v with td %v and current block %x", peerId, td, currentBlockHash[:4])
|
||||
}
|
||||
// check peer current head
|
||||
if self.hasBlock(currentBlock) {
|
||||
if self.hasBlock(currentBlockHash) {
|
||||
// peer not ahead
|
||||
return false
|
||||
}
|
||||
|
@ -234,22 +249,135 @@ func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string,
|
|||
if self.peer == peer {
|
||||
// new block update
|
||||
// peer is already active best peer, request hashes
|
||||
poolLogger.Debugf("[%s] already the best peer. request hashes from %s", peerId, name(currentBlock))
|
||||
peer.requestBlockHashes(currentBlock)
|
||||
return true
|
||||
poolLogger.Debugf("[%s] already the best peer. Request new head section info from %s", peerId, name(currentBlockHash))
|
||||
peer.headSectionC <- nil
|
||||
best = true
|
||||
} else {
|
||||
currentTD := ethutil.Big0
|
||||
if self.peer != nil {
|
||||
currentTD = self.peer.td
|
||||
}
|
||||
if td.Cmp(currentTD) > 0 {
|
||||
poolLogger.Debugf("peer %v promoted best peer", peerId)
|
||||
self.switchPeer(self.peer, peer)
|
||||
self.peer = peer
|
||||
best = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
currentTD := ethutil.Big0
|
||||
if self.peer != nil {
|
||||
currentTD = self.peer.td
|
||||
}
|
||||
if td.Cmp(currentTD) > 0 {
|
||||
poolLogger.Debugf("peer %v promoted best peer", peerId)
|
||||
self.switchPeer(self.peer, peer)
|
||||
self.peer = peer
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func (self *BlockPool) requestHeadSection(peer *peerInfo) {
|
||||
self.wg.Add(1)
|
||||
self.procWg.Add(1)
|
||||
poolLogger.Debugf("[%s] head section at [%s] requesting info", peer.id, name(peer.currentBlockHash))
|
||||
|
||||
go func() {
|
||||
var idle bool
|
||||
peer.lock.RLock()
|
||||
quitC := peer.quitC
|
||||
currentBlockHash := peer.currentBlockHash
|
||||
peer.lock.RUnlock()
|
||||
blockHashesRequestTimer := time.NewTimer(0)
|
||||
blocksRequestTimer := time.NewTimer(0)
|
||||
suicide := time.NewTimer(blockHashesTimeout * time.Second)
|
||||
blockHashesRequestTimer.Stop()
|
||||
defer blockHashesRequestTimer.Stop()
|
||||
defer blocksRequestTimer.Stop()
|
||||
|
||||
entry := self.get(currentBlockHash)
|
||||
if entry != nil {
|
||||
entry.node.lock.RLock()
|
||||
currentBlock := entry.node.block
|
||||
entry.node.lock.RUnlock()
|
||||
if currentBlock != nil {
|
||||
peer.lock.Lock()
|
||||
peer.currentBlock = currentBlock
|
||||
peer.parentHash = currentBlock.ParentHash()
|
||||
poolLogger.Debugf("[%s] head block [%s] found", peer.id, name(currentBlockHash))
|
||||
peer.lock.Unlock()
|
||||
blockHashesRequestTimer.Reset(0)
|
||||
blocksRequestTimer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
|
||||
select {
|
||||
case <-self.quit:
|
||||
break LOOP
|
||||
|
||||
case <-quitC:
|
||||
poolLogger.Debugf("[%s] head section at [%s] incomplete - quit request loop", peer.id, name(currentBlockHash))
|
||||
break LOOP
|
||||
|
||||
case headSection := <-peer.headSectionC:
|
||||
peer.lock.Lock()
|
||||
peer.headSection = headSection
|
||||
if headSection == nil {
|
||||
oldBlockHash := currentBlockHash
|
||||
currentBlockHash = peer.currentBlockHash
|
||||
poolLogger.Debugf("[%s] head section changed [%s] -> [%s]", peer.id, name(oldBlockHash), name(currentBlockHash))
|
||||
if idle {
|
||||
idle = false
|
||||
suicide.Reset(blockHashesTimeout * time.Second)
|
||||
self.procWg.Add(1)
|
||||
}
|
||||
blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond)
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s] head section at [%s] created", peer.id, name(currentBlockHash))
|
||||
if !idle {
|
||||
idle = true
|
||||
suicide.Stop()
|
||||
self.procWg.Done()
|
||||
}
|
||||
}
|
||||
peer.lock.Unlock()
|
||||
blockHashesRequestTimer.Stop()
|
||||
|
||||
case <-blockHashesRequestTimer.C:
|
||||
poolLogger.DebugDetailf("[%s] head section at [%s] not found, requesting block hashes", peer.id, name(currentBlockHash))
|
||||
peer.requestBlockHashes(currentBlockHash)
|
||||
blockHashesRequestTimer.Reset(blockHashesRequestInterval * time.Millisecond)
|
||||
|
||||
case currentBlock := <-peer.currentBlockC:
|
||||
peer.lock.Lock()
|
||||
peer.currentBlock = currentBlock
|
||||
peer.parentHash = currentBlock.ParentHash()
|
||||
poolLogger.DebugDetailf("[%s] head block [%s] found", peer.id, name(currentBlockHash))
|
||||
peer.lock.Unlock()
|
||||
if self.hasBlock(currentBlock.ParentHash()) {
|
||||
if err := self.insertChain(types.Blocks([]*types.Block{currentBlock})); err != nil {
|
||||
peer.peerError(ErrInvalidBlock, "%v", err)
|
||||
}
|
||||
if !idle {
|
||||
idle = true
|
||||
suicide.Stop()
|
||||
self.procWg.Done()
|
||||
}
|
||||
} else {
|
||||
blockHashesRequestTimer.Reset(0)
|
||||
}
|
||||
blocksRequestTimer.Stop()
|
||||
|
||||
case <-blocksRequestTimer.C:
|
||||
peer.lock.RLock()
|
||||
poolLogger.DebugDetailf("[%s] head block [%s] not found, requesting", peer.id, name(currentBlockHash))
|
||||
peer.requestBlocks([][]byte{peer.currentBlockHash})
|
||||
peer.lock.RUnlock()
|
||||
blocksRequestTimer.Reset(blocksRequestInterval * time.Millisecond)
|
||||
|
||||
case <-suicide.C:
|
||||
peer.peerError(ErrInsufficientChainInfo, "peer failed to provide block hashes or head block for block hash %x", currentBlockHash)
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
self.wg.Done()
|
||||
if !idle {
|
||||
self.procWg.Done()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// RemovePeer is called by the eth protocol when the peer disconnects
|
||||
|
@ -274,13 +402,13 @@ func (self *BlockPool) RemovePeer(peerId string) {
|
|||
newPeer = info
|
||||
}
|
||||
}
|
||||
self.peer = newPeer
|
||||
self.switchPeer(peer, newPeer)
|
||||
if newPeer != nil {
|
||||
poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id, newPeer.td)
|
||||
} else {
|
||||
poolLogger.Warnln("no peers")
|
||||
}
|
||||
self.peer = newPeer
|
||||
self.switchPeer(peer, newPeer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,25 +427,56 @@ func (self *BlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string)
|
|||
return
|
||||
}
|
||||
// peer is still the best
|
||||
poolLogger.Debugf("adding hashes for best peer %s", peerId)
|
||||
|
||||
var size, n int
|
||||
var hash []byte
|
||||
var ok bool
|
||||
var section, child, parent *section
|
||||
var ok, headSection bool
|
||||
var sec, child, parent *section
|
||||
var entry *poolEntry
|
||||
var nodes []*poolNode
|
||||
bestPeer := peer
|
||||
|
||||
hash, ok = next()
|
||||
peer.lock.Lock()
|
||||
if bytes.Compare(peer.parentHash, hash) == 0 {
|
||||
if self.hasBlock(peer.currentBlockHash) {
|
||||
return
|
||||
}
|
||||
poolLogger.Debugf("adding hashes at chain head for best peer %s starting from [%s]", peerId, name(peer.currentBlockHash))
|
||||
headSection = true
|
||||
|
||||
if entry := self.get(peer.currentBlockHash); entry == nil {
|
||||
node := &poolNode{
|
||||
hash: peer.currentBlockHash,
|
||||
block: peer.currentBlock,
|
||||
peer: peerId,
|
||||
blockBy: peerId,
|
||||
}
|
||||
if size == 0 {
|
||||
sec = newSection()
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
size++
|
||||
n++
|
||||
} else {
|
||||
child = entry.section
|
||||
}
|
||||
} else {
|
||||
poolLogger.Debugf("adding hashes for best peer %s starting from [%s]", peerId, name(hash))
|
||||
}
|
||||
quitC := peer.quitC
|
||||
peer.lock.Unlock()
|
||||
|
||||
LOOP:
|
||||
// iterate using next (rlp stream lazy decoder) feeding hashesC
|
||||
for hash, ok = next(); ok; hash, ok = next() {
|
||||
for ; ok; hash, ok = next() {
|
||||
n++
|
||||
select {
|
||||
case <-self.quit:
|
||||
return
|
||||
case <-peer.quitC:
|
||||
case <-quitC:
|
||||
// if the peer is demoted, no more hashes taken
|
||||
peer = nil
|
||||
bestPeer = nil
|
||||
break LOOP
|
||||
default:
|
||||
}
|
||||
|
@ -325,8 +484,8 @@ LOOP:
|
|||
// check if known block connecting the downloaded chain to our blockchain
|
||||
poolLogger.DebugDetailf("[%s] known block", name(hash))
|
||||
// mark child as absolute pool root with parent known to blockchain
|
||||
if section != nil {
|
||||
self.connectToBlockChain(section)
|
||||
if sec != nil {
|
||||
self.connectToBlockChain(sec)
|
||||
} else {
|
||||
if child != nil {
|
||||
self.connectToBlockChain(child)
|
||||
|
@ -340,6 +499,7 @@ LOOP:
|
|||
// reached a known chain in the pool
|
||||
if entry.node == entry.section.bottom && n == 1 {
|
||||
// the first block hash received is an orphan in the pool, so rejoice and continue
|
||||
poolLogger.DebugDetailf("[%s] connecting child section", sectionName(entry.section))
|
||||
child = entry.section
|
||||
continue LOOP
|
||||
}
|
||||
|
@ -353,7 +513,7 @@ LOOP:
|
|||
peer: peerId,
|
||||
}
|
||||
if size == 0 {
|
||||
section = newSection()
|
||||
sec = newSection()
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
size++
|
||||
|
@ -379,10 +539,10 @@ LOOP:
|
|||
}
|
||||
|
||||
if size > 0 {
|
||||
self.processSection(section, nodes)
|
||||
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(section), size, sectionName(child))
|
||||
self.link(parent, section)
|
||||
self.link(section, child)
|
||||
self.processSection(sec, nodes)
|
||||
poolLogger.DebugDetailf("[%s]->[%s](%v)->[%s] new chain section", sectionName(parent), sectionName(sec), size, sectionName(child))
|
||||
self.link(parent, sec)
|
||||
self.link(sec, child)
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s]->[%s] connecting known sections", sectionName(parent), sectionName(child))
|
||||
self.link(parent, child)
|
||||
|
@ -390,15 +550,31 @@ LOOP:
|
|||
|
||||
self.chainLock.Unlock()
|
||||
|
||||
if parent != nil && peer != nil {
|
||||
if parent != nil && bestPeer != nil {
|
||||
self.activateChain(parent, peer)
|
||||
poolLogger.Debugf("[%s] activate parent section [%s]", name(parent.top.hash), sectionName(parent))
|
||||
}
|
||||
|
||||
if section != nil {
|
||||
peer.addSection(section.top.hash, section)
|
||||
section.controlC <- peer
|
||||
poolLogger.Debugf("[%s] activate new section", sectionName(section))
|
||||
if sec != nil {
|
||||
peer.addSection(sec.top.hash, sec)
|
||||
// request next section here once, only repeat if bottom block arrives,
|
||||
// otherwise no way to check if it arrived
|
||||
peer.requestBlockHashes(sec.bottom.hash)
|
||||
sec.controlC <- bestPeer
|
||||
poolLogger.Debugf("[%s] activate new section", sectionName(sec))
|
||||
}
|
||||
|
||||
if headSection {
|
||||
var headSec *section
|
||||
switch {
|
||||
case sec != nil:
|
||||
headSec = sec
|
||||
case child != nil:
|
||||
headSec = child
|
||||
default:
|
||||
headSec = parent
|
||||
}
|
||||
peer.headSectionC <- headSec
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -426,14 +602,21 @@ func sectionName(section *section) (name string) {
|
|||
// only the first PoW-valid block for a hash is considered legit
|
||||
func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
||||
hash := block.Hash()
|
||||
if self.hasBlock(hash) {
|
||||
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
||||
return
|
||||
}
|
||||
self.peersLock.Lock()
|
||||
peer := self.peer
|
||||
self.peersLock.Unlock()
|
||||
|
||||
entry := self.get(hash)
|
||||
if bytes.Compare(hash, peer.currentBlockHash) == 0 {
|
||||
poolLogger.Debugf("add head block [%s] for peer %s", name(hash), peerId)
|
||||
peer.currentBlockC <- block
|
||||
} else {
|
||||
if entry == nil {
|
||||
poolLogger.Warnf("unrequested block [%s] by peer %s", name(hash), peerId)
|
||||
self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
|
||||
}
|
||||
}
|
||||
if entry == nil {
|
||||
poolLogger.Warnf("unrequested block [%x] by peer %s", hash, peerId)
|
||||
self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -443,17 +626,21 @@ func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
|
|||
|
||||
// check if block already present
|
||||
if node.block != nil {
|
||||
poolLogger.DebugDetailf("block [%x] already sent by %s", name(hash), node.blockBy)
|
||||
poolLogger.DebugDetailf("block [%s] already sent by %s", name(hash), node.blockBy)
|
||||
return
|
||||
}
|
||||
|
||||
// validate block for PoW
|
||||
if !self.verifyPoW(block) {
|
||||
poolLogger.Warnf("invalid pow on block [%x] by peer %s", hash, peerId)
|
||||
self.peerError(peerId, ErrInvalidPoW, "%x", hash)
|
||||
return
|
||||
}
|
||||
if self.hasBlock(hash) {
|
||||
poolLogger.DebugDetailf("block [%s] already known", name(hash))
|
||||
} else {
|
||||
|
||||
// validate block for PoW
|
||||
if !self.verifyPoW(block) {
|
||||
poolLogger.Warnf("invalid pow on block [%s] by peer %s", name(hash), peerId)
|
||||
self.peerError(peerId, ErrInvalidPoW, "%x", hash)
|
||||
return
|
||||
}
|
||||
}
|
||||
poolLogger.Debugf("added block [%s] sent by peer %s", name(hash), peerId)
|
||||
node.block = block
|
||||
node.blockBy = peerId
|
||||
|
@ -544,23 +731,23 @@ LOOP:
|
|||
// - when turned off (if peer disconnects and new peer connects with alternative chain), no blockrequests are made but absolute expiry timer is ticking
|
||||
// - when turned back on it recursively calls itself on the root of the next chain section
|
||||
// - when exits, signals to
|
||||
func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
||||
func (self *BlockPool) processSection(sec *section, nodes []*poolNode) {
|
||||
|
||||
for i, node := range nodes {
|
||||
entry := &poolEntry{node: node, section: section, index: i}
|
||||
entry := &poolEntry{node: node, section: sec, index: i}
|
||||
self.set(node.hash, entry)
|
||||
}
|
||||
|
||||
section.bottom = nodes[len(nodes)-1]
|
||||
section.top = nodes[0]
|
||||
section.nodes = nodes
|
||||
poolLogger.DebugDetailf("[%s] setup section process", sectionName(section))
|
||||
sec.bottom = nodes[len(nodes)-1]
|
||||
sec.top = nodes[0]
|
||||
sec.nodes = nodes
|
||||
poolLogger.DebugDetailf("[%s] setup section process", sectionName(sec))
|
||||
|
||||
self.wg.Add(1)
|
||||
go func() {
|
||||
|
||||
// absolute time after which sub-chain is killed if not complete (some blocks are missing)
|
||||
suicideTimer := time.After(blockTimeout * time.Minute)
|
||||
suicideTimer := time.After(blocksTimeout * time.Second)
|
||||
|
||||
var peer, newPeer *peerInfo
|
||||
|
||||
|
@ -580,21 +767,23 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
var insertChain bool
|
||||
var quitC chan bool
|
||||
|
||||
var blockChainC = section.blockChainC
|
||||
var blockChainC = sec.blockChainC
|
||||
|
||||
var parentHash []byte
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
|
||||
if insertChain {
|
||||
insertChain = false
|
||||
rest, err := self.addSectionToBlockChain(section)
|
||||
rest, err := self.addSectionToBlockChain(sec)
|
||||
if err != nil {
|
||||
close(section.suicideC)
|
||||
close(sec.suicideC)
|
||||
continue LOOP
|
||||
}
|
||||
if rest == 0 {
|
||||
blocksRequestsComplete = true
|
||||
child := self.getChild(section)
|
||||
child := self.getChild(sec)
|
||||
if child != nil {
|
||||
self.connectToBlockChain(child)
|
||||
}
|
||||
|
@ -603,7 +792,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
|
||||
if blockHashesRequestsComplete && blocksRequestsComplete {
|
||||
// not waiting for hashes any more
|
||||
poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(section), depth, blocksRequests, blockHashesRequests)
|
||||
poolLogger.Debugf("[%s] section complete %v blocks retrieved (%v attempts), hash requests complete on root (%v attempts)", sectionName(sec), depth, blocksRequests, blockHashesRequests)
|
||||
break LOOP
|
||||
} // otherwise suicide if no hashes coming
|
||||
|
||||
|
@ -611,11 +800,12 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
// went through all blocks in section
|
||||
if missing == 0 {
|
||||
// no missing blocks
|
||||
poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] got all blocks. process complete (%v total blocksRequests): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
blocksRequestsComplete = true
|
||||
blocksRequestTimer = nil
|
||||
blocksRequestTime = false
|
||||
} else {
|
||||
poolLogger.DebugDetailf("[%s] section checked: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth)
|
||||
// some missing blocks
|
||||
blocksRequests++
|
||||
if len(hashes) > 0 {
|
||||
|
@ -630,8 +820,8 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
idle++
|
||||
// too many idle rounds
|
||||
if idle >= blocksRequestMaxIdleRounds {
|
||||
poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(section), idle, blocksRequests, missing, lastMissing, depth)
|
||||
close(section.suicideC)
|
||||
poolLogger.DebugDetailf("[%s] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", sectionName(sec), idle, blocksRequests, missing, lastMissing, depth)
|
||||
close(sec.suicideC)
|
||||
}
|
||||
} else {
|
||||
idle = 0
|
||||
|
@ -653,22 +843,39 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
//
|
||||
|
||||
if ready && blocksRequestTime && !blocksRequestsComplete {
|
||||
poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] check if new blocks arrived (attempt %v): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
blocksRequestTimer = time.After(blocksRequestInterval * time.Millisecond)
|
||||
blocksRequestTime = false
|
||||
processC = offC
|
||||
}
|
||||
|
||||
if blockHashesRequestTime {
|
||||
if self.getParent(section) != nil {
|
||||
var parentSection = self.getParent(sec)
|
||||
if parentSection == nil {
|
||||
if parent := self.get(parentHash); parent != nil {
|
||||
parentSection = parent.section
|
||||
self.chainLock.Lock()
|
||||
self.link(parentSection, sec)
|
||||
self.chainLock.Unlock()
|
||||
} else {
|
||||
if self.hasBlock(parentHash) {
|
||||
insertChain = true
|
||||
blockHashesRequestTime = false
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
if parentSection != nil {
|
||||
// if not root of chain, switch off
|
||||
poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(section), blockHashesRequests)
|
||||
poolLogger.DebugDetailf("[%s] parent found, hash requests deactivated (after %v total attempts)\n", sectionName(sec), blockHashesRequests)
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
} else {
|
||||
blockHashesRequests++
|
||||
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(section), blockHashesRequests)
|
||||
peer.requestBlockHashes(section.bottom.hash)
|
||||
poolLogger.Debugf("[%s] hash request on root (%v total attempts)\n", sectionName(sec), blockHashesRequests)
|
||||
peer.requestBlockHashes(sec.bottom.hash)
|
||||
blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Millisecond)
|
||||
}
|
||||
blockHashesRequestTime = false
|
||||
|
@ -682,27 +889,27 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
// peer quit or demoted, put section in idle mode
|
||||
quitC = nil
|
||||
go func() {
|
||||
section.controlC <- nil
|
||||
sec.controlC <- nil
|
||||
}()
|
||||
|
||||
case <-self.purgeC:
|
||||
suicideTimer = time.After(0)
|
||||
|
||||
case <-suicideTimer:
|
||||
close(section.suicideC)
|
||||
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
close(sec.suicideC)
|
||||
poolLogger.Debugf("[%s] timeout. (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
|
||||
case <-section.suicideC:
|
||||
poolLogger.Debugf("[%s] suicide", sectionName(section))
|
||||
case <-sec.suicideC:
|
||||
poolLogger.Debugf("[%s] suicide", sectionName(sec))
|
||||
|
||||
// first delink from child and parent under chainlock
|
||||
self.chainLock.Lock()
|
||||
self.link(nil, section)
|
||||
self.link(section, nil)
|
||||
self.link(nil, sec)
|
||||
self.link(sec, nil)
|
||||
self.chainLock.Unlock()
|
||||
// delete node entries from pool index under pool lock
|
||||
self.lock.Lock()
|
||||
for _, node := range section.nodes {
|
||||
for _, node := range sec.nodes {
|
||||
delete(self.pool, string(node.hash))
|
||||
}
|
||||
self.lock.Unlock()
|
||||
|
@ -710,20 +917,20 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
break LOOP
|
||||
|
||||
case <-blocksRequestTimer:
|
||||
poolLogger.DebugDetailf("[%s] block request time", sectionName(section))
|
||||
poolLogger.DebugDetailf("[%s] block request time", sectionName(sec))
|
||||
blocksRequestTime = true
|
||||
|
||||
case <-blockHashesRequestTimer:
|
||||
poolLogger.DebugDetailf("[%s] hash request time", sectionName(section))
|
||||
poolLogger.DebugDetailf("[%s] hash request time", sectionName(sec))
|
||||
blockHashesRequestTime = true
|
||||
|
||||
case newPeer = <-section.controlC:
|
||||
case newPeer = <-sec.controlC:
|
||||
|
||||
// active -> idle
|
||||
if peer != nil && newPeer == nil {
|
||||
self.procWg.Done()
|
||||
if init {
|
||||
poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(section), blocksRequests, missing, lastMissing, depth)
|
||||
poolLogger.Debugf("[%s] idle mode (%v total attempts): missing %v/%v/%v", sectionName(sec), blocksRequests, missing, lastMissing, depth)
|
||||
}
|
||||
blocksRequestTime = false
|
||||
blocksRequestTimer = nil
|
||||
|
@ -739,11 +946,11 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
if peer == nil && newPeer != nil {
|
||||
self.procWg.Add(1)
|
||||
|
||||
poolLogger.Debugf("[%s] active mode", sectionName(section))
|
||||
poolLogger.Debugf("[%s] active mode", sectionName(sec))
|
||||
if !blocksRequestsComplete {
|
||||
blocksRequestTime = true
|
||||
}
|
||||
if !blockHashesRequestsComplete {
|
||||
if !blockHashesRequestsComplete && parentHash != nil {
|
||||
blockHashesRequestTime = true
|
||||
}
|
||||
if !init {
|
||||
|
@ -753,13 +960,13 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
missing = 0
|
||||
self.wg.Add(1)
|
||||
self.procWg.Add(1)
|
||||
depth = len(section.nodes)
|
||||
depth = len(sec.nodes)
|
||||
lastMissing = depth
|
||||
// if not run at least once fully, launch iterator
|
||||
go func() {
|
||||
var node *poolNode
|
||||
IT:
|
||||
for _, node = range section.nodes {
|
||||
for _, node = range sec.nodes {
|
||||
select {
|
||||
case processC <- node:
|
||||
case <-self.quit:
|
||||
|
@ -771,7 +978,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
self.procWg.Done()
|
||||
}()
|
||||
} else {
|
||||
poolLogger.Debugf("[%s] restore earlier state", sectionName(section))
|
||||
poolLogger.Debugf("[%s] restore earlier state", sectionName(sec))
|
||||
processC = offC
|
||||
}
|
||||
}
|
||||
|
@ -781,7 +988,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
}
|
||||
peer = newPeer
|
||||
|
||||
case waiter := <-section.forkC:
|
||||
case waiter := <-sec.forkC:
|
||||
// this case just blocks the process until section is split at the fork
|
||||
<-waiter
|
||||
init = false
|
||||
|
@ -794,7 +1001,7 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
init = true
|
||||
done = true
|
||||
processC = make(chan *poolNode, missing)
|
||||
poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(section), missing, lastMissing, depth)
|
||||
poolLogger.DebugDetailf("[%s] section initalised: missing %v/%v/%v", sectionName(sec), missing, lastMissing, depth)
|
||||
continue LOOP
|
||||
}
|
||||
if ready {
|
||||
|
@ -811,17 +1018,24 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
missing++
|
||||
hashes = append(hashes, node.hash)
|
||||
if len(hashes) == blockBatchSize {
|
||||
poolLogger.Debugf("[%s] request %v missing blocks", sectionName(section), len(hashes))
|
||||
poolLogger.Debugf("[%s] request %v missing blocks", sectionName(sec), len(hashes))
|
||||
self.requestBlocks(blocksRequests, hashes)
|
||||
hashes = nil
|
||||
}
|
||||
missingC <- node
|
||||
} else {
|
||||
if blockChainC == nil && i == lastMissing {
|
||||
insertChain = true
|
||||
if i == lastMissing {
|
||||
if blockChainC == nil {
|
||||
insertChain = true
|
||||
} else {
|
||||
if parentHash == nil {
|
||||
parentHash = block.ParentHash()
|
||||
poolLogger.Debugf("[%s] found root block [%s]", sectionName(sec), name(parentHash))
|
||||
blockHashesRequestTime = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
poolLogger.Debugf("[%s] %v/%v/%v/%v", sectionName(section), i, missing, lastMissing, depth)
|
||||
if i == lastMissing && init {
|
||||
done = true
|
||||
}
|
||||
|
@ -829,23 +1043,22 @@ func (self *BlockPool) processSection(section *section, nodes []*poolNode) {
|
|||
case <-blockChainC:
|
||||
// closed blockChain channel indicates that the blockpool is reached
|
||||
// connected to the blockchain, insert the longest chain of blocks
|
||||
poolLogger.Debugf("[%s] reached blockchain", sectionName(section))
|
||||
poolLogger.Debugf("[%s] reached blockchain", sectionName(sec))
|
||||
blockChainC = nil
|
||||
// switch off hash requests in case they were on
|
||||
blockHashesRequestTime = false
|
||||
blockHashesRequestTimer = nil
|
||||
blockHashesRequestsComplete = true
|
||||
// section root has block
|
||||
if len(section.nodes) > 0 && section.nodes[len(section.nodes)-1].block != nil {
|
||||
if len(sec.nodes) > 0 && sec.nodes[len(sec.nodes)-1].block != nil {
|
||||
insertChain = true
|
||||
}
|
||||
continue LOOP
|
||||
|
||||
} // select
|
||||
} // for
|
||||
poolLogger.Debugf("[%s] section complete: %v block hashes requests - %v block requests - missing %v/%v/%v", sectionName(section), blockHashesRequests, blocksRequests, missing, lastMissing, depth)
|
||||
|
||||
close(section.offC)
|
||||
close(sec.offC)
|
||||
|
||||
self.wg.Done()
|
||||
if peer != nil {
|
||||
|
@ -917,22 +1130,28 @@ func (self *peerInfo) addSection(hash []byte, section *section) (found *section)
|
|||
defer self.lock.Unlock()
|
||||
key := string(hash)
|
||||
found = self.sections[key]
|
||||
poolLogger.DebugDetailf("[%s] section process %s registered", sectionName(section), self.id)
|
||||
poolLogger.DebugDetailf("[%s] section process stored for %s", sectionName(section), self.id)
|
||||
self.sections[key] = section
|
||||
return
|
||||
}
|
||||
|
||||
func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
||||
if newPeer != nil {
|
||||
entry := self.get(newPeer.currentBlock)
|
||||
if entry == nil {
|
||||
poolLogger.Debugf("[%s] head block [%s] not found, requesting hashes", newPeer.id, name(newPeer.currentBlock))
|
||||
newPeer.requestBlockHashes(newPeer.currentBlock)
|
||||
} else {
|
||||
poolLogger.Debugf("[%s] head block [%s] found, activate chain at section [%s]", newPeer.id, name(newPeer.currentBlock), sectionName(entry.section))
|
||||
self.activateChain(entry.section, newPeer)
|
||||
}
|
||||
newPeer.quitC = make(chan bool)
|
||||
poolLogger.DebugDetailf("[%s] activate section processes", newPeer.id)
|
||||
var addSections []*section
|
||||
for hash, section := range newPeer.sections {
|
||||
// split sections get reorganised here
|
||||
if string(section.top.hash) != hash {
|
||||
addSections = append(addSections, section)
|
||||
if entry := self.get([]byte(hash)); entry != nil {
|
||||
addSections = append(addSections, entry.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, section := range addSections {
|
||||
newPeer.sections[string(section.top.hash)] = section
|
||||
}
|
||||
for hash, section := range newPeer.sections {
|
||||
// this will block if section process is waiting for peer lock
|
||||
select {
|
||||
|
@ -940,12 +1159,26 @@ func (self *BlockPool) switchPeer(oldPeer, newPeer *peerInfo) {
|
|||
poolLogger.DebugDetailf("[%s][%x] section process complete - remove", newPeer.id, hash[:4])
|
||||
delete(newPeer.sections, hash)
|
||||
case section.controlC <- newPeer:
|
||||
poolLogger.DebugDetailf("[%s][%x] registered peer with section", newPeer.id, hash[:4])
|
||||
poolLogger.DebugDetailf("[%s][%x] activates section [%s]", newPeer.id, hash[:4], sectionName(section))
|
||||
}
|
||||
}
|
||||
newPeer.quitC = make(chan bool)
|
||||
newPeer.lock.Lock()
|
||||
headSection := newPeer.headSection
|
||||
currentBlockHash := newPeer.currentBlockHash
|
||||
newPeer.lock.Unlock()
|
||||
if headSection == nil {
|
||||
poolLogger.DebugDetailf("[%s] head section for [%s] not created, requesting info", newPeer.id, name(currentBlockHash))
|
||||
self.requestHeadSection(newPeer)
|
||||
} else {
|
||||
if entry := self.get(currentBlockHash); entry != nil {
|
||||
headSection = entry.section
|
||||
}
|
||||
poolLogger.DebugDetailf("[%s] activate chain at head section [%s] for current head [%s]", newPeer.id, sectionName(headSection), name(currentBlockHash))
|
||||
self.activateChain(headSection, newPeer)
|
||||
}
|
||||
}
|
||||
if oldPeer != nil {
|
||||
poolLogger.DebugDetailf("[%s] quit section processes", oldPeer.id)
|
||||
close(oldPeer.quitC)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
const waitTimeout = 60 // seconds
|
||||
|
||||
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugLevel))
|
||||
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
||||
|
||||
var ini = false
|
||||
|
||||
|
@ -336,12 +336,12 @@ func (self *peerTester) AddPeer() bool {
|
|||
|
||||
// peer sends blockhashes if and when gets a request
|
||||
func (self *peerTester) AddBlockHashes(indexes ...int) {
|
||||
i := 0
|
||||
fmt.Printf("ready to add block hashes %v\n", indexes)
|
||||
|
||||
self.waitBlockHashesRequests(indexes[0])
|
||||
fmt.Printf("adding block hashes %v\n", indexes)
|
||||
hashes := self.hashPool.indexesToHashes(indexes)
|
||||
i := 1
|
||||
next := func() (hash []byte, ok bool) {
|
||||
if i < len(hashes) {
|
||||
hash = hashes[i]
|
||||
|
@ -415,7 +415,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("peer0 (TD=1) not set as best")
|
||||
}
|
||||
peer0.checkBlockHashesRequests(0)
|
||||
// peer0.checkBlockHashesRequests(0)
|
||||
|
||||
best = peer2.AddPeer()
|
||||
if !best {
|
||||
|
@ -424,7 +424,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.id != "peer2" {
|
||||
t.Errorf("peer2 (TD=3) not set as best")
|
||||
}
|
||||
peer2.checkBlockHashesRequests(2)
|
||||
peer2.waitBlocksRequests(2)
|
||||
|
||||
best = peer1.AddPeer()
|
||||
if best {
|
||||
|
@ -449,7 +449,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.td.Cmp(big.NewInt(int64(4))) != 0 {
|
||||
t.Errorf("peer2 TD not updated")
|
||||
}
|
||||
peer2.checkBlockHashesRequests(2, 3)
|
||||
peer2.waitBlocksRequests(3)
|
||||
|
||||
peer1.td = 3
|
||||
peer1.currentBlock = 2
|
||||
|
@ -474,7 +474,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.id != "peer1" {
|
||||
t.Errorf("existing peer1 (TD=3) should be set as best peer")
|
||||
}
|
||||
peer1.checkBlockHashesRequests(2)
|
||||
peer1.waitBlocksRequests(2)
|
||||
|
||||
blockPool.RemovePeer("peer1")
|
||||
peer, best = blockPool.getPeer("peer1")
|
||||
|
@ -485,6 +485,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("existing peer0 (TD=1) should be set as best peer")
|
||||
}
|
||||
peer0.waitBlocksRequests(0)
|
||||
|
||||
blockPool.RemovePeer("peer0")
|
||||
peer, best = blockPool.getPeer("peer0")
|
||||
|
@ -502,7 +503,7 @@ func TestAddPeer(t *testing.T) {
|
|||
if blockPool.peer.id != "peer0" {
|
||||
t.Errorf("peer0 (TD=1) should be set as best")
|
||||
}
|
||||
peer0.checkBlockHashesRequests(0, 0, 3)
|
||||
peer0.waitBlocksRequests(3)
|
||||
|
||||
blockPool.Stop()
|
||||
|
||||
|
@ -513,17 +514,36 @@ func TestPeerWithKnownBlock(t *testing.T) {
|
|||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.refBlockChain[0] = nil
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
// hashPool, blockPool, blockPoolTester := newTestBlockPool()
|
||||
blockPool.Start()
|
||||
|
||||
peer0 := blockPoolTester.newPeer("0", 1, 0)
|
||||
peer0.AddPeer()
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
// no request on known block
|
||||
peer0.checkBlockHashesRequests()
|
||||
}
|
||||
|
||||
func TestPeerWithKnownParentBlock(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.initRefBlockChain(1)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPool.Start()
|
||||
|
||||
peer0 := blockPoolTester.newPeer("0", 1, 1)
|
||||
peer0.AddPeer()
|
||||
peer0.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
peer0.checkBlocksRequests([]int{1})
|
||||
peer0.checkBlockHashesRequests()
|
||||
blockPoolTester.refBlockChain[1] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestSimpleChain(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
|
@ -534,8 +554,9 @@ func TestSimpleChain(t *testing.T) {
|
|||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||
peer1.AddPeer()
|
||||
peer1.AddBlocks(1, 2)
|
||||
go peer1.AddBlockHashes(2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
|
@ -543,6 +564,26 @@ func TestSimpleChain(t *testing.T) {
|
|||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestChainConnectingWithParentHash(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(3)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[3] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestInvalidBlock(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
|
@ -554,8 +595,9 @@ func TestInvalidBlock(t *testing.T) {
|
|||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
|
@ -566,7 +608,7 @@ func TestInvalidBlock(t *testing.T) {
|
|||
t.Errorf("wrong error, got %v, expected %v", peer1.peerErrors[0], ErrInvalidBlock)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expected invalid block error, got nothing")
|
||||
t.Errorf("expected invalid block error, got nothing %v", peer1.peerErrors)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,7 +621,7 @@ func TestVerifyPoW(t *testing.T) {
|
|||
blockPoolTester.blockPool.verifyPoW = func(b pow.Block) bool {
|
||||
bb, _ := b.(*types.Block)
|
||||
indexes := blockPoolTester.hashPool.hashesToIndexes([][]byte{bb.Hash()})
|
||||
if indexes[0] == 1 && !first {
|
||||
if indexes[0] == 2 && !first {
|
||||
first = true
|
||||
return false
|
||||
} else {
|
||||
|
@ -590,15 +632,17 @@ func TestVerifyPoW(t *testing.T) {
|
|||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 2)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 3)
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(2, 1, 0)
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
// blockPool.Wait(waitTimeout * time.Second)
|
||||
time.Sleep(1 * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[2] = []int{}
|
||||
blockPoolTester.refBlockChain[1] = []int{}
|
||||
delete(blockPoolTester.refBlockChain, 2)
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
if len(peer1.peerErrors) == 1 {
|
||||
if peer1.peerErrors[0] != ErrInvalidPoW {
|
||||
|
@ -620,8 +664,9 @@ func TestMultiSectionChain(t *testing.T) {
|
|||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(4, 5)
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
go peer1.AddBlocks(2, 3, 4, 5)
|
||||
go peer1.AddBlocks(2, 3, 4)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0)
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
|
@ -641,14 +686,17 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
|||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(4, 5) // partially complete section
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
peer1.AddBlocks(2, 3) // partially complete section
|
||||
peer1.AddBlocks(3, 4) // partially complete section
|
||||
// peer1 found new blocks
|
||||
peer1.td = 2
|
||||
peer1.currentBlock = 7
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(6, 7)
|
||||
go peer1.AddBlockHashes(7, 6, 5)
|
||||
go peer1.AddBlocks(3, 4, 5, 6, 7)
|
||||
go peer1.AddBlocks(2, 3)
|
||||
go peer1.AddBlocks(5, 6)
|
||||
go peer1.AddBlockHashes(3, 2, 1, 0) // tests that hash request from known chain root is remembered
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
|
@ -658,35 +706,37 @@ func TestNewBlocksOnPartialChain(t *testing.T) {
|
|||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerSwitch(t *testing.T) {
|
||||
func TestPeerSwitchUp(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPoolTester.initRefBlockChain(7)
|
||||
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 5)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 6)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 7)
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(5, 4, 3)
|
||||
go peer1.AddBlocks(5, 6)
|
||||
go peer1.AddBlockHashes(6, 5, 4, 3) //
|
||||
peer1.AddBlocks(2, 3) // section partially complete, block 3 will be preserved after peer demoted
|
||||
peer2.AddPeer() // peer2 is promoted as best peer, peer1 is demoted
|
||||
go peer2.AddBlockHashes(6, 5) //
|
||||
go peer2.AddBlocks(4, 5, 6) // tests that block request for earlier section is remembered
|
||||
go peer2.AddBlocks(6, 7)
|
||||
go peer2.AddBlockHashes(7, 6) //
|
||||
go peer2.AddBlocks(4, 5) // tests that block request for earlier section is remembered
|
||||
go peer1.AddBlocks(3, 4) // tests that connecting section by demoted peer is remembered and blocks are accepted from demoted peer
|
||||
go peer2.AddBlockHashes(3, 2, 1, 0) // tests that known chain section is activated, hash requests from 3 is remembered
|
||||
peer2.AddBlocks(0, 1, 2) // final blocks linking to blockchain sent
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[6] = []int{}
|
||||
blockPoolTester.refBlockChain[7] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerDownSwitch(t *testing.T) {
|
||||
func TestPeerSwitchDown(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
|
@ -698,12 +748,39 @@ func TestPeerDownSwitch(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlockHashes(6, 5, 4)
|
||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
||||
go peer2.AddBlockHashes(6, 5, 4) //
|
||||
peer2.AddBlocks(4, 5) //
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
||||
go peer1.AddBlockHashes(4, 3, 2, 1, 0) //
|
||||
go peer1.AddBlocks(3, 4, 5) // tests that section set by demoted peer is remembered and blocks are accepted
|
||||
go peer1.AddBlocks(3, 4) // tests that section set by demoted peer is remembered and blocks are accepted , this connects the chain sections together
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
blockPoolTester.refBlockChain[6] = []int{}
|
||||
blockPoolTester.checkBlockChain(blockPoolTester.refBlockChain)
|
||||
}
|
||||
|
||||
func TestPeerCompleteSectionSwitchDown(t *testing.T) {
|
||||
logInit()
|
||||
_, blockPool, blockPoolTester := newTestBlockPool(t)
|
||||
blockPoolTester.blockChain[0] = nil
|
||||
blockPoolTester.initRefBlockChain(6)
|
||||
blockPool.Start()
|
||||
|
||||
peer1 := blockPoolTester.newPeer("peer1", 1, 4)
|
||||
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
|
||||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
peer2.AddBlocks(5, 6) // partially complete, section will be preserved
|
||||
go peer2.AddBlockHashes(6, 5, 4) //
|
||||
peer2.AddBlocks(3, 4, 5) // complete section
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects
|
||||
peer1.AddPeer() // inferior peer1 is promoted as best peer
|
||||
peer1.AddBlockHashes(4, 3, 2, 1, 0) // tests that hash request are directly connecting if the head block exists
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
|
@ -725,11 +802,13 @@ func TestPeerSwitchBack(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlocks(7, 8)
|
||||
go peer2.AddBlockHashes(8, 7, 6)
|
||||
go peer2.AddBlockHashes(6, 5, 4)
|
||||
peer2.AddBlocks(4, 5) // section partially complete
|
||||
peer1.AddPeer() // peer1 is promoted as best peer
|
||||
go peer1.AddBlockHashes(11, 10) // only gives useless results
|
||||
go peer1.AddBlocks(10, 11) //
|
||||
peer1.AddBlockHashes(11, 10) // only gives useless results
|
||||
blockPool.RemovePeer("peer1") // peer1 disconnects
|
||||
go peer2.AddBlockHashes(4, 3, 2, 1, 0) // tests that asking for hashes from 4 is remembered
|
||||
go peer2.AddBlocks(3, 4, 5, 6, 7, 8) // tests that section 4, 5, 6 and 7, 8 are remembered for missing blocks
|
||||
|
@ -756,11 +835,13 @@ func TestForkSimple(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(1, 2, 3, 7, 8, 9)
|
||||
peer1.AddBlocks(1, 2, 3, 7, 8)
|
||||
peer2.AddPeer() // peer2 is promoted as best peer
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // fork on 3 -> 4 (earlier child: 7)
|
||||
go peer2.AddBlocks(1, 2, 3, 4, 5, 6)
|
||||
go peer2.AddBlocks(1, 2, 3, 4, 5)
|
||||
go peer2.AddBlockHashes(2, 1, 0)
|
||||
peer2.AddBlocks(0, 1, 2)
|
||||
|
||||
|
@ -790,23 +871,24 @@ func TestForkSwitchBackByNewBlocks(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(8, 9) // partial section
|
||||
peer1.AddBlocks(8, 9) //
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2) //
|
||||
peer1.AddBlocks(7, 8) // partial section
|
||||
peer2.AddPeer() //
|
||||
peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(1, 2, 3, 4, 5, 6) //
|
||||
peer2.AddBlocks(1, 2, 3, 4, 5) //
|
||||
|
||||
// peer1 finds new blocks
|
||||
peer1.td = 3
|
||||
peer1.currentBlock = 11
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(10, 11)
|
||||
go peer1.AddBlockHashes(11, 10, 9)
|
||||
peer1.AddBlocks(7, 8, 9, 10, 11)
|
||||
go peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
||||
// go peer1.AddBlockHashes(1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
||||
peer1.AddBlocks(9, 10)
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered
|
||||
go peer1.AddBlockHashes(2, 1, 0) // tests that hash request from root of connecting chain section (added by demoted peer) is remembered
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
|
@ -834,16 +916,18 @@ func TestForkSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7, 3, 2)
|
||||
peer1.AddBlocks(8, 9)
|
||||
peer2.AddPeer() //
|
||||
peer1.AddBlocks(7, 8)
|
||||
peer2.AddPeer()
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(2, 3, 4, 5, 6) //
|
||||
peer2.AddBlocks(2, 3, 4, 5) //
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
||||
peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered
|
||||
go peer1.AddBlocks(3, 7, 8) // tests that block requests on earlier fork are remembered
|
||||
go peer1.AddBlocks(3, 7) // tests that block requests on earlier fork are remembered and orphan section relinks to existing parent block
|
||||
go peer1.AddBlocks(1, 2) //
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
peer1.AddBlocks(0, 1, 2, 3)
|
||||
peer1.AddBlocks(0, 1)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
blockPool.Stop()
|
||||
|
@ -871,17 +955,19 @@ func TestForkCompleteSectionSwitchBackByPeerSwitchBack(t *testing.T) {
|
|||
peer2.blocksRequestsMap = peer1.blocksRequestsMap
|
||||
|
||||
peer1.AddPeer()
|
||||
go peer1.AddBlocks(8, 9)
|
||||
go peer1.AddBlockHashes(9, 8, 7)
|
||||
peer1.AddBlocks(3, 7, 8, 9) // make sure this section is complete
|
||||
peer1.AddBlocks(3, 7, 8) // make sure this section is complete
|
||||
time.Sleep(1 * time.Second)
|
||||
go peer1.AddBlockHashes(7, 3, 2) // block 3/7 is section boundary
|
||||
peer1.AddBlocks(2, 3) // partially complete sections
|
||||
peer1.AddBlocks(2, 3) // partially complete sections block 2 missing
|
||||
peer2.AddPeer() //
|
||||
go peer2.AddBlocks(5, 6) //
|
||||
go peer2.AddBlockHashes(6, 5, 4, 3, 2) // peer2 forks on block 3
|
||||
peer2.AddBlocks(2, 3, 4, 5, 6) // block 2 still missing.
|
||||
peer2.AddBlocks(2, 3, 4, 5) // block 2 still missing.
|
||||
blockPool.RemovePeer("peer2") // peer2 disconnects, peer1 is promoted again as best peer
|
||||
peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
// peer1.AddBlockHashes(7, 3) // tests that hash request from fork root is remembered even though section process completed
|
||||
go peer1.AddBlockHashes(2, 1, 0) //
|
||||
peer1.AddBlocks(0, 1, 2)
|
||||
|
||||
blockPool.Wait(waitTimeout * time.Second)
|
||||
|
|
|
@ -16,6 +16,7 @@ const (
|
|||
ErrInvalidBlock
|
||||
ErrInvalidPoW
|
||||
ErrUnrequestedBlock
|
||||
ErrInsufficientChainInfo
|
||||
)
|
||||
|
||||
var errorToString = map[int]string{
|
||||
|
@ -30,6 +31,7 @@ var errorToString = map[int]string{
|
|||
ErrInvalidBlock: "Invalid block",
|
||||
ErrInvalidPoW: "Invalid PoW",
|
||||
ErrUnrequestedBlock: "Unrequested block",
|
||||
ErrInsufficientChainInfo: "Insufficient chain info",
|
||||
}
|
||||
|
||||
type protocolError struct {
|
||||
|
|
|
@ -67,6 +67,8 @@ type newBlockMsgData struct {
|
|||
TD *big.Int
|
||||
}
|
||||
|
||||
const maxHashes = 255
|
||||
|
||||
type getBlockHashesMsgData struct {
|
||||
Hash []byte
|
||||
Amount uint64
|
||||
|
@ -122,7 +124,7 @@ func (self *ethProtocol) handle() error {
|
|||
defer msg.Discard()
|
||||
|
||||
switch msg.Code {
|
||||
|
||||
case GetTxMsg: // ignore
|
||||
case StatusMsg:
|
||||
return self.protoError(ErrExtraStatusMsg, "")
|
||||
|
||||
|
@ -139,8 +141,13 @@ func (self *ethProtocol) handle() error {
|
|||
if err := msg.Decode(&request); err != nil {
|
||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||
}
|
||||
|
||||
//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
|
||||
if request.Amount > maxHashes {
|
||||
request.Amount = maxHashes
|
||||
}
|
||||
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
|
||||
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
|
||||
case BlockHashesMsg:
|
||||
// TODO: redo using lazy decode , this way very inefficient on known chains
|
||||
|
@ -185,7 +192,7 @@ func (self *ethProtocol) handle() error {
|
|||
break
|
||||
}
|
||||
}
|
||||
return self.rw.EncodeMsg(BlocksMsg, blocks...)
|
||||
return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...)
|
||||
|
||||
case BlocksMsg:
|
||||
msgStream := rlp.NewStream(msg.Payload)
|
||||
|
@ -211,16 +218,6 @@ func (self *ethProtocol) handle() error {
|
|||
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
|
||||
// (or selected as new best peer)
|
||||
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
||||
called := true
|
||||
iter := func() ([]byte, bool) {
|
||||
if called {
|
||||
called = false
|
||||
return hash, true
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
self.blockPool.AddBlockHashes(iter, self.id)
|
||||
self.blockPool.AddBlock(request.Block, self.id)
|
||||
}
|
||||
|
||||
|
@ -298,12 +295,12 @@ func (self *ethProtocol) handleStatus() error {
|
|||
|
||||
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
||||
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
|
||||
return self.rw.EncodeMsg(GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
|
||||
return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
|
||||
}
|
||||
|
||||
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
||||
self.peer.Debugf("fetching %v blocks", len(hashes))
|
||||
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
}
|
||||
|
||||
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
|
||||
|
|
|
@ -41,10 +41,6 @@ func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return self.WriteMsg(p2p.NewMsg(code, data...))
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||
msg, ok := <-self.in
|
||||
if !ok {
|
||||
|
|
|
@ -12,8 +12,8 @@ EOF
|
|||
|
||||
peer 11 01
|
||||
peer 12 02
|
||||
P13ID=$PID
|
||||
P12ID=$PID
|
||||
test_node $NAME "" -loglevel 5 $JSFILE
|
||||
sleep 0.5
|
||||
kill $P13ID
|
||||
sleep 0.3
|
||||
kill $P12ID
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
TIMEOUT=35
|
||||
TIMEOUT=12
|
||||
|
||||
cat >> $JSFILE <<EOF
|
||||
eth.addPeer("localhost:30311");
|
||||
sleep(30000);
|
||||
sleep(10000);
|
||||
eth.export("$CHAIN_TEST");
|
||||
EOF
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
# launched by run.sh
|
||||
function test_node {
|
||||
rm -rf $DIR/$1
|
||||
ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1"
|
||||
ARGS="-datadir $DIR/$1 -debug debug -seed=false -shh=false -id test$1 -port 303$1"
|
||||
if [ "" != "$2" ]; then
|
||||
chain="chains/$2.chain"
|
||||
echo "import chain $chain"
|
||||
$ETH $ARGS -loglevel 3 -chain $chain | grep CLI |grep import
|
||||
fi
|
||||
echo "starting test node $1 with extra args ${@:3}"
|
||||
$ETH $ARGS -port 303$1 ${@:3} &
|
||||
echo "starting test node $1 with args $ARGS ${@:3}"
|
||||
$ETH $ARGS ${@:3} &
|
||||
PID=$!
|
||||
PIDS="$PIDS $PID"
|
||||
}
|
||||
|
|
|
@ -84,4 +84,5 @@ var (
|
|||
BigFalse = Big0
|
||||
Big32 = big.NewInt(32)
|
||||
Big256 = big.NewInt(0xff)
|
||||
Big257 = big.NewInt(257)
|
||||
)
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
|
||||
// Config struct
|
||||
type ConfigManager struct {
|
||||
Db Database
|
||||
|
||||
ExecPath string
|
||||
Debug bool
|
||||
Diff bool
|
||||
|
|
|
@ -12,7 +12,7 @@ func ExpandHomePath(p string) (path string) {
|
|||
path = p
|
||||
|
||||
// Check in case of paths like "/something/~/something/"
|
||||
if path[:2] == "~/" {
|
||||
if len(path) > 1 && path[:2] == "~/" {
|
||||
usr, _ := user.Current()
|
||||
dir := usr.HomeDir
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ func Encode(object interface{}) []byte {
|
|||
case byte:
|
||||
buff.Write(Encode(big.NewInt(int64(t))))
|
||||
case *big.Int:
|
||||
// Not sure how this is possible while we check for
|
||||
// Not sure how this is possible while we check for nil
|
||||
if t == nil {
|
||||
buff.WriteByte(0xc0)
|
||||
} else {
|
||||
|
|
|
@ -68,3 +68,11 @@ out:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Filters) Match(a, b Filter) bool {
|
||||
return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b)
|
||||
}
|
||||
|
||||
func (self *Filters) Get(i int) Filter {
|
||||
return self.watchers[i]
|
||||
}
|
||||
|
|
|
@ -129,10 +129,9 @@ func (self *JSRE) initStdFuncs() {
|
|||
*/
|
||||
|
||||
func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
||||
var state *state.StateDB
|
||||
var block *types.Block
|
||||
|
||||
if len(call.ArgumentList) > 0 {
|
||||
var block *types.Block
|
||||
if call.Argument(0).IsNumber() {
|
||||
num, _ := call.Argument(0).ToInteger()
|
||||
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
|
||||
|
@ -149,12 +148,12 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
|
|||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
state = block.State()
|
||||
} else {
|
||||
state = self.ethereum.ChainManager().State()
|
||||
block = self.ethereum.ChainManager().CurrentBlock()
|
||||
}
|
||||
|
||||
v, _ := self.Vm.ToValue(state.Dump())
|
||||
statedb := state.New(block.Root(), self.ethereum.Db())
|
||||
v, _ := self.Vm.ToValue(statedb.Dump())
|
||||
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -69,6 +70,7 @@ type Miner struct {
|
|||
mining bool
|
||||
|
||||
MinAcceptedGasPrice *big.Int
|
||||
Extra string
|
||||
}
|
||||
|
||||
func New(coinbase []byte, eth *eth.Ethereum) *Miner {
|
||||
|
@ -177,7 +179,9 @@ func (self *Miner) mine() {
|
|||
blockProcessor = self.eth.BlockProcessor()
|
||||
chainMan = self.eth.ChainManager()
|
||||
block = chainMan.NewBlock(self.Coinbase)
|
||||
state = state.New(block.Root(), self.eth.Db())
|
||||
)
|
||||
block.Header().Extra = self.Extra
|
||||
|
||||
// Apply uncles
|
||||
if len(self.uncles) > 0 {
|
||||
|
@ -185,13 +189,11 @@ func (self *Miner) mine() {
|
|||
}
|
||||
|
||||
parent := chainMan.GetBlock(block.ParentHash())
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
||||
coinbase := state.GetOrNewStateObject(block.Coinbase())
|
||||
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||
|
||||
transactions := self.finiliseTxs()
|
||||
|
||||
state := block.State()
|
||||
|
||||
// Accumulate all valid transactions and apply them to the new state
|
||||
// Error may be ignored. It's not important during mining
|
||||
receipts, txs, _, erroneous, err := blockProcessor.ApplyTransactions(coinbase, state, block, transactions, true)
|
||||
|
|
|
@ -71,14 +71,11 @@ type MsgReader interface {
|
|||
}
|
||||
|
||||
type MsgWriter interface {
|
||||
// WriteMsg sends an existing message.
|
||||
// The Payload reader of the message is consumed.
|
||||
// WriteMsg sends a message. It will block until the message's
|
||||
// Payload has been consumed by the other end.
|
||||
//
|
||||
// Note that messages can be sent only once.
|
||||
WriteMsg(Msg) error
|
||||
|
||||
// EncodeMsg writes an RLP-encoded message with the given
|
||||
// code and data elements.
|
||||
EncodeMsg(code uint64, data ...interface{}) error
|
||||
}
|
||||
|
||||
// MsgReadWriter provides reading and writing of encoded messages.
|
||||
|
@ -87,6 +84,12 @@ type MsgReadWriter interface {
|
|||
MsgWriter
|
||||
}
|
||||
|
||||
// EncodeMsg writes an RLP-encoded message with the given code and
|
||||
// data elements.
|
||||
func EncodeMsg(w MsgWriter, code uint64, data ...interface{}) error {
|
||||
return w.WriteMsg(NewMsg(code, data...))
|
||||
}
|
||||
|
||||
var magicToken = []byte{34, 64, 8, 145}
|
||||
|
||||
func writeMsg(w io.Writer, msg Msg) error {
|
||||
|
@ -209,11 +212,6 @@ func (p *MsgPipeRW) WriteMsg(msg Msg) error {
|
|||
return ErrPipeClosed
|
||||
}
|
||||
|
||||
// EncodeMsg is a convenient shorthand for sending an RLP-encoded message.
|
||||
func (p *MsgPipeRW) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return p.WriteMsg(NewMsg(code, data...))
|
||||
}
|
||||
|
||||
// ReadMsg returns a message sent on the other end of the pipe.
|
||||
func (p *MsgPipeRW) ReadMsg() (Msg, error) {
|
||||
if atomic.LoadInt32(p.closed) == 0 {
|
||||
|
|
|
@ -75,8 +75,8 @@ func TestDecodeRealMsg(t *testing.T) {
|
|||
func ExampleMsgPipe() {
|
||||
rw1, rw2 := MsgPipe()
|
||||
go func() {
|
||||
rw1.EncodeMsg(8, []byte{0, 0})
|
||||
rw1.EncodeMsg(5, []byte{1, 1})
|
||||
EncodeMsg(rw1, 8, []byte{0, 0})
|
||||
EncodeMsg(rw1, 5, []byte{1, 1})
|
||||
rw1.Close()
|
||||
}()
|
||||
|
||||
|
@ -100,7 +100,7 @@ loop:
|
|||
rw1, rw2 := MsgPipe()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
if err := rw1.EncodeMsg(1); err == nil {
|
||||
if err := EncodeMsg(rw1, 1); err == nil {
|
||||
t.Error("EncodeMsg returned nil error")
|
||||
} else if err != ErrPipeClosed {
|
||||
t.Error("EncodeMsg returned wrong error: got %v, want %v", err, ErrPipeClosed)
|
||||
|
|
22
p2p/peer.go
22
p2p/peer.go
|
@ -460,25 +460,3 @@ func (r *eofSignal) Read(buf []byte) (int, error) {
|
|||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (peer *Peer) PeerList() []interface{} {
|
||||
peers := peer.otherPeers()
|
||||
ds := make([]interface{}, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
p.infolock.Lock()
|
||||
addr := p.listenAddr
|
||||
p.infolock.Unlock()
|
||||
// filter out this peer and peers that are not listening or
|
||||
// have not completed the handshake.
|
||||
// TODO: track previously sent peers and exclude them as well.
|
||||
if p == peer || addr == nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, addr)
|
||||
}
|
||||
ourAddr := peer.ourListenAddr
|
||||
if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() {
|
||||
ds = append(ds, ourAddr)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
|
|
@ -126,10 +126,10 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
|
|||
Name: "a",
|
||||
Length: 2,
|
||||
Run: func(peer *Peer, rw MsgReadWriter) error {
|
||||
if err := rw.EncodeMsg(2); err == nil {
|
||||
if err := EncodeMsg(rw, 2); err == nil {
|
||||
t.Error("expected error for out-of-range msg code, got nil")
|
||||
}
|
||||
if err := rw.EncodeMsg(1, "foo", "bar"); err != nil {
|
||||
if err := EncodeMsg(rw, 1, "foo", "bar"); err != nil {
|
||||
t.Errorf("write error: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -119,14 +119,14 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
|||
|
||||
getPeersTick := time.NewTicker(10 * time.Second)
|
||||
defer getPeersTick.Stop()
|
||||
err := bp.rw.EncodeMsg(getPeersMsg)
|
||||
err := EncodeMsg(bp.rw, getPeersMsg)
|
||||
|
||||
for err == nil {
|
||||
select {
|
||||
case err = <-quit:
|
||||
return err
|
||||
case <-getPeersTick.C:
|
||||
err = bp.rw.EncodeMsg(getPeersMsg)
|
||||
err = EncodeMsg(bp.rw, getPeersMsg)
|
||||
case event := <-activity.Chan():
|
||||
ping.Reset(pingTimeout)
|
||||
lastActive = event.(time.Time)
|
||||
|
@ -134,7 +134,7 @@ func (bp *baseProtocol) loop(quit <-chan error) error {
|
|||
if lastActive.Add(pingTimeout * 2).Before(t) {
|
||||
err = newPeerError(errPingTimeout, "")
|
||||
} else if lastActive.Add(pingTimeout).Before(t) {
|
||||
err = bp.rw.EncodeMsg(pingMsg)
|
||||
err = EncodeMsg(bp.rw, pingMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,12 +164,12 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error {
|
|||
return discRequestedError(reason[0])
|
||||
|
||||
case pingMsg:
|
||||
return bp.rw.EncodeMsg(pongMsg)
|
||||
return EncodeMsg(bp.rw, pongMsg)
|
||||
|
||||
case pongMsg:
|
||||
|
||||
case getPeersMsg:
|
||||
peers := bp.peer.PeerList()
|
||||
peers := bp.peerList()
|
||||
// this is dangerous. the spec says that we should _delay_
|
||||
// sending the response if no new information is available.
|
||||
// this means that would need to send a response later when
|
||||
|
@ -177,7 +177,7 @@ func (bp *baseProtocol) handle(rw MsgReadWriter) error {
|
|||
//
|
||||
// TODO: add event mechanism to notify baseProtocol for new peers
|
||||
if len(peers) > 0 {
|
||||
return bp.rw.EncodeMsg(peersMsg, peers...)
|
||||
return EncodeMsg(bp.rw, peersMsg, peers...)
|
||||
}
|
||||
|
||||
case peersMsg:
|
||||
|
@ -264,3 +264,25 @@ func (bp *baseProtocol) handshakeMsg() Msg {
|
|||
bp.peer.ourID.Pubkey()[1:],
|
||||
)
|
||||
}
|
||||
|
||||
func (bp *baseProtocol) peerList() []interface{} {
|
||||
peers := bp.peer.otherPeers()
|
||||
ds := make([]interface{}, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
p.infolock.Lock()
|
||||
addr := p.listenAddr
|
||||
p.infolock.Unlock()
|
||||
// filter out this peer and peers that are not listening or
|
||||
// have not completed the handshake.
|
||||
// TODO: track previously sent peers and exclude them as well.
|
||||
if p == bp.peer || addr == nil {
|
||||
continue
|
||||
}
|
||||
ds = append(ds, addr)
|
||||
}
|
||||
ourAddr := bp.peer.ourListenAddr
|
||||
if ourAddr != nil && !ourAddr.IP.IsLoopback() && !ourAddr.IP.IsUnspecified() {
|
||||
ds = append(ds, ourAddr)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
@ -36,50 +37,71 @@ func newTestPeer() (peer *Peer) {
|
|||
}
|
||||
|
||||
func TestBaseProtocolPeers(t *testing.T) {
|
||||
cannedPeerList := []*peerAddr{
|
||||
peerList := []*peerAddr{
|
||||
{IP: net.ParseIP("1.2.3.4"), Port: 2222, Pubkey: []byte{}},
|
||||
{IP: net.ParseIP("5.6.7.8"), Port: 3333, Pubkey: []byte{}},
|
||||
}
|
||||
var ownAddr *peerAddr = &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}}
|
||||
listenAddr := &peerAddr{IP: net.ParseIP("1.3.5.7"), Port: 1111, Pubkey: []byte{}}
|
||||
rw1, rw2 := MsgPipe()
|
||||
defer rw1.Close()
|
||||
wg := new(sync.WaitGroup)
|
||||
|
||||
// run matcher, close pipe when addresses have arrived
|
||||
addrChan := make(chan *peerAddr, len(cannedPeerList))
|
||||
numPeers := len(peerList) + 1
|
||||
addrChan := make(chan *peerAddr)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for _, want := range cannedPeerList {
|
||||
got := <-addrChan
|
||||
t.Logf("got peer: %+v", got)
|
||||
i := 0
|
||||
for got := range addrChan {
|
||||
var want *peerAddr
|
||||
switch {
|
||||
case i < len(peerList):
|
||||
want = peerList[i]
|
||||
case i == len(peerList):
|
||||
want = listenAddr // listenAddr should be the last thing sent
|
||||
}
|
||||
t.Logf("got peer %d/%d: %v", i+1, numPeers, got)
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("mismatch: got %#v, want %#v", got, want)
|
||||
t.Errorf("mismatch: got %+v, want %+v", got, want)
|
||||
}
|
||||
i++
|
||||
if i == numPeers {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(addrChan)
|
||||
var own []*peerAddr
|
||||
var got *peerAddr
|
||||
for got = range addrChan {
|
||||
own = append(own, got)
|
||||
if i != numPeers {
|
||||
t.Errorf("wrong number of peers received: got %d, want %d", i, numPeers)
|
||||
}
|
||||
if len(own) != 1 || !reflect.DeepEqual(ownAddr, own[0]) {
|
||||
t.Errorf("mismatch: peers own address is incorrectly or not given, got %v, want %#v", ownAddr)
|
||||
}
|
||||
rw2.Close()
|
||||
rw1.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
// run first peer
|
||||
|
||||
// run first peer (in background)
|
||||
peer1 := newTestPeer()
|
||||
peer1.ourListenAddr = ownAddr
|
||||
peer1.ourListenAddr = listenAddr
|
||||
peer1.otherPeers = func() []*Peer {
|
||||
pl := make([]*Peer, len(cannedPeerList))
|
||||
for i, addr := range cannedPeerList {
|
||||
pl := make([]*Peer, len(peerList))
|
||||
for i, addr := range peerList {
|
||||
pl[i] = &Peer{listenAddr: addr}
|
||||
}
|
||||
return pl
|
||||
}
|
||||
go runBaseProtocol(peer1, rw1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
runBaseProtocol(peer1, rw1)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// run second peer
|
||||
peer2 := newTestPeer()
|
||||
peer2.newPeerAddr = addrChan // feed peer suggestions into matcher
|
||||
if err := runBaseProtocol(peer2, rw2); err != ErrPipeClosed {
|
||||
t.Errorf("peer2 terminated with unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// terminate matcher
|
||||
close(addrChan)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBaseProtocolDisconnect(t *testing.T) {
|
||||
|
@ -93,7 +115,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
|||
if err := expectMsg(rw2, handshakeMsg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err := rw2.EncodeMsg(handshakeMsg,
|
||||
err := EncodeMsg(rw2, handshakeMsg,
|
||||
baseProtocolVersion,
|
||||
"",
|
||||
[]interface{}{},
|
||||
|
@ -106,7 +128,7 @@ func TestBaseProtocolDisconnect(t *testing.T) {
|
|||
if err := expectMsg(rw2, getPeersMsg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := rw2.EncodeMsg(discMsg, DiscQuitting); err != nil {
|
||||
if err := EncodeMsg(rw2, discMsg, DiscQuitting); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -333,7 +333,7 @@ func (srv *Server) dialLoop() {
|
|||
case desc := <-suggest:
|
||||
// candidate peer found, will dial out asyncronously
|
||||
// if connection fails slot will be released
|
||||
srvlog.Infof("dial %v (%v)", desc, *slot)
|
||||
srvlog.DebugDetailf("dial %v (%v)", desc, *slot)
|
||||
go srv.dialPeer(desc, *slot)
|
||||
// we can watch if more peers needed in the next loop
|
||||
slots = srv.peerSlots
|
||||
|
@ -355,7 +355,7 @@ func (srv *Server) dialPeer(desc *peerAddr, slot int) {
|
|||
srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot)
|
||||
conn, err := srv.Dialer.Dial(desc.Network(), desc.String())
|
||||
if err != nil {
|
||||
srvlog.Errorf("Dial error: %v", err)
|
||||
srvlog.DebugDetailf("dial error: %v", err)
|
||||
srv.peerSlots <- slot
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package ar
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type Block interface {
|
||||
Trie() *trie.Trie
|
||||
Diff() *big.Int
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package ar
|
||||
|
||||
import "math/big"
|
||||
|
||||
const lenops int64 = 9
|
||||
|
||||
type OpsFunc func(a, b *big.Int) *big.Int
|
||||
|
||||
var ops [lenops]OpsFunc
|
||||
|
||||
func init() {
|
||||
ops[0] = Add
|
||||
ops[1] = Mul
|
||||
ops[2] = Mod
|
||||
ops[3] = Xor
|
||||
ops[4] = And
|
||||
ops[5] = Or
|
||||
ops[6] = Sub1
|
||||
ops[7] = XorSub
|
||||
ops[8] = Rsh
|
||||
}
|
||||
|
||||
func Add(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Add(x, y)
|
||||
}
|
||||
func Mul(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Mul(x, y)
|
||||
}
|
||||
func Mod(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Mod(x, y)
|
||||
}
|
||||
func Xor(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Xor(x, y)
|
||||
}
|
||||
func And(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).And(x, y)
|
||||
}
|
||||
func Or(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Or(x, y)
|
||||
}
|
||||
func Sub1(x, y *big.Int) *big.Int {
|
||||
a := big.NewInt(-1)
|
||||
a.Sub(a, x)
|
||||
|
||||
return a
|
||||
}
|
||||
func XorSub(x, y *big.Int) *big.Int {
|
||||
t := Sub1(x, nil)
|
||||
|
||||
return t.Xor(t, y)
|
||||
}
|
||||
func Rsh(x, y *big.Int) *big.Int {
|
||||
return new(big.Int).Rsh(x, uint(y.Uint64()%64))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue