Merge branch 'release/poc5-rc1'
This commit is contained in:
commit
a77dcd1041
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,4 +9,5 @@
|
|||||||
*un~
|
*un~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/**/.DS_Store
|
*/**/.DS_Store
|
||||||
|
./ethereum/ethereum
|
||||||
|
|
||||||
|
55
README.md
55
README.md
@ -3,28 +3,37 @@ Ethereum
|
|||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
|
[![Build Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
|
||||||
|
|
||||||
Ethereum Go Client (c) Jeffrey Wilcke
|
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||||
|
|
||||||
The current state is "Proof of Concept 3.5".
|
Current state: Proof of Concept 5.0 RC1.
|
||||||
|
|
||||||
For the development Go Package please see [eth-go package](https://github.com/ethereum/eth-go).
|
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
|
||||||
|
|
||||||
Build
|
Build
|
||||||
=======
|
=======
|
||||||
|
|
||||||
For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go))
|
To build Ethereal (GUI):
|
||||||
Command line options
|
|
||||||
|
`go get github.com/ethereum/go-ethereum/ethereal`
|
||||||
|
|
||||||
|
To build the node (CLI):
|
||||||
|
|
||||||
|
`go get github.com/ethereum/go-ethereum/ethereum`
|
||||||
|
|
||||||
|
For further, detailed, build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go))
|
||||||
|
|
||||||
|
General command line options
|
||||||
====================
|
====================
|
||||||
|
|
||||||
```
|
```
|
||||||
-c Launch the developer console
|
-c Launch the developer console (node only)
|
||||||
-m Start mining blocks
|
-m Start mining blocks
|
||||||
-genaddr Generates a new address and private key (destructive action)
|
-genaddr Generates a new address and private key (destructive action)
|
||||||
-p Port on which the server will accept incomming connections (= 30303)
|
-p Port on which the server will accept incomming connections (= 30303)
|
||||||
-upnp Enable UPnP (= false)
|
-upnp Enable UPnP (= false)
|
||||||
-x Desired amount of peers (= 5)
|
-x Desired amount of peers (= 5)
|
||||||
-h This help
|
-h This help
|
||||||
-gui Launch with GUI (= true)
|
-r Start JSON RPC
|
||||||
-dir Data directory used to store configs and databases (=".ethereum")
|
-dir Data directory used to store configs and databases (=".ethereum")
|
||||||
-import Import a private key (hex)
|
-import Import a private key (hex)
|
||||||
```
|
```
|
||||||
@ -35,6 +44,7 @@ Developer console commands
|
|||||||
```
|
```
|
||||||
addp <host>:<port> Connect to the given host
|
addp <host>:<port> Connect to the given host
|
||||||
tx <addr> <amount> Send <amount> Wei to the specified <addr>
|
tx <addr> <amount> Send <amount> Wei to the specified <addr>
|
||||||
|
contract <value> <gasprice> Creates a new contract and launches the editor
|
||||||
```
|
```
|
||||||
|
|
||||||
See the "help" command for *developer* options.
|
See the "help" command for *developer* options.
|
||||||
@ -42,26 +52,24 @@ See the "help" command for *developer* options.
|
|||||||
Contribution
|
Contribution
|
||||||
============
|
============
|
||||||
|
|
||||||
If you'd like to contribute to Ethereum Go please fork, fix, commit and
|
If you would like to contribute to Ethereum Go, please fork, fix, commit and
|
||||||
send a pull request. Commits who do not comply with the coding standards
|
send a pull request to the main repository. Commits which do not comply with the coding standards explained below
|
||||||
are ignored. If you send pull requests make absolute sure that you
|
will be ignored. If you send a pull request, make sure that you
|
||||||
commit on the `develop` branch and that you do not merge to master.
|
commit to the `develop` branch and that you do not merge to `master`.
|
||||||
Commits that are directly based on master are simply ignored.
|
Commits that are directly based off of the `master` branch instead of the `develop` branch will be ignored.
|
||||||
|
|
||||||
To make life easier try [git flow](http://nvie.com/posts/a-successful-git-branching-model/) it sets
|
To make this process simpler try following the [git flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model, as it sets this process up and streamlines work flow.
|
||||||
this all up and streamlines your work flow.
|
|
||||||
|
|
||||||
Coding standards
|
Coding standards
|
||||||
================
|
================
|
||||||
|
|
||||||
Sources should be formatted according to the [Go Formatting
|
Code should be formatted according to the [Go Formatting
|
||||||
Style](http://golang.org/doc/effective_go.html#formatting).
|
Style](http://golang.org/doc/effective_go.html#formatting).
|
||||||
|
|
||||||
Unless structs fields are supposed to be directly accesible, provide
|
Unless struct fields are supposed to be directly accessible, provide
|
||||||
Getters and hide the fields through Go's exporting facility.
|
getters and hide the fields through Go's exporting facility.
|
||||||
|
|
||||||
When you comment put meaningfull comments. Describe in detail what you
|
Make comments in your code meaningful and only use them when necessary. Describe in detail what your code is trying to achieve. For example, this would be redundant and unnecessary commenting:
|
||||||
want to achieve.
|
|
||||||
|
|
||||||
*wrong*
|
*wrong*
|
||||||
|
|
||||||
@ -72,12 +80,7 @@ if x > y {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Everyone reading the source probably know what you wanted to achieve
|
Everyone reading the source code should know what this code snippet was meant to achieve, and so those are **not** meaningful comments.
|
||||||
with above code. Those are **not** meaningful comments.
|
|
||||||
|
|
||||||
While the project isn't 100% tested I want you to write tests non the
|
While this project is constantly tested and run, code tests should be written regardless. There is not time to evaluate every person's code specifically, so it is expected of you to write tests for the code so that it does not have to be tested manually. In fact, contributing by simply writing tests is perfectly fine!
|
||||||
less. I haven't got time to evaluate everyone's code in detail so I
|
|
||||||
expect you to write tests for me so I don't have to test your code
|
|
||||||
manually. (If you want to contribute by just writing tests that's fine
|
|
||||||
too!)
|
|
||||||
|
|
||||||
|
236
ethereal/assets/ethereum.js
Normal file
236
ethereal/assets/ethereum.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// Main Ethereum library
|
||||||
|
window.eth = {
|
||||||
|
prototype: Object(),
|
||||||
|
|
||||||
|
// 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(sec, recipient, value, gas, gasPrice, data, cb) {
|
||||||
|
postData({call: "transact", args: [sec, recipient, value, gas, gasPrice, data]}, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
create: function(sec, value, gas, gasPrice, init, body, cb) {
|
||||||
|
postData({call: "create", args: [sec, value, gas, gasPrice, init, body]}, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
getStorageAt: function(address, storageAddress, cb) {
|
||||||
|
postData({call: "getStorage", args: [address, storageAddress]}, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
getKey: function(cb) {
|
||||||
|
postData({call: "getKey"}, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
getBalanceAt: function(address, cb) {
|
||||||
|
postData({call: "getBalance", 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]});
|
||||||
|
},
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
window.eth._callbacks = {}
|
||||||
|
window.eth._onCallbacks = {}
|
||||||
|
|
||||||
|
function hello() {
|
||||||
|
debug("hello")
|
||||||
|
window.dataTest = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug(/**/) {
|
||||||
|
var args = arguments;
|
||||||
|
var msg = ""
|
||||||
|
for(var i = 0; i < args.length; i++){
|
||||||
|
if(typeof args[i] === "object") {
|
||||||
|
msg += " " + JSON.stringify(args[i])
|
||||||
|
} else {
|
||||||
|
msg += args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postData({call:"debug", args:[msg]})
|
||||||
|
document.getElementById("debug").innerHTML += "<br>" + msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
||||||
|
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) {
|
||||||
|
// Figure out whether the returned data was an array
|
||||||
|
// array means multiple return arguments (multiple params)
|
||||||
|
if(data.data instanceof Array) {
|
||||||
|
cb.apply(this, data.data)
|
||||||
|
} else {
|
||||||
|
cb.call(this, data.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the "trigger" callback
|
||||||
|
delete eth._callbacks[ev._seed];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.eth._0 = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||||
|
String.prototype.pad = function(len) {
|
||||||
|
var bin = this.bin();
|
||||||
|
var l = bin.length;
|
||||||
|
if(l < 32) {
|
||||||
|
return eth._0.substr(0, 32 - bin.length) + bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.unpad = function() {
|
||||||
|
var i, l;
|
||||||
|
for(i = 0, l = this.length; i < l; i++) {
|
||||||
|
if(this[i] != "\0") {
|
||||||
|
return this.substr(i, this.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.substr(i, this.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.bin = function() {
|
||||||
|
if(this.substr(0, 2) == "0x") {
|
||||||
|
return this.hex2bin();
|
||||||
|
} else if(/^\d+$/.test(this)) {
|
||||||
|
return this.num2bin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we'll return the "String" object instead of an actual string
|
||||||
|
return this.substr(0, this.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.unbin = function() {
|
||||||
|
var i, l, o = '';
|
||||||
|
for(i = 0, l = this.length; i < l; i++) {
|
||||||
|
var n = this.charCodeAt(i).toString(16);
|
||||||
|
o += n.length < 2 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0x" + o;
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.hex2bin = function() {
|
||||||
|
bytes = []
|
||||||
|
|
||||||
|
for(var i=2; i< this.length-1; i+=2) {
|
||||||
|
bytes.push(parseInt(this.substr(i, 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.fromCharCode.apply(String, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.num2bin = function() {
|
||||||
|
return ("0x"+parseInt(this).toString(16)).bin()
|
||||||
|
}
|
||||||
|
|
272
ethereal/assets/muted/codemirror.css
Normal file
272
ethereal/assets/muted/codemirror.css
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/* BASICS */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
/* Set height, width, borders, and global font properties here */
|
||||||
|
font-family: monospace;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
/* Set scrolling behaviour here */
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PADDING */
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
padding: 4px 0; /* Vertical padding around content */
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
padding: 0 4px; /* Horizontal padding of content */
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||||
|
background-color: white; /* The little square between H and V scrollbars */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GUTTER */
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumbers {}
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
padding: 0 3px 0 5px;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: #999;
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CURSOR */
|
||||||
|
|
||||||
|
.CodeMirror div.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
}
|
||||||
|
/* Shown when moving in bi-directional text */
|
||||||
|
.CodeMirror div.CodeMirror-secondarycursor {
|
||||||
|
border-left: 1px solid silver;
|
||||||
|
}
|
||||||
|
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
|
||||||
|
width: auto;
|
||||||
|
border: 0;
|
||||||
|
background: #7e7;
|
||||||
|
}
|
||||||
|
/* Can style cursor different in overwrite (non-insert) mode */
|
||||||
|
div.CodeMirror-overwrite div.CodeMirror-cursor {}
|
||||||
|
|
||||||
|
.cm-tab { display: inline-block; }
|
||||||
|
|
||||||
|
.CodeMirror-ruler {
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEFAULT THEME */
|
||||||
|
|
||||||
|
.cm-s-default .cm-keyword {color: #708;}
|
||||||
|
.cm-s-default .cm-atom {color: #219;}
|
||||||
|
.cm-s-default .cm-number {color: #164;}
|
||||||
|
.cm-s-default .cm-def {color: #00f;}
|
||||||
|
.cm-s-default .cm-variable,
|
||||||
|
.cm-s-default .cm-punctuation,
|
||||||
|
.cm-s-default .cm-property,
|
||||||
|
.cm-s-default .cm-operator {}
|
||||||
|
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||||
|
.cm-s-default .cm-variable-3 {color: #085;}
|
||||||
|
.cm-s-default .cm-comment {color: #a50;}
|
||||||
|
.cm-s-default .cm-string {color: #a11;}
|
||||||
|
.cm-s-default .cm-string-2 {color: #f50;}
|
||||||
|
.cm-s-default .cm-meta {color: #555;}
|
||||||
|
.cm-s-default .cm-qualifier {color: #555;}
|
||||||
|
.cm-s-default .cm-builtin {color: #30a;}
|
||||||
|
.cm-s-default .cm-bracket {color: #997;}
|
||||||
|
.cm-s-default .cm-tag {color: #170;}
|
||||||
|
.cm-s-default .cm-attribute {color: #00c;}
|
||||||
|
.cm-s-default .cm-header {color: blue;}
|
||||||
|
.cm-s-default .cm-quote {color: #090;}
|
||||||
|
.cm-s-default .cm-hr {color: #999;}
|
||||||
|
.cm-s-default .cm-link {color: #00c;}
|
||||||
|
|
||||||
|
.cm-negative {color: #d44;}
|
||||||
|
.cm-positive {color: #292;}
|
||||||
|
.cm-header, .cm-strong {font-weight: bold;}
|
||||||
|
.cm-em {font-style: italic;}
|
||||||
|
.cm-link {text-decoration: underline;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-error {color: #f00;}
|
||||||
|
.cm-invalidchar {color: #f00;}
|
||||||
|
|
||||||
|
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
||||||
|
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||||
|
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||||
|
|
||||||
|
/* STOP */
|
||||||
|
|
||||||
|
/* The rest of this file contains styles related to the mechanics of
|
||||||
|
the editor. You probably shouldn't touch them. */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
line-height: 1;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||||
|
/* See overflow: hidden in .CodeMirror */
|
||||||
|
margin-bottom: -30px; margin-right: -30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100%;
|
||||||
|
outline: none; /* Prevent dragging from highlighting the element */
|
||||||
|
position: relative;
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
.CodeMirror-sizer {
|
||||||
|
position: relative;
|
||||||
|
border-right: 30px solid transparent;
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||||
|
before actuall scrolling happens, thus preventing shaking and
|
||||||
|
flickering artifacts. */
|
||||||
|
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.CodeMirror-vscrollbar {
|
||||||
|
right: 0; top: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-hscrollbar {
|
||||||
|
bottom: 0; left: 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-scrollbar-filler {
|
||||||
|
right: 0; bottom: 0;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-filler {
|
||||||
|
left: 0; bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
position: absolute; left: 0; top: 0;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter {
|
||||||
|
white-space: normal;
|
||||||
|
height: 100%;
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
margin-bottom: -32px;
|
||||||
|
display: inline-block;
|
||||||
|
/* Hack to make IE7 behave */
|
||||||
|
*zoom:1;
|
||||||
|
*display:inline;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-elt {
|
||||||
|
position: absolute;
|
||||||
|
cursor: default;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
/* Reset some styles that the rest of the page might have set */
|
||||||
|
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||||
|
border-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
color: inherit;
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
.CodeMirror-wrap pre {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linebackground {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; right: 0; top: 0; bottom: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linewidget {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-widget {}
|
||||||
|
|
||||||
|
.CodeMirror-wrap .CodeMirror-scroll {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-measure {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.CodeMirror-measure pre { position: static; }
|
||||||
|
|
||||||
|
.CodeMirror div.CodeMirror-cursor {
|
||||||
|
position: absolute;
|
||||||
|
border-right: none;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.CodeMirror-focused div.CodeMirror-cursors {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-selected { background: #d9d9d9; }
|
||||||
|
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||||
|
.CodeMirror-crosshair { cursor: crosshair; }
|
||||||
|
|
||||||
|
.cm-searching {
|
||||||
|
background: #ffa;
|
||||||
|
background: rgba(255, 255, 0, .4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
||||||
|
.CodeMirror span { *vertical-align: text-bottom; }
|
||||||
|
|
||||||
|
/* Used to force a border model for a node */
|
||||||
|
.cm-force-border { padding-right: .1px; }
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Hide the cursor when printing */
|
||||||
|
.CodeMirror div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
53
ethereal/assets/muted/debugger.html
Normal file
53
ethereal/assets/muted/debugger.html
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
html, body {
|
||||||
|
margin: 0; padding: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debugger {
|
||||||
|
height: 100%;
|
||||||
|
font-family: "Monaco"
|
||||||
|
}
|
||||||
|
#debugger .line {
|
||||||
|
overflow: none;
|
||||||
|
}
|
||||||
|
#debugger .col1, #debugger .col2 {
|
||||||
|
float: left;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
#debugger .col1 {
|
||||||
|
width: 10px;
|
||||||
|
padding-left: 10px
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
#debugger .col2 {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.prompt {
|
||||||
|
color: "#5089D4";
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="debugger">
|
||||||
|
<div class="line">
|
||||||
|
<div class="col1 prompt">
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col2" contenteditable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
23
ethereal/assets/muted/eclipse.css
Normal file
23
ethereal/assets/muted/eclipse.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.cm-s-eclipse span.cm-meta {color: #FF1717;}
|
||||||
|
.cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; }
|
||||||
|
.cm-s-eclipse span.cm-atom {color: #219;}
|
||||||
|
.cm-s-eclipse span.cm-number {color: #164;}
|
||||||
|
.cm-s-eclipse span.cm-def {color: #00f;}
|
||||||
|
.cm-s-eclipse span.cm-variable {color: black;}
|
||||||
|
.cm-s-eclipse span.cm-variable-2 {color: #0000C0;}
|
||||||
|
.cm-s-eclipse span.cm-variable-3 {color: #0000C0;}
|
||||||
|
.cm-s-eclipse span.cm-property {color: black;}
|
||||||
|
.cm-s-eclipse span.cm-operator {color: black;}
|
||||||
|
.cm-s-eclipse span.cm-comment {color: #3F7F5F;}
|
||||||
|
.cm-s-eclipse span.cm-string {color: #2A00FF;}
|
||||||
|
.cm-s-eclipse span.cm-string-2 {color: #f50;}
|
||||||
|
.cm-s-eclipse span.cm-qualifier {color: #555;}
|
||||||
|
.cm-s-eclipse span.cm-builtin {color: #30a;}
|
||||||
|
.cm-s-eclipse span.cm-bracket {color: #cc7;}
|
||||||
|
.cm-s-eclipse span.cm-tag {color: #170;}
|
||||||
|
.cm-s-eclipse span.cm-attribute {color: #00c;}
|
||||||
|
.cm-s-eclipse span.cm-link {color: #219;}
|
||||||
|
.cm-s-eclipse span.cm-error {color: #f00;}
|
||||||
|
|
||||||
|
.cm-s-eclipse .CodeMirror-activeline-background {background: #e8f2ff !important;}
|
||||||
|
.cm-s-eclipse .CodeMirror-matchingbracket {outline:1px solid grey; color:black !important;}
|
80
ethereal/assets/muted/index.html
Normal file
80
ethereal/assets/muted/index.html
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Mutan Editor</title>
|
||||||
|
<link rel="stylesheet" href="codemirror.css">
|
||||||
|
<link rel="stylesheet" href="eclipse.css">
|
||||||
|
<script src="lib/codemirror.js"></script>
|
||||||
|
<script src="lib/matchbrackets.js"></script>
|
||||||
|
<script src="lib/go.js"></script>
|
||||||
|
<script src="muted.js"></script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
html, body {
|
||||||
|
margin: 0; padding: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debugger {
|
||||||
|
height: 30%;
|
||||||
|
font-family: "Monaco";
|
||||||
|
border-top: 5px solid grey;
|
||||||
|
}
|
||||||
|
#debugger .line {
|
||||||
|
overflow: none;
|
||||||
|
}
|
||||||
|
#debugger .col1, #debugger .col2 {
|
||||||
|
float: left;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
#debugger .col1 {
|
||||||
|
width: 10px;
|
||||||
|
padding-left: 10px
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
#debugger .col2 {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.prompt {
|
||||||
|
color: "#5089D4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
height: 70%;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<textarea id="editor"></textarea>
|
||||||
|
|
||||||
|
<div id="debugger">
|
||||||
|
<div class="line">
|
||||||
|
<div class="col1 prompt">
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col2" contenteditable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var textArea = document.querySelector("#editor")
|
||||||
|
var editor = CodeMirror.fromTextArea(textArea, {
|
||||||
|
theme: "eclipse",
|
||||||
|
mode: "text/html",
|
||||||
|
lineNumbers: true,
|
||||||
|
mode: "text/x-go",
|
||||||
|
indentUnit: 8,
|
||||||
|
tabSize: 8,
|
||||||
|
indentWithTabs: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
7526
ethereal/assets/muted/lib/codemirror.js
Normal file
7526
ethereal/assets/muted/lib/codemirror.js
Normal file
File diff suppressed because it is too large
Load Diff
182
ethereal/assets/muted/lib/go.js
Normal file
182
ethereal/assets/muted/lib/go.js
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
CodeMirror.defineMode("go", function(config) {
|
||||||
|
var indentUnit = config.indentUnit;
|
||||||
|
|
||||||
|
var keywords = {
|
||||||
|
"break":true, "case":true, "chan":true, "const":true, "continue":true,
|
||||||
|
"default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
|
||||||
|
"func":true, "go":true, "goto":true, "if":true, "import":true,
|
||||||
|
"interface":true, "map":true, "package":true, "range":true, "return":true,
|
||||||
|
"select":true, "struct":true, "switch":true, "type":true, "var":true,
|
||||||
|
"bool":true, "byte":true, "complex64":true, "complex128":true,
|
||||||
|
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
|
||||||
|
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
|
||||||
|
"uint64":true, "int":true, "uint":true, "uintptr":true, "big": true,
|
||||||
|
"main": true, "init": true, "this":true
|
||||||
|
};
|
||||||
|
|
||||||
|
var atoms = {
|
||||||
|
"true":true, "false":true, "iota":true, "nil":true, "append":true,
|
||||||
|
"cap":true, "close":true, "complex":true, "copy":true, "imag":true,
|
||||||
|
"len":true, "make":true, "new":true, "panic":true, "print":true,
|
||||||
|
"println":true, "real":true, "recover":true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
|
||||||
|
|
||||||
|
var curPunc;
|
||||||
|
|
||||||
|
function tokenBase(stream, state) {
|
||||||
|
var ch = stream.next();
|
||||||
|
if (ch == '"' || ch == "'" || ch == "`") {
|
||||||
|
state.tokenize = tokenString(ch);
|
||||||
|
return state.tokenize(stream, state);
|
||||||
|
}
|
||||||
|
if (/[\d\.]/.test(ch)) {
|
||||||
|
if (ch == ".") {
|
||||||
|
stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
|
||||||
|
} else if (ch == "0") {
|
||||||
|
stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
|
||||||
|
} else {
|
||||||
|
stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
|
||||||
|
}
|
||||||
|
return "number";
|
||||||
|
}
|
||||||
|
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||||
|
curPunc = ch;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (ch == "/") {
|
||||||
|
if (stream.eat("*")) {
|
||||||
|
state.tokenize = tokenComment;
|
||||||
|
return tokenComment(stream, state);
|
||||||
|
}
|
||||||
|
if (stream.eat("/")) {
|
||||||
|
stream.skipToEnd();
|
||||||
|
return "comment";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isOperatorChar.test(ch)) {
|
||||||
|
stream.eatWhile(isOperatorChar);
|
||||||
|
return "operator";
|
||||||
|
}
|
||||||
|
stream.eatWhile(/[\w\$_]/);
|
||||||
|
var cur = stream.current();
|
||||||
|
if (keywords.propertyIsEnumerable(cur)) {
|
||||||
|
if (cur == "case" || cur == "default") curPunc = "case";
|
||||||
|
return "keyword";
|
||||||
|
}
|
||||||
|
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
||||||
|
return "variable";
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenString(quote) {
|
||||||
|
return function(stream, state) {
|
||||||
|
var escaped = false, next, end = false;
|
||||||
|
while ((next = stream.next()) != null) {
|
||||||
|
if (next == quote && !escaped) {end = true; break;}
|
||||||
|
escaped = !escaped && next == "\\";
|
||||||
|
}
|
||||||
|
if (end || !(escaped || quote == "`"))
|
||||||
|
state.tokenize = tokenBase;
|
||||||
|
return "string";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenComment(stream, state) {
|
||||||
|
var maybeEnd = false, ch;
|
||||||
|
while (ch = stream.next()) {
|
||||||
|
if (ch == "/" && maybeEnd) {
|
||||||
|
state.tokenize = tokenBase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
maybeEnd = (ch == "*");
|
||||||
|
}
|
||||||
|
return "comment";
|
||||||
|
}
|
||||||
|
|
||||||
|
function Context(indented, column, type, align, prev) {
|
||||||
|
this.indented = indented;
|
||||||
|
this.column = column;
|
||||||
|
this.type = type;
|
||||||
|
this.align = align;
|
||||||
|
this.prev = prev;
|
||||||
|
}
|
||||||
|
function pushContext(state, col, type) {
|
||||||
|
return state.context = new Context(state.indented, col, type, null, state.context);
|
||||||
|
}
|
||||||
|
function popContext(state) {
|
||||||
|
var t = state.context.type;
|
||||||
|
if (t == ")" || t == "]" || t == "}")
|
||||||
|
state.indented = state.context.indented;
|
||||||
|
return state.context = state.context.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface
|
||||||
|
|
||||||
|
return {
|
||||||
|
startState: function(basecolumn) {
|
||||||
|
return {
|
||||||
|
tokenize: null,
|
||||||
|
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
||||||
|
indented: 0,
|
||||||
|
startOfLine: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
token: function(stream, state) {
|
||||||
|
var ctx = state.context;
|
||||||
|
if (stream.sol()) {
|
||||||
|
if (ctx.align == null) ctx.align = false;
|
||||||
|
state.indented = stream.indentation();
|
||||||
|
state.startOfLine = true;
|
||||||
|
if (ctx.type == "case") ctx.type = "}";
|
||||||
|
}
|
||||||
|
if (stream.eatSpace()) return null;
|
||||||
|
curPunc = null;
|
||||||
|
var style = (state.tokenize || tokenBase)(stream, state);
|
||||||
|
if (style == "comment") return style;
|
||||||
|
if (ctx.align == null) ctx.align = true;
|
||||||
|
|
||||||
|
if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||||
|
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||||
|
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||||
|
else if (curPunc == "case") ctx.type = "case";
|
||||||
|
else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
|
||||||
|
else if (curPunc == ctx.type) popContext(state);
|
||||||
|
state.startOfLine = false;
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
|
||||||
|
indent: function(state, textAfter) {
|
||||||
|
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
|
||||||
|
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
||||||
|
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
|
||||||
|
state.context.type = "}";
|
||||||
|
return ctx.indented;
|
||||||
|
}
|
||||||
|
var closing = firstChar == ctx.type;
|
||||||
|
if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
||||||
|
else return ctx.indented + (closing ? 0 : indentUnit);
|
||||||
|
},
|
||||||
|
|
||||||
|
electricChars: "{}):",
|
||||||
|
fold: "brace",
|
||||||
|
blockCommentStart: "/*",
|
||||||
|
blockCommentEnd: "*/",
|
||||||
|
lineComment: "//"
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineMIME("text/x-go", "go");
|
||||||
|
|
||||||
|
});
|
117
ethereal/assets/muted/lib/matchbrackets.js
Normal file
117
ethereal/assets/muted/lib/matchbrackets.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
||||||
|
(document.documentMode == null || document.documentMode < 8);
|
||||||
|
|
||||||
|
var Pos = CodeMirror.Pos;
|
||||||
|
|
||||||
|
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
||||||
|
|
||||||
|
function findMatchingBracket(cm, where, strict, config) {
|
||||||
|
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
|
||||||
|
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
||||||
|
if (!match) return null;
|
||||||
|
var dir = match.charAt(1) == ">" ? 1 : -1;
|
||||||
|
if (strict && (dir > 0) != (pos == where.ch)) return null;
|
||||||
|
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
|
||||||
|
|
||||||
|
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
|
||||||
|
if (found == null) return null;
|
||||||
|
return {from: Pos(where.line, pos), to: found && found.pos,
|
||||||
|
match: found && found.ch == match.charAt(0), forward: dir > 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
// bracketRegex is used to specify which type of bracket to scan
|
||||||
|
// should be a regexp, e.g. /[[\]]/
|
||||||
|
//
|
||||||
|
// Note: If "where" is on an open bracket, then this bracket is ignored.
|
||||||
|
//
|
||||||
|
// Returns false when no bracket was found, null when it reached
|
||||||
|
// maxScanLines and gave up
|
||||||
|
function scanForBracket(cm, where, dir, style, config) {
|
||||||
|
var maxScanLen = (config && config.maxScanLineLength) || 10000;
|
||||||
|
var maxScanLines = (config && config.maxScanLines) || 1000;
|
||||||
|
|
||||||
|
var stack = [];
|
||||||
|
var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
|
||||||
|
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
|
||||||
|
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
||||||
|
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
||||||
|
var line = cm.getLine(lineNo);
|
||||||
|
if (!line) continue;
|
||||||
|
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
||||||
|
if (line.length > maxScanLen) continue;
|
||||||
|
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
|
||||||
|
for (; pos != end; pos += dir) {
|
||||||
|
var ch = line.charAt(pos);
|
||||||
|
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
|
||||||
|
var match = matching[ch];
|
||||||
|
if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
|
||||||
|
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
|
||||||
|
else stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchBrackets(cm, autoclear, config) {
|
||||||
|
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
||||||
|
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
|
||||||
|
var marks = [], ranges = cm.listSelections();
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config);
|
||||||
|
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
|
||||||
|
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
||||||
|
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
|
||||||
|
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
|
||||||
|
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (marks.length) {
|
||||||
|
// Kludge to work around the IE bug from issue #1193, where text
|
||||||
|
// input stops going to the textare whever this fires.
|
||||||
|
if (ie_lt8 && cm.state.focused) cm.display.input.focus();
|
||||||
|
|
||||||
|
var clear = function() {
|
||||||
|
cm.operation(function() {
|
||||||
|
for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (autoclear) setTimeout(clear, 800);
|
||||||
|
else return clear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentlyHighlighted = null;
|
||||||
|
function doMatchBrackets(cm) {
|
||||||
|
cm.operation(function() {
|
||||||
|
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
|
||||||
|
currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
||||||
|
if (old && old != CodeMirror.Init)
|
||||||
|
cm.off("cursorActivity", doMatchBrackets);
|
||||||
|
if (val) {
|
||||||
|
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
||||||
|
cm.on("cursorActivity", doMatchBrackets);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
||||||
|
CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){
|
||||||
|
return findMatchingBracket(this, pos, strict, config);
|
||||||
|
});
|
||||||
|
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
|
||||||
|
return scanForBracket(this, pos, dir, style, config);
|
||||||
|
});
|
||||||
|
});
|
61
ethereal/assets/muted/muted.js
Normal file
61
ethereal/assets/muted/muted.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
||||||
|
function postData(data, cb) {
|
||||||
|
data._seed = Math.floor(Math.random() * 1000000)
|
||||||
|
if(cb) {
|
||||||
|
Muted._callbacks[data._seed] = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.args === undefined) {
|
||||||
|
data.args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.qt.postMessage(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Muted = {
|
||||||
|
prototype: Object(),
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Muted._callbacks = {}
|
||||||
|
window.Muted._onCallbacks = {}
|
||||||
|
|
||||||
|
function debug(/**/) {
|
||||||
|
console.log("hello world")
|
||||||
|
|
||||||
|
var args = arguments;
|
||||||
|
var msg = ""
|
||||||
|
for(var i = 0; i < args.length; i++){
|
||||||
|
if(typeof args[i] == "object") {
|
||||||
|
msg += " " + JSON.stringify(args[i])
|
||||||
|
} else {
|
||||||
|
msg += args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector("#debugger").innerHTML += "<div class='line'><div class='col1'></div><div class='col2'>"+msg+"</div></div>";
|
||||||
|
}
|
||||||
|
console.log = function() {
|
||||||
|
var args = []
|
||||||
|
for(var i = 0; i < arguments.length; i++) {
|
||||||
|
args.push(arguments[i]);
|
||||||
|
}
|
||||||
|
postData({call:"log", args:args})
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.qt.onmessage = function(ev) {
|
||||||
|
var data = JSON.parse(ev.data)
|
||||||
|
|
||||||
|
if(data._event !== undefined) {
|
||||||
|
Muted.trigger(data._event, data.data);
|
||||||
|
} else {
|
||||||
|
if(data._seed) {
|
||||||
|
var cb = Muted._callbacks[data._seed];
|
||||||
|
if(cb) {
|
||||||
|
// Call the callback
|
||||||
|
cb(data.data);
|
||||||
|
// Remove the "trigger" callback
|
||||||
|
delete Muted._callbacks[ev._seed];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
ethereal/assets/qml/first_run.qml
Normal file
155
ethereal/assets/qml/first_run.qml
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
// Which ones do we actually need?
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: wizardRoot
|
||||||
|
width: 500
|
||||||
|
height: 400
|
||||||
|
title: "Ethereal first run setup"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 5
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
Text {
|
||||||
|
visible: true
|
||||||
|
text: "<h2>Ethereal setup</h2>"
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: restoreColumn
|
||||||
|
spacing: 5
|
||||||
|
Text {
|
||||||
|
visible: true
|
||||||
|
font.pointSize: 14
|
||||||
|
text: "Restore your Ethereum account"
|
||||||
|
id: restoreLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: txPrivKey
|
||||||
|
width: 480
|
||||||
|
placeholderText: "Private key or mnemonic words"
|
||||||
|
focus: true
|
||||||
|
onTextChanged: {
|
||||||
|
if(this.text.length == 64){
|
||||||
|
detailLabel.text = "Private (hex) key detected."
|
||||||
|
actionButton.enabled = true
|
||||||
|
}
|
||||||
|
else if(this.text.split(" ").length == 24){
|
||||||
|
detailLabel.text = "Mnemonic key detected."
|
||||||
|
actionButton.enabled = true
|
||||||
|
}else{
|
||||||
|
detailLabel.text = ""
|
||||||
|
actionButton.enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
spacing: 10
|
||||||
|
Button {
|
||||||
|
id: actionButton
|
||||||
|
text: "Restore"
|
||||||
|
enabled: false
|
||||||
|
onClicked: {
|
||||||
|
var success = eth.importAndSetPrivKey(txPrivKey.text)
|
||||||
|
if(success){
|
||||||
|
importedDetails.visible = true
|
||||||
|
restoreColumn.visible = false
|
||||||
|
newKey.visible = false
|
||||||
|
wizardRoot.height = 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: detailLabel
|
||||||
|
font.pointSize: 12
|
||||||
|
anchors.topMargin: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
id: importedDetails
|
||||||
|
visible: false
|
||||||
|
Text {
|
||||||
|
text: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: 460
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
spacing: 5
|
||||||
|
id: newDetailsColumn
|
||||||
|
visible: false
|
||||||
|
Text {
|
||||||
|
font.pointSize: 14
|
||||||
|
text: "Your account details"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Address"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: addressInput
|
||||||
|
readOnly:true
|
||||||
|
width: 480
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Private key"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: privkeyInput
|
||||||
|
readOnly:true
|
||||||
|
width: 480
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Mnemonic words"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: mnemonicInput
|
||||||
|
readOnly:true
|
||||||
|
width: 480
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: 480
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Please restart the application once you have completed the steps above."
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: 480
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
id: newKey
|
||||||
|
text: "I don't have an account yet"
|
||||||
|
onClicked: {
|
||||||
|
var res = eth.createAndSetPrivKey()
|
||||||
|
mnemonicInput.text = res[0]
|
||||||
|
addressInput.text = res[1]
|
||||||
|
privkeyInput.text = res[2]
|
||||||
|
|
||||||
|
// Hide restore
|
||||||
|
restoreColumn.visible = false
|
||||||
|
|
||||||
|
// Show new details
|
||||||
|
newDetailsColumn.visible = true
|
||||||
|
newKey.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
ethereal/assets/qml/muted.qml
Normal file
74
ethereal/assets/qml/muted.qml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtWebKit 3.0
|
||||||
|
import QtWebKit.experimental 1.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: window
|
||||||
|
title: "muted"
|
||||||
|
width: 900
|
||||||
|
height: 600
|
||||||
|
minimumHeight: 300
|
||||||
|
|
||||||
|
property alias url: webView.url
|
||||||
|
property alias webView: webView
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
WebView {
|
||||||
|
objectName: "webView"
|
||||||
|
id: webView
|
||||||
|
anchors {
|
||||||
|
top: root.top
|
||||||
|
right: root.right
|
||||||
|
left: root.left
|
||||||
|
bottom: root.bottom
|
||||||
|
//bottom: sizeGrip.top
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental.preferences.javascriptEnabled: true
|
||||||
|
experimental.preferences.navigatorQtObjectEnabled: true
|
||||||
|
experimental.onMessageReceived: {
|
||||||
|
var data = JSON.parse(message.data)
|
||||||
|
|
||||||
|
switch(data.call) {
|
||||||
|
case "log":
|
||||||
|
console.log.apply(this, data.args)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function postData(seed, data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
|
||||||
|
}
|
||||||
|
function postEvent(event, data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Rectangle {
|
||||||
|
id: sizeGrip
|
||||||
|
color: "gray"
|
||||||
|
height: 5
|
||||||
|
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 - sizeGrip.height
|
||||||
|
drag.axis: Drag.YAxis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
196
ethereal/assets/qml/newTransaction/_new_contract.qml
Normal file
196
ethereal/assets/qml/newTransaction/_new_contract.qml
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: newContract
|
||||||
|
Column {
|
||||||
|
id: mainContractColumn
|
||||||
|
function contractFormReady(){
|
||||||
|
if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
|
||||||
|
txButton.state = "READY"
|
||||||
|
}else{
|
||||||
|
txButton.state = "NOTREADY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
states: [
|
||||||
|
State{
|
||||||
|
name: "ERROR"
|
||||||
|
PropertyChanges { target: txResult; visible:true}
|
||||||
|
PropertyChanges { target: codeView; visible:true}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "DONE"
|
||||||
|
PropertyChanges { target: txValue; visible:false}
|
||||||
|
PropertyChanges { target: txGas; visible:false}
|
||||||
|
PropertyChanges { target: txGasPrice; visible:false}
|
||||||
|
PropertyChanges { target: codeView; visible:false}
|
||||||
|
PropertyChanges { target: txButton; visible:false}
|
||||||
|
PropertyChanges { target: txDataLabel; visible:false}
|
||||||
|
|
||||||
|
PropertyChanges { target: txResult; visible:true}
|
||||||
|
PropertyChanges { target: txOutput; visible:true}
|
||||||
|
PropertyChanges { target: newTxButton; visible:true}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "SETUP"
|
||||||
|
PropertyChanges { target: txValue; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: txGas; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: txGasPrice; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: codeView; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: txButton; visible:true}
|
||||||
|
PropertyChanges { target: txDataLabel; visible:true}
|
||||||
|
|
||||||
|
PropertyChanges { target: txResult; visible:false}
|
||||||
|
PropertyChanges { target: txOutput; visible:false}
|
||||||
|
PropertyChanges { target: newTxButton; visible:false}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
width: 400
|
||||||
|
spacing: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: 5
|
||||||
|
anchors.topMargin: 5
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: txValue
|
||||||
|
width: 200
|
||||||
|
placeholderText: "Amount"
|
||||||
|
validator: IntValidator { }
|
||||||
|
onTextChanged: {
|
||||||
|
contractFormReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txGas
|
||||||
|
width: 200
|
||||||
|
validator: IntValidator { }
|
||||||
|
placeholderText: "Gas"
|
||||||
|
onTextChanged: {
|
||||||
|
contractFormReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txGasPrice
|
||||||
|
width: 200
|
||||||
|
placeholderText: "Gas price"
|
||||||
|
validator: IntValidator { }
|
||||||
|
onTextChanged: {
|
||||||
|
contractFormReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rowContract
|
||||||
|
ExclusiveGroup { id: contractTypeGroup }
|
||||||
|
RadioButton {
|
||||||
|
id: createContractRadio
|
||||||
|
text: "Create contract"
|
||||||
|
checked: true
|
||||||
|
exclusiveGroup: contractTypeGroup
|
||||||
|
onClicked: {
|
||||||
|
txFuelRecipient.visible = false
|
||||||
|
txDataLabel.text = "Contract code"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RadioButton {
|
||||||
|
id: runContractRadio
|
||||||
|
text: "Run contract"
|
||||||
|
exclusiveGroup: contractTypeGroup
|
||||||
|
onClicked: {
|
||||||
|
txFuelRecipient.visible = true
|
||||||
|
txDataLabel.text = "Contract arguments"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: txDataLabel
|
||||||
|
text: "Contract code"
|
||||||
|
}
|
||||||
|
|
||||||
|
TextArea {
|
||||||
|
id: codeView
|
||||||
|
height: 300
|
||||||
|
anchors.topMargin: 5
|
||||||
|
Layout.fillWidth: true
|
||||||
|
width: parent.width /2
|
||||||
|
onTextChanged: {
|
||||||
|
contractFormReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: txFuelRecipient
|
||||||
|
placeholderText: "Contract address"
|
||||||
|
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
|
||||||
|
visible: false
|
||||||
|
width: 530
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: txButton
|
||||||
|
/* enabled: false */
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "READY"
|
||||||
|
PropertyChanges { target: txButton; /*enabled: true*/}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "NOTREADY"
|
||||||
|
PropertyChanges { target: txButton; /*enabled:false*/}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
text: "Send"
|
||||||
|
onClicked: {
|
||||||
|
//this.enabled = false
|
||||||
|
var res = eth.createTx(txFuelRecipient.text, txValue.text, txGas.text, txGasPrice.text, codeView.text)
|
||||||
|
if(res[1]) {
|
||||||
|
txResult.text = "Your contract <b>could not</b> be send over the network:\n<b>"
|
||||||
|
txResult.text += res[1].error()
|
||||||
|
txResult.text += "</b>"
|
||||||
|
mainContractColumn.state = "ERROR"
|
||||||
|
} else {
|
||||||
|
txResult.text = "Your transaction has been submitted:\n"
|
||||||
|
txOutput.text = res[0]
|
||||||
|
mainContractColumn.state = "DONE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: txResult
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txOutput
|
||||||
|
visible: false
|
||||||
|
width: 530
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: newTxButton
|
||||||
|
visible: false
|
||||||
|
text: "Create an other contract"
|
||||||
|
onClicked: {
|
||||||
|
this.visible = false
|
||||||
|
txResult.text = ""
|
||||||
|
txOutput.text = ""
|
||||||
|
mainContractColumn.state = "SETUP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: debugButton
|
||||||
|
text: "Debug"
|
||||||
|
onClicked: {
|
||||||
|
var res = ui.debugTx("", txValue.text, txGas.text, txGasPrice.text, codeView.text)
|
||||||
|
debugWindow.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
ethereal/assets/qml/newTransaction/_simple_send.qml
Normal file
111
ethereal/assets/qml/newTransaction/_simple_send.qml
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: newTransaction
|
||||||
|
Column {
|
||||||
|
id: simpleSendColumn
|
||||||
|
states: [
|
||||||
|
State{
|
||||||
|
name: "ERROR"
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "DONE"
|
||||||
|
PropertyChanges { target: txSimpleValue; visible:false}
|
||||||
|
PropertyChanges { target: txSimpleRecipient; visible:false}
|
||||||
|
PropertyChanges { target:newSimpleTxButton; visible:false}
|
||||||
|
|
||||||
|
PropertyChanges { target: txSimpleResult; visible:true}
|
||||||
|
PropertyChanges { target: txSimpleOutput; visible:true}
|
||||||
|
PropertyChanges { target:newSimpleTxButton; visible:true}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "SETUP"
|
||||||
|
PropertyChanges { target: txSimpleValue; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: txSimpleRecipient; visible:true; text: ""}
|
||||||
|
PropertyChanges { target: txSimpleButton; visible:true}
|
||||||
|
PropertyChanges { target:newSimpleTxButton; visible:false}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
spacing: 5
|
||||||
|
anchors.leftMargin: 5
|
||||||
|
anchors.topMargin: 5
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
function checkFormState(){
|
||||||
|
if(txSimpleRecipient.text.length == 40 && txSimpleValue.text.length > 0) {
|
||||||
|
txSimpleButton.state = "READY"
|
||||||
|
}else{
|
||||||
|
txSimpleButton.state = "NOTREADY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: txSimpleRecipient
|
||||||
|
placeholderText: "Recipient address"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
|
||||||
|
width: 530
|
||||||
|
onTextChanged: { checkFormState() }
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txSimpleValue
|
||||||
|
placeholderText: "Amount"
|
||||||
|
anchors.rightMargin: 5
|
||||||
|
validator: IntValidator { }
|
||||||
|
onTextChanged: { checkFormState() }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: txSimpleButton
|
||||||
|
/*enabled: false*/
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "READY"
|
||||||
|
PropertyChanges { target: txSimpleButton; /*enabled: true*/}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "NOTREADY"
|
||||||
|
PropertyChanges { target: txSimpleButton; /*enabled: false*/}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
text: "Send"
|
||||||
|
onClicked: {
|
||||||
|
//this.enabled = false
|
||||||
|
var res = eth.createTx(txSimpleRecipient.text, txSimpleValue.text,"","","")
|
||||||
|
if(res[1]) {
|
||||||
|
txSimpleResult.text = "There has been an error broadcasting your transaction:" + res[1].error()
|
||||||
|
} else {
|
||||||
|
txSimpleResult.text = "Your transaction has been broadcasted over the network.\nYour transaction id is:"
|
||||||
|
txSimpleOutput.text = res[0]
|
||||||
|
this.visible = false
|
||||||
|
simpleSendColumn.state = "DONE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: txSimpleResult
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txSimpleOutput
|
||||||
|
visible: false
|
||||||
|
width: 530
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: newSimpleTxButton
|
||||||
|
visible: false
|
||||||
|
text: "Create an other transaction"
|
||||||
|
onClicked: {
|
||||||
|
this.visible = false
|
||||||
|
simpleSendColumn.state = "SETUP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,11 @@ ApplicationWindow {
|
|||||||
shortcut: "Ctrl+o"
|
shortcut: "Ctrl+o"
|
||||||
onTriggered: openAppDialog.open()
|
onTriggered: openAppDialog.open()
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: "Muted"
|
||||||
|
shortcut: "Ctrl+e"
|
||||||
|
onTriggered: ui.muted("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
@ -79,6 +84,7 @@ ApplicationWindow {
|
|||||||
//color: "#D9DDE7"
|
//color: "#D9DDE7"
|
||||||
color: "#252525"
|
color: "#252525"
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
y: 50
|
y: 50
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@ -139,6 +145,7 @@ ApplicationWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
|
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
|
||||||
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
|
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
|
||||||
|
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
|
||||||
|
|
||||||
model: txModel
|
model: txModel
|
||||||
}
|
}
|
||||||
@ -150,44 +157,25 @@ ApplicationWindow {
|
|||||||
visible: false
|
visible: false
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#00000000"
|
color: "#00000000"
|
||||||
|
TabView{
|
||||||
ColumnLayout {
|
anchors.fill: parent
|
||||||
width: 400
|
anchors.rightMargin: 5
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.leftMargin: 5
|
anchors.leftMargin: 5
|
||||||
anchors.topMargin: 5
|
anchors.topMargin: 5
|
||||||
TextField {
|
anchors.bottomMargin: 5
|
||||||
id: txAmount
|
id: newTransactionTab
|
||||||
width: 200
|
Component.onCompleted:{
|
||||||
placeholderText: "Amount"
|
var component = Qt.createComponent("newTransaction/_simple_send.qml")
|
||||||
}
|
var newTransaction = component.createObject("newTransaction")
|
||||||
|
|
||||||
TextField {
|
component = Qt.createComponent("newTransaction/_new_contract.qml")
|
||||||
id: txReceiver
|
var newContract = component.createObject("newContract")
|
||||||
placeholderText: "Receiver Address (or empty for contract)"
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
addTab("Simple send", newTransaction)
|
||||||
text: "Transaction data"
|
addTab("Contracts", newContract)
|
||||||
}
|
|
||||||
TextArea {
|
|
||||||
id: codeView
|
|
||||||
anchors.topMargin: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
width: parent.width /2
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Send"
|
|
||||||
onClicked: {
|
|
||||||
console.log(eth.createTx(txReceiver.text, txAmount.text, codeView.text))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: networkView
|
id: networkView
|
||||||
@ -257,7 +245,10 @@ ApplicationWindow {
|
|||||||
id: openAppDialog
|
id: openAppDialog
|
||||||
title: "Open QML Application"
|
title: "Open QML Application"
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
ui.open(openAppDialog.fileUrl.toString())
|
//ui.open(openAppDialog.fileUrl.toString())
|
||||||
|
//ui.openHtml(Qt.resolvedUrl(ui.assetPath("test.html")))
|
||||||
|
ui.openHtml(openAppDialog.fileUrl.toString())
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,11 +365,117 @@ ApplicationWindow {
|
|||||||
anchors.left: aboutIcon.right
|
anchors.left: aboutIcon.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
text: "<h2>Ethereum(Go)</h2><br><h3>Development</h3>Jeffrey Wilcke<br><h3>Binary Distribution</h3>Jarrad Hope<br>"
|
text: "<h2>Ethereal</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br><h3>Binary Distribution</h3>Jarrad Hope<br>"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Window {
|
||||||
|
id: debugWindow
|
||||||
|
visible: false
|
||||||
|
title: "Debugger"
|
||||||
|
minimumWidth: 600
|
||||||
|
minimumHeight: 600
|
||||||
|
width: 800
|
||||||
|
height: 600
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyHandler
|
||||||
|
focus: true
|
||||||
|
Keys.onPressed: {
|
||||||
|
if (event.key == Qt.Key_Space) {
|
||||||
|
ui.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SplitView {
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
property var asmModel: ListModel {
|
||||||
|
id: asmModel
|
||||||
|
}
|
||||||
|
TableView {
|
||||||
|
id: asmTableView
|
||||||
|
width: 200
|
||||||
|
TableViewColumn{ role: "value" ; title: "" ; width: 100 }
|
||||||
|
model: asmModel
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: asmTableView.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
property var memModel: ListModel {
|
||||||
|
id: memModel
|
||||||
|
}
|
||||||
|
height: parent.height/2
|
||||||
|
width: parent.width
|
||||||
|
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
|
||||||
|
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
|
||||||
|
model: memModel
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
TableView {
|
||||||
|
property var debuggerLog: ListModel {
|
||||||
|
id: debuggerLog
|
||||||
|
}
|
||||||
|
TableViewColumn{ role: "value"; title: "Debug messages" }
|
||||||
|
model: debuggerLog
|
||||||
|
}
|
||||||
|
TableView {
|
||||||
|
property var stackModel: ListModel {
|
||||||
|
id: stackModel
|
||||||
|
}
|
||||||
|
height: parent.height/2
|
||||||
|
width: parent.width
|
||||||
|
TableViewColumn{ role: "value" ; title: "Stack" ; width: parent.width }
|
||||||
|
model: stackModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAsm(asm) {
|
||||||
|
asmModel.append({asm: asm})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInstruction(num) {
|
||||||
|
asmTableView.selection.clear()
|
||||||
|
asmTableView.selection.select(num-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAsm() {
|
||||||
|
asmModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMem(mem) {
|
||||||
|
memModel.append({num: mem.num, value: mem.value})
|
||||||
|
}
|
||||||
|
function clearMem(){
|
||||||
|
memModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStack(stack) {
|
||||||
|
stackModel.append({value: stack})
|
||||||
|
}
|
||||||
|
function addDebugMessage(message){
|
||||||
|
console.log("WOOP:")
|
||||||
|
debuggerLog.append({value: message})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearStack() {
|
||||||
|
stackModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
function loadPlugin(name) {
|
function loadPlugin(name) {
|
||||||
console.log("Loading plugin" + name)
|
console.log("Loading plugin" + name)
|
||||||
mainView.addPlugin(name)
|
mainView.addPlugin(name)
|
||||||
@ -389,7 +486,13 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addTx(tx) {
|
function addTx(tx) {
|
||||||
txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value})
|
var isContract
|
||||||
|
if (tx.contract == true){
|
||||||
|
isContract = "Yes"
|
||||||
|
}else{
|
||||||
|
isContract = "No"
|
||||||
|
}
|
||||||
|
txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value, contract: isContract})
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBlock(block) {
|
function addBlock(block) {
|
||||||
|
184
ethereal/assets/qml/webapp.qml
Normal file
184
ethereal/assets/qml/webapp.qml
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtWebKit 3.0
|
||||||
|
import QtWebKit.experimental 1.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: window
|
||||||
|
title: "Ethereum"
|
||||||
|
width: 900
|
||||||
|
height: 600
|
||||||
|
minimumHeight: 300
|
||||||
|
|
||||||
|
property alias url: webview.url
|
||||||
|
property alias webView: webview
|
||||||
|
|
||||||
|
Item {
|
||||||
|
objectName: "root"
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
state: "inspectorShown"
|
||||||
|
|
||||||
|
WebView {
|
||||||
|
objectName: "webView"
|
||||||
|
id: webview
|
||||||
|
anchors.fill: parent
|
||||||
|
/*
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: sizeGrip.top
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
onTitleChanged: { window.title = title }
|
||||||
|
experimental.preferences.javascriptEnabled: true
|
||||||
|
experimental.preferences.navigatorQtObjectEnabled: true
|
||||||
|
experimental.preferences.developerExtrasEnabled: true
|
||||||
|
experimental.userScripts: [ui.assetPath("ethereum.js")]
|
||||||
|
experimental.onMessageReceived: {
|
||||||
|
console.log("[onMessageReceived]: ", message.data)
|
||||||
|
// TODO move to messaging.js
|
||||||
|
var data = JSON.parse(message.data)
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch(data.call) {
|
||||||
|
case "getBlockByNumber":
|
||||||
|
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
|
||||||
|
postData(data._seed, block)
|
||||||
|
break
|
||||||
|
case "getBlockByHash":
|
||||||
|
var block = eth.getBlock("b9b56cf6f907fbee21db0cd7cbc0e6fea2fe29503a3943e275c5e467d649cb06")
|
||||||
|
postData(data._seed, block)
|
||||||
|
break
|
||||||
|
case "transact":
|
||||||
|
require(5)
|
||||||
|
|
||||||
|
var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5])
|
||||||
|
postData(data._seed, tx)
|
||||||
|
|
||||||
|
break
|
||||||
|
case "create":
|
||||||
|
postData(data._seed, null)
|
||||||
|
|
||||||
|
break
|
||||||
|
case "getStorage":
|
||||||
|
require(2);
|
||||||
|
|
||||||
|
var stateObject = eth.getStateObject(data.args[0])
|
||||||
|
var storage = stateObject.getStorage(data.args[1])
|
||||||
|
postData(data._seed, storage)
|
||||||
|
|
||||||
|
break
|
||||||
|
case "getBalance":
|
||||||
|
require(1);
|
||||||
|
|
||||||
|
postData(data._seed, eth.getStateObject(data.args[0]).value());
|
||||||
|
|
||||||
|
break
|
||||||
|
case "getKey":
|
||||||
|
var key = eth.getKey().privateKey;
|
||||||
|
|
||||||
|
postData(data._seed, key)
|
||||||
|
break
|
||||||
|
case "watch":
|
||||||
|
require(1)
|
||||||
|
eth.watch(data.args[0], data.args[1]);
|
||||||
|
break
|
||||||
|
case "disconnect":
|
||||||
|
require(1)
|
||||||
|
postData(data._seed, null)
|
||||||
|
break;
|
||||||
|
case "set":
|
||||||
|
for(var key in data.args) {
|
||||||
|
if(webview.hasOwnProperty(key)) {
|
||||||
|
window[key] = data.args[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "getSecretToAddress":
|
||||||
|
require(1)
|
||||||
|
postData(data._seed, eth.secretToAddress(data.args[0]))
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
console.log(data.args[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.log(data.call + ": " + e)
|
||||||
|
|
||||||
|
postData(data._seed, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, _seed: seed}))
|
||||||
|
}
|
||||||
|
function postEvent(event, data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
url: webview.experimental.remoteInspectorUrl
|
||||||
|
anchors {
|
||||||
|
left: root.left
|
||||||
|
right: root.right
|
||||||
|
top: sizeGrip.bottom
|
||||||
|
bottom: root.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "inspectorShown"
|
||||||
|
PropertyChanges {
|
||||||
|
target: inspector
|
||||||
|
url: webview.experimental.remoteInspectorUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
7
ethereal/assets/samplecoin/bootstrap-theme.min.css
vendored
Executable file
7
ethereal/assets/samplecoin/bootstrap-theme.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
7
ethereal/assets/samplecoin/bootstrap.min.css
vendored
Executable file
7
ethereal/assets/samplecoin/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
BIN
ethereal/assets/samplecoin/icon.png
Normal file
BIN
ethereal/assets/samplecoin/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
34
ethereal/assets/samplecoin/samplecoin.css
Normal file
34
ethereal/assets/samplecoin/samplecoin.css
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* Space out content a bit */
|
||||||
|
body {
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everything but the jumbotron gets side spacing for mobile first
|
||||||
|
* views */
|
||||||
|
.header,
|
||||||
|
.marketing,
|
||||||
|
.footer {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom page header */
|
||||||
|
.header {
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
/* Make the masthead heading the same height as the navigation */
|
||||||
|
.header h3 {
|
||||||
|
padding-bottom: 19px;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jumbotron {
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 300px;
|
||||||
|
}
|
72
ethereal/assets/samplecoin/samplecoin.html
Normal file
72
ethereal/assets/samplecoin/samplecoin.html
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>jeffcoin</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="bootstrap-theme.min.css">
|
||||||
|
<link rel="stylesheet" href="samplecoin.css">
|
||||||
|
<script src="promise.min.js"></script>
|
||||||
|
<meta name="viewport" content="minimum-scale=1; maximum-scale=1; initial-scale=1;">
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var jefcoinAddr = "3dff537f51350239abc95c76a5864aa605259e7d"
|
||||||
|
var mAddr = ""
|
||||||
|
|
||||||
|
function createTransaction() {
|
||||||
|
var addr = document.querySelector("#addr").value;
|
||||||
|
var amount = document.querySelector("#amount").value;
|
||||||
|
|
||||||
|
var data = (("0x"+addr).pad(32) + amount.pad(32)).unbin()
|
||||||
|
eth.transact(mAddr, jefcoinAddr, 0, "10000000", "250", data, function(receipt) {
|
||||||
|
debug("received tx hash:", reciept.address)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
eth.set({width: 500});
|
||||||
|
|
||||||
|
eth.getKey(function(sec) {
|
||||||
|
eth.getSecretToAddress(sec, function(addr) {
|
||||||
|
mAddr = addr;
|
||||||
|
|
||||||
|
eth.getStorageAt(jefcoinAddr, addr, function(storage) {
|
||||||
|
document.querySelector("#current-amount").innerHTML = storage;
|
||||||
|
});
|
||||||
|
|
||||||
|
eth.watch(jefcoinAddr, addr, function(addr, value) {
|
||||||
|
document.querySelector("#current-amount").innerHTML = value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="init();">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h3 class="text-muted">JeffCoin</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="jumbotron ">
|
||||||
|
<img src="icon.png">
|
||||||
|
<div>Amount: <strong id="current-amount"></strong></div>
|
||||||
|
|
||||||
|
<div id="transactions">
|
||||||
|
<div class="form-group">
|
||||||
|
<input id="addr" class="form-control" type="text" placeholder="Receiver address"></input><br>
|
||||||
|
<input id="amount" class="form-control" type="text" placeholder="Amount"></input><br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-default" onclick="createTransaction();">Send Tx</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="debug" style="border: 1px solid black; min-height: 30px;"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
var StartConsole bool
|
var StartConsole bool
|
||||||
var StartMining bool
|
var StartMining bool
|
||||||
|
var StartRpc bool
|
||||||
var UseUPnP bool
|
var UseUPnP bool
|
||||||
var OutboundPort string
|
var OutboundPort string
|
||||||
var ShowGenesis bool
|
var ShowGenesis bool
|
||||||
@ -21,6 +22,7 @@ var AssetPath string
|
|||||||
func Init() {
|
func Init() {
|
||||||
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
|
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
|
||||||
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
|
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
|
||||||
|
flag.BoolVar(&StartRpc, "r", false, "start rpc server")
|
||||||
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
|
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/eth-go"
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
|
"github.com/ethereum/eth-go/ethrpc"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/ethereal/ui"
|
"github.com/ethereum/go-ethereum/ethereal/ui"
|
||||||
"github.com/ethereum/go-ethereum/utils"
|
"github.com/ethereum/go-ethereum/utils"
|
||||||
"github.com/niemeyer/qml"
|
"github.com/go-qml/qml"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -84,8 +86,6 @@ func main() {
|
|||||||
utils.ImportPrivateKey(ImportKey)
|
utils.ImportPrivateKey(ImportKey)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
utils.CreateKeyPair(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +100,11 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if StartRpc {
|
||||||
|
ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool()))
|
||||||
|
go ethereum.RpcServer.Start()
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
|
log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
|
||||||
|
|
||||||
// Set the max peers
|
// Set the max peers
|
||||||
|
126
ethereal/ui/ext_app.go
Normal file
126
ethereal/ui/ext_app.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package ethui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/go-qml/qml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppContainer interface {
|
||||||
|
Create() error
|
||||||
|
Destroy()
|
||||||
|
|
||||||
|
Window() *qml.Window
|
||||||
|
Engine() *qml.Engine
|
||||||
|
|
||||||
|
NewBlock(*ethchain.Block)
|
||||||
|
ObjectChanged(*ethchain.StateObject)
|
||||||
|
StorageChanged(*ethchain.StorageState)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtApplication struct {
|
||||||
|
*ethpub.PEthereum
|
||||||
|
|
||||||
|
blockChan chan ethutil.React
|
||||||
|
changeChan chan ethutil.React
|
||||||
|
quitChan chan bool
|
||||||
|
|
||||||
|
container AppContainer
|
||||||
|
lib *UiLib
|
||||||
|
registeredEvents []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
|
||||||
|
app := &ExtApplication{
|
||||||
|
ethpub.NewPEthereum(lib.eth.StateManager(), lib.eth.BlockChain(), lib.eth.TxPool()),
|
||||||
|
make(chan ethutil.React, 1),
|
||||||
|
make(chan ethutil.React, 1),
|
||||||
|
make(chan bool),
|
||||||
|
container,
|
||||||
|
lib,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) run() {
|
||||||
|
// Set the "eth" api on to the containers context
|
||||||
|
context := app.container.Engine().Context()
|
||||||
|
context.SetVar("eth", app)
|
||||||
|
context.SetVar("ui", app.lib)
|
||||||
|
|
||||||
|
err := app.container.Create()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the main loop
|
||||||
|
go app.mainLoop()
|
||||||
|
|
||||||
|
// Subscribe to events
|
||||||
|
reactor := app.lib.eth.Reactor()
|
||||||
|
reactor.Subscribe("newBlock", app.blockChan)
|
||||||
|
|
||||||
|
win := app.container.Window()
|
||||||
|
win.Show()
|
||||||
|
win.Wait()
|
||||||
|
|
||||||
|
app.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) stop() {
|
||||||
|
// Clean up
|
||||||
|
reactor := app.lib.eth.Reactor()
|
||||||
|
reactor.Unsubscribe("newBlock", app.blockChan)
|
||||||
|
for _, event := range app.registeredEvents {
|
||||||
|
reactor.Unsubscribe(event, app.changeChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill the main loop
|
||||||
|
app.quitChan <- true
|
||||||
|
|
||||||
|
close(app.blockChan)
|
||||||
|
close(app.quitChan)
|
||||||
|
close(app.changeChan)
|
||||||
|
|
||||||
|
app.container.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) mainLoop() {
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-app.quitChan:
|
||||||
|
break out
|
||||||
|
case block := <-app.blockChan:
|
||||||
|
if block, ok := block.Resource.(*ethchain.Block); ok {
|
||||||
|
app.container.NewBlock(block)
|
||||||
|
}
|
||||||
|
case object := <-app.changeChan:
|
||||||
|
if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
|
||||||
|
app.container.ObjectChanged(stateObject)
|
||||||
|
} else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok {
|
||||||
|
app.container.StorageChanged(storageObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) Watch(addr, storageAddr string) {
|
||||||
|
var event string
|
||||||
|
if len(storageAddr) == 0 {
|
||||||
|
event = "object:" + string(ethutil.FromHex(addr))
|
||||||
|
app.lib.eth.Reactor().Subscribe(event, app.changeChan)
|
||||||
|
} else {
|
||||||
|
event = "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
|
||||||
|
app.lib.eth.Reactor().Subscribe(event, app.changeChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registeredEvents = append(app.registeredEvents, event)
|
||||||
|
}
|
@ -2,42 +2,17 @@ package ethui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/eth-go"
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethdb"
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/niemeyer/qml"
|
"github.com/go-qml/qml"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Block interface exposed to QML
|
|
||||||
type Block struct {
|
|
||||||
Number int
|
|
||||||
Hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tx struct {
|
|
||||||
Value, Hash, Address string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTxFromTransaction(tx *ethchain.Transaction) *Tx {
|
|
||||||
hash := hex.EncodeToString(tx.Hash())
|
|
||||||
sender := hex.EncodeToString(tx.Recipient)
|
|
||||||
|
|
||||||
return &Tx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new QML Block from a chain block
|
|
||||||
func NewBlockFromBlock(block *ethchain.Block) *Block {
|
|
||||||
info := block.BlockInfo()
|
|
||||||
hash := hex.EncodeToString(block.Hash())
|
|
||||||
|
|
||||||
return &Block{Number: int(info.Number), Hash: hash}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
// The main application window
|
// The main application window
|
||||||
win *qml.Window
|
win *qml.Window
|
||||||
@ -53,7 +28,6 @@ type Gui struct {
|
|||||||
txDb *ethdb.LDBDatabase
|
txDb *ethdb.LDBDatabase
|
||||||
|
|
||||||
addr []byte
|
addr []byte
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GUI, but doesn't start it
|
// Create GUI, but doesn't start it
|
||||||
@ -64,10 +38,16 @@ func New(ethereum *eth.Ethereum) *Gui {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||||
|
// On first run we won't have any keys yet, so this would crash.
|
||||||
|
// Therefor we check if we are ready to actually start this process
|
||||||
|
var addr []byte
|
||||||
|
if len(data) > 0 {
|
||||||
key := ethutil.Config.Db.GetKeys()[0]
|
key := ethutil.Config.Db.GetKeys()[0]
|
||||||
addr := key.Address()
|
addr = key.Address()
|
||||||
|
|
||||||
ethereum.StateManager().WatchAddr(addr)
|
ethereum.StateManager().WatchAddr(addr)
|
||||||
|
}
|
||||||
|
|
||||||
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
|
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
|
||||||
}
|
}
|
||||||
@ -77,12 +57,12 @@ func (ui *Gui) Start(assetPath string) {
|
|||||||
|
|
||||||
// Register ethereum functions
|
// Register ethereum functions
|
||||||
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||||
Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
||||||
}, {
|
}, {
|
||||||
Init: func(p *Tx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
|
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
|
||||||
}})
|
}})
|
||||||
|
|
||||||
ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.1"))
|
ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.2"))
|
||||||
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
|
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
|
||||||
// Create a new QML engine
|
// Create a new QML engine
|
||||||
ui.engine = qml.NewEngine()
|
ui.engine = qml.NewEngine()
|
||||||
@ -94,14 +74,27 @@ func (ui *Gui) Start(assetPath string) {
|
|||||||
context.SetVar("ui", uiLib)
|
context.SetVar("ui", uiLib)
|
||||||
|
|
||||||
// Load the main QML interface
|
// Load the main QML interface
|
||||||
component, err := ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
|
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||||
|
var err error
|
||||||
|
var component qml.Object
|
||||||
|
firstRun := len(data) == 0
|
||||||
|
|
||||||
|
if firstRun {
|
||||||
|
component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml"))
|
||||||
|
} else {
|
||||||
|
component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
|
ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
|
||||||
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ui.engine.LoadFile(uiLib.AssetPath("qml/transactions.qml"))
|
|
||||||
|
|
||||||
ui.win = component.CreateWindow(nil)
|
ui.win = component.CreateWindow(nil)
|
||||||
|
uiLib.win = ui.win
|
||||||
|
db := &Debugger{ui.win, make(chan bool)}
|
||||||
|
ui.lib.Db = db
|
||||||
|
uiLib.Db = db
|
||||||
|
|
||||||
// Register the ui as a block processor
|
// Register the ui as a block processor
|
||||||
//ui.eth.BlockManager.SecondaryBlockProcessor = ui
|
//ui.eth.BlockManager.SecondaryBlockProcessor = ui
|
||||||
@ -111,9 +104,11 @@ func (ui *Gui) Start(assetPath string) {
|
|||||||
ethutil.Config.Log.AddLogSystem(ui)
|
ethutil.Config.Log.AddLogSystem(ui)
|
||||||
|
|
||||||
// Loads previous blocks
|
// Loads previous blocks
|
||||||
|
if firstRun == false {
|
||||||
go ui.setInitialBlockChain()
|
go ui.setInitialBlockChain()
|
||||||
go ui.readPreviousTransactions()
|
go ui.readPreviousTransactions()
|
||||||
go ui.update()
|
go ui.update()
|
||||||
|
}
|
||||||
|
|
||||||
ui.win.Show()
|
ui.win.Show()
|
||||||
ui.win.Wait()
|
ui.win.Wait()
|
||||||
@ -135,13 +130,13 @@ func (ui *Gui) readPreviousTransactions() {
|
|||||||
for it.Next() {
|
for it.Next() {
|
||||||
tx := ethchain.NewTransactionFromBytes(it.Value())
|
tx := ethchain.NewTransactionFromBytes(it.Value())
|
||||||
|
|
||||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
|
||||||
}
|
}
|
||||||
it.Release()
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *Gui) ProcessBlock(block *ethchain.Block) {
|
func (ui *Gui) ProcessBlock(block *ethchain.Block) {
|
||||||
ui.win.Root().Call("addBlock", NewBlockFromBlock(block))
|
ui.win.Root().Call("addBlock", ethpub.NewPBlock(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple go routine function that updates the list of peers in the GUI
|
// Simple go routine function that updates the list of peers in the GUI
|
||||||
@ -149,23 +144,26 @@ func (ui *Gui) update() {
|
|||||||
txChan := make(chan ethchain.TxMsg, 1)
|
txChan := make(chan ethchain.TxMsg, 1)
|
||||||
ui.eth.TxPool().Subscribe(txChan)
|
ui.eth.TxPool().Subscribe(txChan)
|
||||||
|
|
||||||
account := ui.eth.StateManager().GetAddrState(ui.addr).Account
|
account := ui.eth.StateManager().GetAddrState(ui.addr).Object
|
||||||
unconfirmedFunds := new(big.Int)
|
unconfirmedFunds := new(big.Int)
|
||||||
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
|
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
|
||||||
|
|
||||||
|
addrState := ui.eth.StateManager().GetAddrState(ui.addr)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case txMsg := <-txChan:
|
case txMsg := <-txChan:
|
||||||
tx := txMsg.Tx
|
tx := txMsg.Tx
|
||||||
|
|
||||||
if txMsg.Type == ethchain.TxPre {
|
if txMsg.Type == ethchain.TxPre {
|
||||||
if bytes.Compare(tx.Sender(), ui.addr) == 0 {
|
if bytes.Compare(tx.Sender(), ui.addr) == 0 && addrState.Nonce <= tx.Nonce {
|
||||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
|
||||||
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
|
|
||||||
ui.eth.StateManager().GetAddrState(ui.addr).Nonce += 1
|
addrState.Nonce += 1
|
||||||
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
|
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
|
||||||
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
|
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
|
||||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
|
||||||
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
|
|
||||||
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
|
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
|
||||||
|
73
ethereal/ui/html_container.go
Normal file
73
ethereal/ui/html_container.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package ethui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/go-qml/qml"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HtmlApplication struct {
|
||||||
|
win *qml.Window
|
||||||
|
webView qml.Object
|
||||||
|
engine *qml.Engine
|
||||||
|
lib *UiLib
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication {
|
||||||
|
engine := qml.NewEngine()
|
||||||
|
|
||||||
|
return &HtmlApplication{engine: engine, lib: lib, path: path}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) Create() error {
|
||||||
|
component, err := app.engine.LoadFile(app.lib.AssetPath("qml/webapp.qml"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Ext(app.path) == "eth" {
|
||||||
|
return errors.New("Ethereum package not yet supported")
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
ethutil.OpenPackage(app.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
win := component.CreateWindow(nil)
|
||||||
|
win.Set("url", app.path)
|
||||||
|
webView := win.ObjectByName("webView")
|
||||||
|
|
||||||
|
app.win = win
|
||||||
|
app.webView = webView
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) Engine() *qml.Engine {
|
||||||
|
return app.engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) Window() *qml.Window {
|
||||||
|
return app.win
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
|
||||||
|
b := ðpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
|
||||||
|
app.webView.Call("onNewBlockCb", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
|
||||||
|
app.webView.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) StorageChanged(storageObject *ethchain.StorageState) {
|
||||||
|
app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *HtmlApplication) Destroy() {
|
||||||
|
app.engine.Destroy()
|
||||||
|
}
|
@ -4,7 +4,10 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/utils"
|
||||||
|
"github.com/obscuren/secp256k1-go"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,49 +15,114 @@ type EthLib struct {
|
|||||||
stateManager *ethchain.StateManager
|
stateManager *ethchain.StateManager
|
||||||
blockChain *ethchain.BlockChain
|
blockChain *ethchain.BlockChain
|
||||||
txPool *ethchain.TxPool
|
txPool *ethchain.TxPool
|
||||||
|
Db *Debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lib *EthLib) CreateTx(receiver, a, data string) string {
|
func (lib *EthLib) ImportAndSetPrivKey(privKey string) bool {
|
||||||
|
fmt.Println(privKey)
|
||||||
|
mnemonic := strings.Split(privKey, " ")
|
||||||
|
if len(mnemonic) == 24 {
|
||||||
|
fmt.Println("Got mnemonic key, importing.")
|
||||||
|
key := ethutil.MnemonicDecode(mnemonic)
|
||||||
|
utils.ImportPrivateKey(key)
|
||||||
|
} else if len(mnemonic) == 1 {
|
||||||
|
fmt.Println("Got hex key, importing.")
|
||||||
|
utils.ImportPrivateKey(privKey)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Did not recognise format, exiting.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) {
|
||||||
|
pub, prv := secp256k1.GenerateKeyPair()
|
||||||
|
pair := ðutil.Key{PrivateKey: prv, PublicKey: pub}
|
||||||
|
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
||||||
|
mne := ethutil.MnemonicEncode(ethutil.Hex(prv))
|
||||||
|
mnemonicString := strings.Join(mne, " ")
|
||||||
|
return mnemonicString, fmt.Sprintf("%x", pair.Address()), fmt.Sprintf("%x", prv), fmt.Sprintf("%x", pub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) GetKey() string {
|
||||||
|
return ethutil.Hex(ethutil.Config.Db.GetKeys()[0].Address())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) GetStateObject(address string) *ethpub.PStateObject {
|
||||||
|
stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
|
||||||
|
if stateObject != nil {
|
||||||
|
return ethpub.NewPStateObject(stateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See GetStorage for explanation on "nil"
|
||||||
|
return ethpub.NewPStateObject(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) Watch(addr, storageAddr string) {
|
||||||
|
// lib.stateManager.Watch(ethutil.FromHex(addr), ethutil.FromHex(storageAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
|
||||||
|
return lib.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lib *EthLib) Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
|
||||||
var hash []byte
|
var hash []byte
|
||||||
if len(receiver) == 0 {
|
var contractCreation bool
|
||||||
hash = ethchain.ContractAddr
|
if len(recipient) == 0 {
|
||||||
|
contractCreation = true
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
hash, err = hex.DecodeString(receiver)
|
hash, err = hex.DecodeString(recipient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
k, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
keyPair := ethutil.Config.Db.GetKeys()[0]
|
||||||
keyPair := ethutil.NewKeyFromBytes(k)
|
value := ethutil.Big(valueStr)
|
||||||
|
gas := ethutil.Big(gasStr)
|
||||||
|
gasPrice := ethutil.Big(gasPriceStr)
|
||||||
|
var tx *ethchain.Transaction
|
||||||
|
// Compile and assemble the given data
|
||||||
|
if contractCreation {
|
||||||
|
// Compile script
|
||||||
|
mainScript, initScript, err := utils.CompileScript(dataStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
amount := ethutil.Big(a)
|
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
|
||||||
code := ethchain.Compile(strings.Split(data, "\n"))
|
} else {
|
||||||
tx := ethchain.NewTransaction(hash, amount, code)
|
lines := strings.Split(dataStr, "\n")
|
||||||
tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce
|
var data []byte
|
||||||
|
for _, line := range lines {
|
||||||
|
data = append(data, ethutil.BigToBytes(ethutil.Big(line), 256)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
|
||||||
|
}
|
||||||
|
acc := lib.stateManager.GetAddrState(keyPair.Address())
|
||||||
|
tx.Nonce = acc.Nonce
|
||||||
tx.Sign(keyPair.PrivateKey)
|
tx.Sign(keyPair.PrivateKey)
|
||||||
|
|
||||||
lib.txPool.QueueTransaction(tx)
|
lib.txPool.QueueTransaction(tx)
|
||||||
|
|
||||||
if len(receiver) == 0 {
|
if contractCreation {
|
||||||
ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
|
ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
|
||||||
} else {
|
} else {
|
||||||
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
|
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethutil.Hex(tx.Hash())
|
return ethutil.Hex(tx.Hash()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lib *EthLib) GetBlock(hexHash string) *Block {
|
func (lib *EthLib) GetBlock(hexHash string) *ethpub.PBlock {
|
||||||
hash, err := hex.DecodeString(hexHash)
|
hash, err := hex.DecodeString(hexHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
block := lib.blockChain.GetBlock(hash)
|
block := lib.blockChain.GetBlock(hash)
|
||||||
fmt.Println(block)
|
|
||||||
|
|
||||||
return &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
|
return ðpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,33 @@ package ethui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bitbucket.org/kardianos/osext"
|
"bitbucket.org/kardianos/osext"
|
||||||
|
"fmt"
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/eth-go"
|
||||||
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/niemeyer/qml"
|
"github.com/ethereum/go-ethereum/utils"
|
||||||
|
"github.com/go-qml/qml"
|
||||||
|
"github.com/obscuren/mutan"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type memAddr struct {
|
||||||
|
Num string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
// UI Library that has some basic functionality exposed
|
// UI Library that has some basic functionality exposed
|
||||||
type UiLib struct {
|
type UiLib struct {
|
||||||
engine *qml.Engine
|
engine *qml.Engine
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
connected bool
|
connected bool
|
||||||
assetPath string
|
assetPath string
|
||||||
|
// The main application window
|
||||||
|
win *qml.Window
|
||||||
|
Db *Debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||||
@ -40,6 +52,40 @@ func (ui *UiLib) Open(path string) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) OpenHtml(path string) {
|
||||||
|
container := NewHtmlApplication(path, ui)
|
||||||
|
app := NewExtApplication(container, ui)
|
||||||
|
|
||||||
|
go app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) Watch(addr, storageAddr string) {
|
||||||
|
if len(storageAddr) == 0 {
|
||||||
|
ui.eth.Reactor().Subscribe("storage:"+string(ethutil.FromHex(addr))+":"+string(ethutil.FromHex(storageAddr)), nil)
|
||||||
|
} else {
|
||||||
|
ui.eth.Reactor().Subscribe("object:"+string(ethutil.FromHex(addr)), nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) Muted(content string) {
|
||||||
|
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
|
||||||
|
if err != nil {
|
||||||
|
ethutil.Config.Log.Debugln(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
win := component.CreateWindow(nil)
|
||||||
|
go func() {
|
||||||
|
path := "file://" + ui.AssetPath("muted/index.html")
|
||||||
|
win.Set("url", path)
|
||||||
|
//debuggerPath := "file://" + ui.AssetPath("muted/debugger.html")
|
||||||
|
//win.Set("debugUrl", debuggerPath)
|
||||||
|
|
||||||
|
win.Show()
|
||||||
|
win.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
func (ui *UiLib) Connect(button qml.Object) {
|
func (ui *UiLib) Connect(button qml.Object) {
|
||||||
if !ui.connected {
|
if !ui.connected {
|
||||||
ui.eth.Start()
|
ui.eth.Start()
|
||||||
@ -58,7 +104,6 @@ func (ui *UiLib) AssetPath(p string) string {
|
|||||||
|
|
||||||
func DefaultAssetPath() string {
|
func DefaultAssetPath() string {
|
||||||
var base string
|
var base string
|
||||||
|
|
||||||
// If the current working directory is the go-ethereum dir
|
// If the current working directory is the go-ethereum dir
|
||||||
// assume a debug build and use the source directory as
|
// assume a debug build and use the source directory as
|
||||||
// asset directory.
|
// asset directory.
|
||||||
@ -82,3 +127,84 @@ func DefaultAssetPath() string {
|
|||||||
|
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) {
|
||||||
|
state := ui.eth.BlockChain().CurrentBlock.State()
|
||||||
|
|
||||||
|
mainInput, _ := mutan.PreProcess(data)
|
||||||
|
callerScript, err := utils.Compile(mainInput)
|
||||||
|
if err != nil {
|
||||||
|
ethutil.Config.Log.Debugln(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dis := ethchain.Disassemble(callerScript)
|
||||||
|
ui.win.Root().Call("clearAsm")
|
||||||
|
|
||||||
|
for _, str := range dis {
|
||||||
|
ui.win.Root().Call("setAsm", str)
|
||||||
|
}
|
||||||
|
callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), callerScript, nil)
|
||||||
|
|
||||||
|
// Contract addr as test address
|
||||||
|
keyPair := ethutil.Config.Db.GetKeys()[0]
|
||||||
|
account := ui.eth.StateManager().GetAddrState(keyPair.Address()).Object
|
||||||
|
c := ethchain.MakeContract(callerTx, state)
|
||||||
|
callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr), ethutil.Big(valueStr))
|
||||||
|
|
||||||
|
block := ui.eth.BlockChain().CurrentBlock
|
||||||
|
vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{
|
||||||
|
Origin: account.Address(),
|
||||||
|
BlockNumber: block.BlockInfo().Number,
|
||||||
|
PrevHash: block.PrevHash,
|
||||||
|
Coinbase: block.Coinbase,
|
||||||
|
Time: block.Time,
|
||||||
|
Diff: block.Difficulty,
|
||||||
|
TxData: nil,
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
callerClosure.Call(vm, nil, ui.Db.halting)
|
||||||
|
|
||||||
|
state.Reset()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) Next() {
|
||||||
|
ui.Db.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Debugger struct {
|
||||||
|
win *qml.Window
|
||||||
|
N chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack) {
|
||||||
|
d.win.Root().Call("setInstruction", pc)
|
||||||
|
d.win.Root().Call("clearMem")
|
||||||
|
d.win.Root().Call("clearStack")
|
||||||
|
|
||||||
|
addr := 0
|
||||||
|
for i := 0; i+32 <= mem.Len(); i += 32 {
|
||||||
|
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range stack.Data() {
|
||||||
|
d.win.Root().Call("setStack", val.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-d.N:
|
||||||
|
break out
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Debugger) Next() {
|
||||||
|
d.N <- true
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
var StartConsole bool
|
var StartConsole bool
|
||||||
var StartMining bool
|
var StartMining bool
|
||||||
|
var StartRpc bool
|
||||||
var UseUPnP bool
|
var UseUPnP bool
|
||||||
var OutboundPort string
|
var OutboundPort string
|
||||||
var ShowGenesis bool
|
var ShowGenesis bool
|
||||||
@ -15,20 +16,23 @@ var GenAddr bool
|
|||||||
var UseSeed bool
|
var UseSeed bool
|
||||||
var ImportKey string
|
var ImportKey string
|
||||||
var ExportKey bool
|
var ExportKey bool
|
||||||
|
var LogFile string
|
||||||
//var UseGui bool
|
|
||||||
var DataDir string
|
var DataDir string
|
||||||
|
var NonInteractive bool
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
|
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
|
||||||
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
|
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
|
||||||
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
|
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
|
||||||
//flag.BoolVar(&UseGui, "gui", true, "use the gui")
|
//flag.BoolVar(&UseGui, "gui", true, "use the gui")
|
||||||
|
flag.BoolVar(&StartRpc, "r", false, "start rpc server")
|
||||||
|
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||||
flag.BoolVar(&ExportKey, "export", false, "export private key")
|
flag.BoolVar(&ExportKey, "export", false, "export private key")
|
||||||
flag.StringVar(&OutboundPort, "p", "30303", "listening port")
|
flag.StringVar(&OutboundPort, "p", "30303", "listening port")
|
||||||
|
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||||
flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory")
|
flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory")
|
||||||
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
|
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
|
||||||
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
|
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
|
||||||
|
@ -11,7 +11,8 @@ import (
|
|||||||
"github.com/ethereum/eth-go/ethdb"
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
_ "math/big"
|
"github.com/ethereum/go-ethereum/utils"
|
||||||
|
"github.com/obscuren/mutan"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -52,15 +53,15 @@ func (i *Console) ValidateInput(action string, argumentLength int) error {
|
|||||||
case action == "gettx" && argumentLength != 1:
|
case action == "gettx" && argumentLength != 1:
|
||||||
err = true
|
err = true
|
||||||
expArgCount = 1
|
expArgCount = 1
|
||||||
case action == "tx" && argumentLength != 2:
|
case action == "tx" && argumentLength != 4:
|
||||||
err = true
|
err = true
|
||||||
expArgCount = 2
|
expArgCount = 4
|
||||||
case action == "getaddr" && argumentLength != 1:
|
case action == "getaddr" && argumentLength != 1:
|
||||||
err = true
|
err = true
|
||||||
expArgCount = 1
|
expArgCount = 1
|
||||||
case action == "contract" && argumentLength != 1:
|
case action == "contract" && argumentLength != 2:
|
||||||
err = true
|
err = true
|
||||||
expArgCount = 1
|
expArgCount = 2
|
||||||
case action == "say" && argumentLength != 1:
|
case action == "say" && argumentLength != 1:
|
||||||
err = true
|
err = true
|
||||||
expArgCount = 1
|
expArgCount = 1
|
||||||
@ -79,7 +80,7 @@ func (i *Console) ValidateInput(action string, argumentLength int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Console) Editor() []string {
|
func (i *Console) Editor() string {
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
for {
|
for {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
@ -94,15 +95,7 @@ func (i *Console) Editor() []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(buff.String()))
|
return buff.String()
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
|
|
||||||
var lines []string
|
|
||||||
for scanner.Scan() {
|
|
||||||
lines = append(lines, scanner.Text())
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Console) PrintRoot() {
|
func (i *Console) PrintRoot() {
|
||||||
@ -178,7 +171,7 @@ func (i *Console) ParseInput(input string) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("recipient err:", err)
|
fmt.Println("recipient err:", err)
|
||||||
} else {
|
} else {
|
||||||
tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""})
|
tx := ethchain.NewTransactionMessage(recipient, ethutil.Big(tokens[2]), ethutil.Big(tokens[3]), ethutil.Big(tokens[4]), nil)
|
||||||
|
|
||||||
key := ethutil.Config.Db.GetKeys()[0]
|
key := ethutil.Config.Db.GetKeys()[0]
|
||||||
tx.Sign(key.PrivateKey)
|
tx.Sign(key.PrivateKey)
|
||||||
@ -197,9 +190,22 @@ func (i *Console) ParseInput(input string) bool {
|
|||||||
}
|
}
|
||||||
case "contract":
|
case "contract":
|
||||||
fmt.Println("Contract editor (Ctrl-D = done)")
|
fmt.Println("Contract editor (Ctrl-D = done)")
|
||||||
code := ethchain.Compile(i.Editor())
|
|
||||||
|
|
||||||
contract := ethchain.NewTransaction(ethchain.ContractAddr, ethutil.Big(tokens[1]), code)
|
mainInput, initInput := mutan.PreProcess(i.Editor())
|
||||||
|
mainScript, err := utils.Compile(mainInput)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
initScript, err := utils.Compile(initInput)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
contract := ethchain.NewContractCreationTx(ethutil.Big(tokens[0]), ethutil.Big(tokens[1]), ethutil.Big(tokens[1]), mainScript, initScript)
|
||||||
|
|
||||||
key := ethutil.Config.Db.GetKeys()[0]
|
key := ethutil.Config.Db.GetKeys()[0]
|
||||||
contract.Sign(key.PrivateKey)
|
contract.Sign(key.PrivateKey)
|
||||||
|
@ -4,19 +4,22 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/eth-go"
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethminer"
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
|
"github.com/ethereum/eth-go/ethrpc"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
|
||||||
"github.com/ethereum/go-ethereum/utils"
|
"github.com/ethereum/go-ethereum/utils"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Debug = true
|
const Debug = true
|
||||||
|
|
||||||
// Register interrupt handlers so we can stop the ethereum
|
// Register interrupt handlers so we can stop the ethereum
|
||||||
func RegisterInterupts(s *eth.Ethereum) {
|
func RegisterInterrupts(s *eth.Ethereum) {
|
||||||
// Buffered chan of one is enough
|
// Buffered chan of one is enough
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
// Notify about interrupts for now
|
// Notify about interrupts for now
|
||||||
@ -24,19 +27,52 @@ func RegisterInterupts(s *eth.Ethereum) {
|
|||||||
go func() {
|
go func() {
|
||||||
for sig := range c {
|
for sig := range c {
|
||||||
fmt.Printf("Shutting down (%v) ... \n", sig)
|
fmt.Printf("Shutting down (%v) ... \n", sig)
|
||||||
|
|
||||||
s.Stop()
|
s.Stop()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func confirm(message string) bool {
|
||||||
|
fmt.Println(message, "Are you sure? (y/n)")
|
||||||
|
var r string
|
||||||
|
fmt.Scanln(&r)
|
||||||
|
for ; ; fmt.Scanln(&r) {
|
||||||
|
if r == "n" || r == "y" {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Yes or no?", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r == "y"
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
Init()
|
Init()
|
||||||
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
ethchain.InitFees()
|
// set logger
|
||||||
|
var logSys *log.Logger
|
||||||
|
flags := log.LstdFlags
|
||||||
|
|
||||||
ethutil.ReadConfig(DataDir)
|
ethutil.ReadConfig(DataDir)
|
||||||
|
logger := ethutil.Config.Log
|
||||||
|
|
||||||
|
if LogFile != "" {
|
||||||
|
logfile, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error opening log file '%s': %v", LogFile, err))
|
||||||
|
}
|
||||||
|
defer logfile.Close()
|
||||||
|
log.SetOutput(logfile)
|
||||||
|
logSys = log.New(logfile, "", flags)
|
||||||
|
logger.AddLogSystem(logSys)
|
||||||
|
}
|
||||||
|
/*else {
|
||||||
|
logSys = log.New(os.Stdout, "", flags)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
ethchain.InitFees()
|
||||||
ethutil.Config.Seed = UseSeed
|
ethutil.Config.Seed = UseSeed
|
||||||
|
|
||||||
// Instantiated a eth stack
|
// Instantiated a eth stack
|
||||||
@ -47,57 +83,42 @@ func main() {
|
|||||||
}
|
}
|
||||||
ethereum.Port = OutboundPort
|
ethereum.Port = OutboundPort
|
||||||
|
|
||||||
if GenAddr {
|
// bookkeeping tasks
|
||||||
fmt.Println("This action overwrites your old private key. Are you sure? (y/n)")
|
switch {
|
||||||
|
case GenAddr:
|
||||||
var r string
|
if NonInteractive || confirm("This action overwrites your old private key.") {
|
||||||
fmt.Scanln(&r)
|
|
||||||
for ; ; fmt.Scanln(&r) {
|
|
||||||
if r == "n" || r == "y" {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Yes or no?", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == "y" {
|
|
||||||
utils.CreateKeyPair(true)
|
utils.CreateKeyPair(true)
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
} else {
|
case len(ImportKey) > 0:
|
||||||
if len(ImportKey) > 0 {
|
if NonInteractive || confirm("This action overwrites your old private key.") {
|
||||||
fmt.Println("This action overwrites your old private key. Are you sure? (y/n)")
|
mnemonic := strings.Split(ImportKey, " ")
|
||||||
var r string
|
if len(mnemonic) == 24 {
|
||||||
fmt.Scanln(&r)
|
logSys.Println("Got mnemonic key, importing.")
|
||||||
for ; ; fmt.Scanln(&r) {
|
key := ethutil.MnemonicDecode(mnemonic)
|
||||||
if r == "n" || r == "y" {
|
utils.ImportPrivateKey(key)
|
||||||
break
|
} else if len(mnemonic) == 1 {
|
||||||
} else {
|
logSys.Println("Got hex key, importing.")
|
||||||
fmt.Printf("Yes or no?", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == "y" {
|
|
||||||
utils.ImportPrivateKey(ImportKey)
|
utils.ImportPrivateKey(ImportKey)
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
logSys.Println("Did not recognise format, exiting.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
case ExportKey:
|
||||||
|
key := ethutil.Config.Db.GetKeys()[0]
|
||||||
|
logSys.Println(fmt.Sprintf("prvk: %x\n", key.PrivateKey))
|
||||||
|
os.Exit(0)
|
||||||
|
case ShowGenesis:
|
||||||
|
logSys.Println(ethereum.BlockChain().Genesis())
|
||||||
|
os.Exit(0)
|
||||||
|
default:
|
||||||
|
// Creates a keypair if non exists
|
||||||
utils.CreateKeyPair(false)
|
utils.CreateKeyPair(false)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ExportKey {
|
// client
|
||||||
key := ethutil.Config.Db.GetKeys()[0]
|
logger.Infoln(fmt.Sprintf("Starting Ethereum v%s", ethutil.Config.Ver))
|
||||||
fmt.Printf("%x\n", key.PrivateKey)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ShowGenesis {
|
|
||||||
fmt.Println(ethereum.BlockChain().Genesis())
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Starting Ethereum v%s\n", ethutil.Config.Ver)
|
|
||||||
|
|
||||||
// Set the max peers
|
// Set the max peers
|
||||||
ethereum.MaxPeers = MaxPeer
|
ethereum.MaxPeers = MaxPeer
|
||||||
@ -112,45 +133,35 @@ func main() {
|
|||||||
console := NewConsole(ethereum)
|
console := NewConsole(ethereum)
|
||||||
go console.Start()
|
go console.Start()
|
||||||
}
|
}
|
||||||
|
if StartRpc {
|
||||||
|
ethereum.RpcServer = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum.StateManager(), ethereum.BlockChain(), ethereum.TxPool()))
|
||||||
|
go ethereum.RpcServer.Start()
|
||||||
|
}
|
||||||
|
|
||||||
RegisterInterupts(ethereum)
|
RegisterInterrupts(ethereum)
|
||||||
ethereum.Start()
|
ethereum.Start()
|
||||||
|
|
||||||
if StartMining {
|
if StartMining {
|
||||||
log.Printf("Miner started\n")
|
logger.Infoln("Miner started")
|
||||||
|
|
||||||
// Fake block mining. It broadcasts a new block every 5 seconds
|
// Fake block mining. It broadcasts a new block every 5 seconds
|
||||||
go func() {
|
go func() {
|
||||||
pow := ðchain.EasyPow{}
|
|
||||||
|
if StartMining {
|
||||||
|
logger.Infoln("Miner started")
|
||||||
|
|
||||||
|
go func() {
|
||||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||||
keyRing := ethutil.NewValueFromBytes(data)
|
keyRing := ethutil.NewValueFromBytes(data)
|
||||||
addr := keyRing.Get(1).Bytes()
|
addr := keyRing.Get(1).Bytes()
|
||||||
|
|
||||||
for {
|
miner := ethminer.NewDefaultMiner(addr, ethereum)
|
||||||
txs := ethereum.TxPool().Flush()
|
miner.Start()
|
||||||
// Create a new block which we're going to mine
|
|
||||||
block := ethereum.BlockChain().NewBlock(addr, txs)
|
|
||||||
log.Println("Mining on new block. Includes", len(block.Transactions()), "transactions")
|
|
||||||
// Apply all transactions to the block
|
|
||||||
ethereum.StateManager().ApplyTransactions(block, block.Transactions())
|
|
||||||
|
|
||||||
ethereum.StateManager().Prepare(block.State(), block.State())
|
}()
|
||||||
ethereum.StateManager().AccumelateRewards(block)
|
|
||||||
|
|
||||||
// Search the nonce
|
|
||||||
block.Nonce = pow.Search(block)
|
|
||||||
ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
|
||||||
|
|
||||||
ethereum.StateManager().PrepareDefault(block)
|
|
||||||
err := ethereum.StateManager().ProcessBlock(block)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
log.Println("\n+++++++ MINED BLK +++++++\n", ethereum.BlockChain().CurrentBlock)
|
|
||||||
log.Printf("🔨 Mined block %x\n", block.Hash())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for shutdown
|
// Wait for shutdown
|
||||||
|
41
utils/compile.go
Normal file
41
utils/compile.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/obscuren/mutan"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// General compile function
|
||||||
|
func Compile(script string) ([]byte, error) {
|
||||||
|
byteCode, errors := mutan.Compile(strings.NewReader(script), false)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
var errs string
|
||||||
|
for _, er := range errors {
|
||||||
|
if er != nil {
|
||||||
|
errs += er.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileScript(script string) ([]byte, []byte, error) {
|
||||||
|
// Preprocess
|
||||||
|
mainInput, initInput := mutan.PreProcess(script)
|
||||||
|
// Compile main script
|
||||||
|
mainScript, err := Compile(mainInput)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile init script
|
||||||
|
initScript, err := Compile(initInput)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainScript, initScript, nil
|
||||||
|
}
|
@ -12,6 +12,7 @@ func CreateKeyPair(force bool) {
|
|||||||
pub, prv := secp256k1.GenerateKeyPair()
|
pub, prv := secp256k1.GenerateKeyPair()
|
||||||
pair := ðutil.Key{PrivateKey: prv, PublicKey: pub}
|
pair := ðutil.Key{PrivateKey: prv, PublicKey: pub}
|
||||||
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
||||||
|
mne := ethutil.MnemonicEncode(ethutil.Hex(prv))
|
||||||
|
|
||||||
fmt.Printf(`
|
fmt.Printf(`
|
||||||
Generating new address and keypair.
|
Generating new address and keypair.
|
||||||
@ -22,8 +23,8 @@ addr: %x
|
|||||||
prvk: %x
|
prvk: %x
|
||||||
pubk: %x
|
pubk: %x
|
||||||
++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
save these words so you can restore your account later: %s
|
||||||
`, pair.Address(), prv, pub)
|
`, pair.Address(), prv, pub, mne)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user