Compare commits
298 Commits
Author | SHA1 | Date | |
---|---|---|---|
cba4796311 | |||
65cbea2b6a | |||
c89566a42f | |||
3ec0c719b9 | |||
e517fb5f9d | |||
98335d2040 | |||
c1220e8729 | |||
3744151359 | |||
7ab735eb02 | |||
ef1b923b31 | |||
e7a22af0e6 | |||
1d300bbc10 | |||
e36badd744 | |||
57e3b1b093 | |||
0ee258bb75 | |||
66199497a0 | |||
aa8a86f0a6 | |||
d929c63474 | |||
ba3623d0cc | |||
cc20b0e3a0 | |||
a107a5db05 | |||
964587b14a | |||
7843390ecd | |||
307fe4a3cd | |||
3755616a29 | |||
cc1d043423 | |||
9e411d785b | |||
a6f4eef1da | |||
98811f11e5 | |||
d6acb74ac9 | |||
397e99fcc6 | |||
be27309dbb | |||
4dfce5d347 | |||
0bdb0a9d58 | |||
5f28013f79 | |||
65c5a20e1c | |||
1020d7ff67 | |||
e7c9b86a5a | |||
0938b56829 | |||
fcbf99a30a | |||
40d72ff40b | |||
efadfbfb17 | |||
8ee6574d12 | |||
efb3ee044b | |||
0b4c42d756 | |||
f0f205004c | |||
f802e17626 | |||
8fab7ce37d | |||
44db1a1eb2 | |||
0aee830bde | |||
06d41794f9 | |||
2114218ed8 | |||
58032d60e7 | |||
d4f9daa631 | |||
1eda1d25b0 | |||
aaeb268522 | |||
540d39220d | |||
09728bf43c | |||
090447c664 | |||
d4af5a5763 | |||
198ef97108 | |||
138b7fe2d8 | |||
87a669aeda | |||
969b4a4a36 | |||
118860abb2 | |||
0dda955f90 | |||
34b861c19c | |||
a0f73c2703 | |||
2be9823010 | |||
47417506c3 | |||
d0b31e2030 | |||
6fe42f007c | |||
1ab865a994 | |||
4fd267a778 | |||
07204f129e | |||
47a58b40cd | |||
474c85bc9d | |||
d694e00a33 | |||
f7eb4e587f | |||
5fc6ee6a4a | |||
5374a95c58 | |||
26ecf4b780 | |||
0d89c1d212 | |||
818bc84591 | |||
b42c70be9c | |||
d35380c19e | |||
36683f2e29 | |||
5f8911f7cb | |||
6b115659ca | |||
8419ba0ec0 | |||
01b833146f | |||
b902de20c7 | |||
3f5b348451 | |||
93d79babc9 | |||
941e0ba60a | |||
7f1a4c377c | |||
3ddaf56afd | |||
10e2c40b59 | |||
68f4a12a8b | |||
34008da807 | |||
16bd88c10a | |||
0bccf1c3cd | |||
d16d56d39f | |||
93e12250c7 | |||
f4551a7e9f | |||
563c035eb5 | |||
de1dfae717 | |||
4198969302 | |||
c07c454935 | |||
34014c1c51 | |||
0cf617ef0c | |||
0ef7f63729 | |||
a05adb1128 | |||
dfc3cb441b | |||
92eaa98e83 | |||
017bbbb582 | |||
16421106d4 | |||
3b7707c3fd | |||
30842eb8d0 | |||
43f88b2bbb | |||
770808ce0d | |||
2ac292dc7a | |||
6a78e080e6 | |||
0a03484188 | |||
cbce882f5e | |||
3a2bddc160 | |||
9ba3c6d1af | |||
942f552c62 | |||
a73ae8727d | |||
278ee3f16c | |||
9a057021c3 | |||
a1dcc5cd17 | |||
c9db87277b | |||
2c7b625daa | |||
f18ec51cb3 | |||
9fce273ce9 | |||
2012e0c67a | |||
e8147cf7c6 | |||
0d9c948b9b | |||
faa3073625 | |||
809b4ae0f6 | |||
7f94b266c7 | |||
1adfc272a8 | |||
9caf53f8c6 | |||
fca36cc03d | |||
b71094b01c | |||
edc281ac5f | |||
ee2cef3b2e | |||
54eff2d778 | |||
20ea78945e | |||
dd60382fc3 | |||
32c6126593 | |||
03371b74d7 | |||
c99aa7bdcf | |||
5208b04821 | |||
9a03df7bd8 | |||
67820506cb | |||
8c9e6746ce | |||
b9876df5dc | |||
a5963d1377 | |||
c3ad210846 | |||
618f523124 | |||
c7132e5d22 | |||
c43ea30e75 | |||
cf7ab07264 | |||
5d15563ea7 | |||
a3cc4b0b80 | |||
721d3a9a57 | |||
23fc50c61b | |||
faa3aa14ed | |||
b0023f66b0 | |||
109395daaa | |||
f73a5f067a | |||
1471585af0 | |||
0bf2d24cb0 | |||
f59f515def | |||
71defc11fa | |||
53eb59ed81 | |||
fb903619e8 | |||
a77dcd1041 | |||
70221c36e3 | |||
c9852c4929 | |||
cb9ca992de | |||
e94e5ac75d | |||
91824af46b | |||
dd45197bcd | |||
bcb3ad7332 | |||
2582d719b2 | |||
231ad9b562 | |||
ed64434dcc | |||
60f9966cd8 | |||
f1da6f0564 | |||
3424bf17ca | |||
ee04c6ff67 | |||
5a692b9f2b | |||
471bd398f3 | |||
1b597b8ca9 | |||
9e481804a7 | |||
76cd14ab7b | |||
abb2bebf7f | |||
da7828f336 | |||
c5481b7654 | |||
183dbcc6a0 | |||
e85d5dd428 | |||
64c2550b31 | |||
922974c760 | |||
c0de11955b | |||
a0f35d3248 | |||
68e5568804 | |||
883810b533 | |||
0e8ca84b67 | |||
e16fd323e8 | |||
d0438ac10a | |||
6ca99709d2 | |||
bbde892d50 | |||
c535d0d246 | |||
bb72347acf | |||
43f1214f97 | |||
b962779a13 | |||
a3c8f83562 | |||
aec3e26ea0 | |||
6d5d539a85 | |||
a0c97b663d | |||
59a7b13019 | |||
32b09d652d | |||
f4c13f8656 | |||
7f0c974008 | |||
9cf77cdbad | |||
1cd7d4456b | |||
6b644c17a7 | |||
7cb065489c | |||
c23a971a1f | |||
91c75c9305 | |||
28a48f1d9a | |||
8a2698ad5e | |||
d092d05a31 | |||
ce43a9500f | |||
ab8d96258e | |||
3f82d5172f | |||
11aa7da6c3 | |||
5768b18a3b | |||
710bbed1a2 | |||
a9a6585913 | |||
4d18798468 | |||
cf1ae41bc0 | |||
8280dd65e6 | |||
3238894a3b | |||
e2bf5d1270 | |||
834e43622c | |||
d9ce5f5f82 | |||
cc5501b12f | |||
52b63459e9 | |||
1e94cb5286 | |||
cd799926d2 | |||
2edf133b46 | |||
5da03f2e36 | |||
ee5e7f2b35 | |||
5660d598df | |||
e403b28eea | |||
97b98b1250 | |||
ebbc5e7cb8 | |||
3fb7ae2fa1 | |||
c5215fd4fb | |||
1257e8b4b3 | |||
1323f60c07 | |||
e65c4ee93e | |||
49c710bf44 | |||
a30f5730b3 | |||
6724d27c0c | |||
642630db15 | |||
45ec9c88e4 | |||
fe79a8f724 | |||
1f2547b8a7 | |||
22b4e9b617 | |||
0db86e4485 | |||
3002570085 | |||
07734c1e1c | |||
85e0447684 | |||
13e18e1d8f | |||
1c983ed80c | |||
96fcc1da32 | |||
fbd53f0e34 | |||
f7fb5b902c | |||
8275059856 | |||
833a1a4eab | |||
3d2c3b0107 | |||
a482b0cc1b | |||
00533003b7 | |||
b0be847416 | |||
570ab249b3 | |||
c6d6ca283d | |||
30c5922aa4 | |||
59a68b316f | |||
893da20ead | |||
aa7c53b7ef | |||
a9d89d1f59 | |||
f6a9aa4110 | |||
b72d3528bf |
1
.gitignore
vendored
@ -9,4 +9,5 @@
|
||||
*un~
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
./ethereum/ethereum
|
||||
|
||||
|
84
README.md
@ -3,67 +3,72 @@ Ethereum
|
||||
|
||||
[](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". For build instructions see
|
||||
the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge).
|
||||
Current state: Proof of Concept 5.0 RC14.
|
||||
|
||||
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
|
||||
=======
|
||||
|
||||
For build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge)
|
||||
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
|
||||
-m Start mining blocks
|
||||
Shared between ethereum and ethereal
|
||||
-id Set the custom identifier of the client (shows up on other clients)
|
||||
-port Port on which the server will accept incomming connections
|
||||
-upnp Enable UPnP
|
||||
-maxpeer Desired amount of peers
|
||||
-rpc Start JSON RPC
|
||||
|
||||
-dir Data directory used to store configs and databases
|
||||
-import Import a private key
|
||||
-genaddr Generates a new address and private key (destructive action)
|
||||
-p Port on which the server will accept incomming connections (= 30303)
|
||||
-upnp Enable UPnP (= false)
|
||||
-x Desired amount of peers (= 5)
|
||||
-h This help
|
||||
-gui Launch with GUI (= true)
|
||||
-dir Data directory used to store configs and databases (=".ethereum")
|
||||
-import Import a private key (hex)
|
||||
```
|
||||
-h This
|
||||
|
||||
Developer console commands
|
||||
==========================
|
||||
Ethereum only
|
||||
ethereum [options] [filename]
|
||||
-js Start the JavaScript REPL
|
||||
filename Load the given file and interpret as JavaScript
|
||||
-m Start mining blocks
|
||||
|
||||
Etheral only
|
||||
-asset_path absolute path to GUI assets directory
|
||||
```
|
||||
addp <host>:<port> Connect to the given host
|
||||
tx <addr> <amount> Send <amount> Wei to the specified <addr>
|
||||
```
|
||||
|
||||
See the "help" command for *developer* options.
|
||||
|
||||
Contribution
|
||||
============
|
||||
|
||||
If you'd like to contribute to Ethereum Go please fork, fix, commit and
|
||||
send a pull request. Commits who do not comply with the coding standards
|
||||
are ignored. If you send pull requests make absolute sure that you
|
||||
commit on the `develop` branch and that you do not merge to master.
|
||||
Commits that are directly based on master are simply ignored.
|
||||
If you would like to contribute to Ethereum Go, please fork, fix, commit and
|
||||
send a pull request to the main repository. Commits which do not comply with the coding standards explained below
|
||||
will be ignored. If you send a pull request, make sure that you
|
||||
commit to the `develop` branch and that you do not merge to `master`.
|
||||
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
|
||||
this all up and streamlines your work flow.
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
Unless structs fields are supposed to be directly accesible, provide
|
||||
Getters and hide the fields through Go's exporting facility.
|
||||
Unless struct fields are supposed to be directly accessible, provide
|
||||
getters and hide the fields through Go's exporting facility.
|
||||
|
||||
When you comment put meaningfull comments. Describe in detail what you
|
||||
want to achieve.
|
||||
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:
|
||||
|
||||
*wrong*
|
||||
|
||||
@ -74,12 +79,7 @@ if x > y {
|
||||
}
|
||||
```
|
||||
|
||||
Everyone reading the source probably know what you wanted to achieve
|
||||
with above code. Those are **not** meaningful comments.
|
||||
Everyone reading the source code should know what this code snippet was meant to achieve, and so those are **not** meaningful comments.
|
||||
|
||||
While the project isn't 100% tested I want you to write tests non the
|
||||
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!)
|
||||
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!
|
||||
|
||||
|
36
config.go
@ -1,36 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
var StartConsole bool
|
||||
var StartMining bool
|
||||
var UseUPnP bool
|
||||
var OutboundPort string
|
||||
var ShowGenesis bool
|
||||
var AddPeer string
|
||||
var MaxPeer int
|
||||
var GenAddr bool
|
||||
var UseSeed bool
|
||||
var ImportKey string
|
||||
var ExportKey bool
|
||||
var UseGui bool
|
||||
var DataDir string
|
||||
|
||||
func Init() {
|
||||
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
|
||||
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
|
||||
flag.BoolVar(&ShowGenesis, "g", false, "prints genesis header and exits")
|
||||
flag.BoolVar(&UseGui, "gui", true, "use the gui")
|
||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||
flag.BoolVar(&ExportKey, "export", false, "export private key")
|
||||
flag.StringVar(&OutboundPort, "p", "30303", "listening port")
|
||||
flag.StringVar(&DataDir, "dir", ".ethereum", "ethereum data directory")
|
||||
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
|
||||
flag.IntVar(&MaxPeer, "x", 5, "maximum desired peers")
|
||||
|
||||
flag.Parse()
|
||||
}
|
253
dev_console.go
@ -1,253 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
_ "math/big"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Console struct {
|
||||
db *ethdb.MemDatabase
|
||||
trie *ethutil.Trie
|
||||
ethereum *eth.Ethereum
|
||||
}
|
||||
|
||||
func NewConsole(s *eth.Ethereum) *Console {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
trie := ethutil.NewTrie(db, "")
|
||||
|
||||
return &Console{db: db, trie: trie, ethereum: s}
|
||||
}
|
||||
|
||||
func (i *Console) ValidateInput(action string, argumentLength int) error {
|
||||
err := false
|
||||
var expArgCount int
|
||||
|
||||
switch {
|
||||
case action == "update" && argumentLength != 2:
|
||||
err = true
|
||||
expArgCount = 2
|
||||
case action == "get" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "dag" && argumentLength != 2:
|
||||
err = true
|
||||
expArgCount = 2
|
||||
case action == "decode" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "encode" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "gettx" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "tx" && argumentLength != 2:
|
||||
err = true
|
||||
expArgCount = 2
|
||||
case action == "getaddr" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "contract" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "say" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "addp" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
case action == "block" && argumentLength != 1:
|
||||
err = true
|
||||
expArgCount = 1
|
||||
}
|
||||
|
||||
if err {
|
||||
return errors.New(fmt.Sprintf("'%s' requires %d args, got %d", action, expArgCount, argumentLength))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Console) Editor() []string {
|
||||
var buff bytes.Buffer
|
||||
for {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
str, _, err := reader.ReadLine()
|
||||
if len(str) > 0 {
|
||||
buff.Write(str)
|
||||
buff.WriteString("\n")
|
||||
}
|
||||
|
||||
if err != nil && err.Error() == "EOF" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(buff.String()))
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
var lines []string
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (i *Console) PrintRoot() {
|
||||
root := ethutil.NewValue(i.trie.Root)
|
||||
if len(root.Bytes()) != 0 {
|
||||
fmt.Println(hex.EncodeToString(root.Bytes()))
|
||||
} else {
|
||||
fmt.Println(i.trie.Root)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Console) ParseInput(input string) bool {
|
||||
scanner := bufio.NewScanner(strings.NewReader(input))
|
||||
scanner.Split(bufio.ScanWords)
|
||||
|
||||
count := 0
|
||||
var tokens []string
|
||||
for scanner.Scan() {
|
||||
count++
|
||||
tokens = append(tokens, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "reading input:", err)
|
||||
}
|
||||
|
||||
if len(tokens) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
err := i.ValidateInput(tokens[0], count-1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
switch tokens[0] {
|
||||
case "update":
|
||||
i.trie.Update(tokens[1], tokens[2])
|
||||
|
||||
i.PrintRoot()
|
||||
case "get":
|
||||
fmt.Println(i.trie.Get(tokens[1]))
|
||||
case "root":
|
||||
i.PrintRoot()
|
||||
case "rawroot":
|
||||
fmt.Println(i.trie.Root)
|
||||
case "print":
|
||||
i.db.Print()
|
||||
case "dag":
|
||||
fmt.Println(ethchain.DaggerVerify(ethutil.Big(tokens[1]), // hash
|
||||
ethutil.BigPow(2, 36), // diff
|
||||
ethutil.Big(tokens[2]))) // nonce
|
||||
case "decode":
|
||||
value := ethutil.NewValueFromBytes([]byte(tokens[1]))
|
||||
fmt.Println(value)
|
||||
case "getaddr":
|
||||
encoded, _ := hex.DecodeString(tokens[1])
|
||||
addr := i.ethereum.BlockManager.BlockChain().CurrentBlock.GetAddr(encoded)
|
||||
fmt.Println("addr:", addr)
|
||||
case "block":
|
||||
encoded, _ := hex.DecodeString(tokens[1])
|
||||
block := i.ethereum.BlockManager.BlockChain().GetBlock(encoded)
|
||||
info := block.BlockInfo()
|
||||
fmt.Printf("++++++++++ #%d ++++++++++\n%v\n", info.Number, block)
|
||||
case "say":
|
||||
i.ethereum.Broadcast(ethwire.MsgTalkTy, []interface{}{tokens[1]})
|
||||
case "addp":
|
||||
i.ethereum.ConnectToPeer(tokens[1])
|
||||
case "pcount":
|
||||
fmt.Println("peers:", i.ethereum.Peers().Len())
|
||||
case "encode":
|
||||
fmt.Printf("%q\n", ethutil.Encode(tokens[1]))
|
||||
case "tx":
|
||||
recipient, err := hex.DecodeString(tokens[1])
|
||||
if err != nil {
|
||||
fmt.Println("recipient err:", err)
|
||||
} else {
|
||||
tx := ethchain.NewTransaction(recipient, ethutil.Big(tokens[2]), []string{""})
|
||||
|
||||
key := ethutil.Config.Db.GetKeys()[0]
|
||||
tx.Sign(key.PrivateKey)
|
||||
i.ethereum.TxPool.QueueTransaction(tx)
|
||||
|
||||
fmt.Printf("%x\n", tx.Hash())
|
||||
}
|
||||
case "gettx":
|
||||
addr, _ := hex.DecodeString(tokens[1])
|
||||
data, _ := ethutil.Config.Db.Get(addr)
|
||||
if len(data) != 0 {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
fmt.Println(decoder)
|
||||
} else {
|
||||
fmt.Println("gettx: tx not found")
|
||||
}
|
||||
case "contract":
|
||||
fmt.Println("Contract editor (Ctrl-D = done)")
|
||||
code := ethchain.Compile(i.Editor())
|
||||
|
||||
contract := ethchain.NewTransaction(ethchain.ContractAddr, ethutil.Big(tokens[1]), code)
|
||||
|
||||
key := ethutil.Config.Db.GetKeys()[0]
|
||||
contract.Sign(key.PrivateKey)
|
||||
|
||||
i.ethereum.TxPool.QueueTransaction(contract)
|
||||
|
||||
fmt.Printf("%x\n", contract.Hash()[12:])
|
||||
case "exit", "quit", "q":
|
||||
return false
|
||||
case "help":
|
||||
fmt.Printf("COMMANDS:\n" +
|
||||
"\033[1m= DB =\033[0m\n" +
|
||||
"update KEY VALUE - Updates/Creates a new value for the given key\n" +
|
||||
"get KEY - Retrieves the given key\n" +
|
||||
"root - Prints the hex encoded merkle root\n" +
|
||||
"rawroot - Prints the raw merkle root\n" +
|
||||
"block HASH - Prints the block\n" +
|
||||
"getaddr ADDR - Prints the account associated with the address\n" +
|
||||
"\033[1m= Dagger =\033[0m\n" +
|
||||
"dag HASH NONCE - Verifies a nonce with the given hash with dagger\n" +
|
||||
"\033[1m= Encoding =\033[0m\n" +
|
||||
"decode STR\n" +
|
||||
"encode STR\n" +
|
||||
"\033[1m= Other =\033[0m\n" +
|
||||
"addp HOST:PORT\n" +
|
||||
"tx TO AMOUNT\n" +
|
||||
"contract AMOUNT\n")
|
||||
|
||||
default:
|
||||
fmt.Println("Unknown command:", tokens[0])
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *Console) Start() {
|
||||
fmt.Printf("Eth Console. Type (help) for help\n")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Printf("eth >>> ")
|
||||
str, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Println("Error reading input", err)
|
||||
} else {
|
||||
if !i.ParseInput(string(str)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
ethereal/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
UNAME = $(shell uname)
|
||||
FILES=qml *.png
|
||||
GOPATH=$(PWD)
|
||||
|
||||
|
||||
# Default is building
|
||||
all:
|
||||
go get -d
|
||||
cp *.go $(GOPATH)/src/github.com/ethereum/go-ethereum
|
||||
cp -r ui $(GOPATH)/src/github.com/ethereum/go-ethereum
|
||||
go build
|
||||
|
||||
install:
|
||||
# Linux build
|
||||
ifeq ($(UNAME),Linux)
|
||||
cp -r assets/* /usr/share/ethereal
|
||||
cp go-ethereum /usr/local/bin/ethereal
|
||||
endif
|
||||
# OS X build
|
||||
ifeq ($(UNAME),Darwin)
|
||||
# Execute py script
|
||||
endif
|
264
ethereal/assets/debugger/debugger.qml
Normal file
@ -0,0 +1,264 @@
|
||||
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
|
||||
|
||||
ApplicationWindow {
|
||||
visible: false
|
||||
title: "IceCREAM"
|
||||
minimumWidth: 1280
|
||||
minimumHeight: 900
|
||||
width: 1290
|
||||
height: 900
|
||||
|
||||
property alias codeText: codeEditor.text
|
||||
property alias dataText: rawDataField.text
|
||||
|
||||
MenuBar {
|
||||
Menu {
|
||||
title: "Debugger"
|
||||
MenuItem {
|
||||
text: "Run"
|
||||
shortcut: "Ctrl+r"
|
||||
onTriggered: debugCurrent()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Next"
|
||||
shortcut: "Ctrl+n"
|
||||
onTriggered: dbg.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 {
|
||||
color: "#00000000"
|
||||
anchors.left: asmTableView.right
|
||||
anchors.right: parent.right
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
color: "#00000000"
|
||||
height: 500
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
TextArea {
|
||||
id: codeEditor
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: settings.left
|
||||
}
|
||||
|
||||
Column {
|
||||
id: settings
|
||||
spacing: 5
|
||||
width: 300
|
||||
height: parent.height
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Label {
|
||||
text: "Arbitrary data"
|
||||
}
|
||||
TextArea {
|
||||
id: rawDataField
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 150
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Amount"
|
||||
}
|
||||
TextField {
|
||||
id: txValue
|
||||
width: 200
|
||||
placeholderText: "Amount"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
Label {
|
||||
text: "Amount of gas"
|
||||
}
|
||||
TextField {
|
||||
id: txGas
|
||||
width: 200
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
text: "10000"
|
||||
placeholderText: "Gas"
|
||||
}
|
||||
Label {
|
||||
text: "Gas price"
|
||||
}
|
||||
TextField {
|
||||
id: txGasPrice
|
||||
width: 200
|
||||
placeholderText: "Gas price"
|
||||
text: "1000000000000"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
id: inspectorPane
|
||||
height: 500
|
||||
|
||||
SplitView {
|
||||
orientation: Qt.Horizontal
|
||||
height: 150
|
||||
|
||||
TableView {
|
||||
id: stackTableView
|
||||
property var stackModel: ListModel {
|
||||
id: stackModel
|
||||
}
|
||||
height: parent.height
|
||||
width: 300
|
||||
TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 }
|
||||
model: stackModel
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: memoryTableView
|
||||
property var memModel: ListModel {
|
||||
id: memModel
|
||||
}
|
||||
height: parent.height
|
||||
width: parent.width - stackTableView.width
|
||||
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
|
||||
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
|
||||
model: memModel
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 100
|
||||
width: parent.width
|
||||
TableView {
|
||||
id: storageTableView
|
||||
property var memModel: ListModel {
|
||||
id: storageModel
|
||||
}
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2}
|
||||
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2}
|
||||
model: storageModel
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 200
|
||||
width: parent.width
|
||||
TableView {
|
||||
id: logTableView
|
||||
property var logModel: ListModel {
|
||||
id: logModel
|
||||
}
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width }
|
||||
model: logModel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toolBar: ToolBar {
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
|
||||
Button {
|
||||
property var enabled: true
|
||||
id: debugStart
|
||||
onClicked: {
|
||||
debugCurrent()
|
||||
}
|
||||
text: "Debug"
|
||||
}
|
||||
|
||||
Button {
|
||||
property var enabled: true
|
||||
id: debugNextButton
|
||||
onClicked: {
|
||||
dbg.next()
|
||||
}
|
||||
text: "Next"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function debugCurrent() {
|
||||
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
|
||||
}
|
||||
|
||||
function setAsm(asm) {
|
||||
asmModel.append({asm: asm})
|
||||
}
|
||||
|
||||
function clearAsm() {
|
||||
asmModel.clear()
|
||||
}
|
||||
|
||||
function setInstruction(num) {
|
||||
asmTableView.selection.clear()
|
||||
asmTableView.selection.select(num)
|
||||
}
|
||||
|
||||
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){
|
||||
debuggerLog.append({value: message})
|
||||
}
|
||||
|
||||
function clearStack() {
|
||||
stackModel.clear()
|
||||
}
|
||||
|
||||
function clearStorage() {
|
||||
storageModel.clear()
|
||||
}
|
||||
|
||||
function setStorage(storage) {
|
||||
storageModel.append({key: storage.key, value: storage.value})
|
||||
}
|
||||
|
||||
function setLog(msg) {
|
||||
logModel.insert(0, {message: msg})
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
logModel.clear()
|
||||
}
|
||||
}
|
380
ethereal/assets/ext/big.js
Normal file
@ -0,0 +1,380 @@
|
||||
var bigInt = (function () {
|
||||
var base = 10000000, logBase = 7;
|
||||
var sign = {
|
||||
positive: false,
|
||||
negative: true
|
||||
};
|
||||
|
||||
var normalize = function (first, second) {
|
||||
var a = first.value, b = second.value;
|
||||
var length = a.length > b.length ? a.length : b.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
a[i] = a[i] || 0;
|
||||
b[i] = b[i] || 0;
|
||||
}
|
||||
for (var i = length - 1; i >= 0; i--) {
|
||||
if (a[i] === 0 && b[i] === 0) {
|
||||
a.pop();
|
||||
b.pop();
|
||||
} else break;
|
||||
}
|
||||
if (!a.length) a = [0], b = [0];
|
||||
first.value = a;
|
||||
second.value = b;
|
||||
};
|
||||
|
||||
var parse = function (text, first) {
|
||||
if (typeof text === "object") return text;
|
||||
text += "";
|
||||
var s = sign.positive, value = [];
|
||||
if (text[0] === "-") {
|
||||
s = sign.negative;
|
||||
text = text.slice(1);
|
||||
}
|
||||
var base = 10;
|
||||
if (text.slice(0, 2) == "0x") {
|
||||
base = 16;
|
||||
text = text.slice(2);
|
||||
}
|
||||
else {
|
||||
var texts = text.split("e");
|
||||
if (texts.length > 2) throw new Error("Invalid integer");
|
||||
if (texts[1]) {
|
||||
var exp = texts[1];
|
||||
if (exp[0] === "+") exp = exp.slice(1);
|
||||
exp = parse(exp);
|
||||
if (exp.lesser(0)) throw new Error("Cannot include negative exponent part for integers");
|
||||
while (exp.notEquals(0)) {
|
||||
texts[0] += "0";
|
||||
exp = exp.prev();
|
||||
}
|
||||
}
|
||||
text = texts[0];
|
||||
}
|
||||
if (text === "-0") text = "0";
|
||||
text = text.toUpperCase();
|
||||
var isValid = (base == 16 ? /^[0-9A-F]*$/ : /^[0-9]+$/).test(text);
|
||||
if (!isValid) throw new Error("Invalid integer");
|
||||
if (base == 16) {
|
||||
var val = bigInt(0);
|
||||
while (text.length) {
|
||||
v = text.charCodeAt(0) - 48;
|
||||
if (v > 9)
|
||||
v -= 7;
|
||||
text = text.slice(1);
|
||||
val = val.times(16).plus(v);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
while (text.length) {
|
||||
var divider = text.length > logBase ? text.length - logBase : 0;
|
||||
value.push(+text.slice(divider));
|
||||
text = text.slice(0, divider);
|
||||
}
|
||||
var val = bigInt(value, s);
|
||||
if (first) normalize(first, val);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
var goesInto = function (a, b) {
|
||||
var a = bigInt(a, sign.positive), b = bigInt(b, sign.positive);
|
||||
if (a.equals(0)) throw new Error("Cannot divide by 0");
|
||||
var n = 0;
|
||||
do {
|
||||
var inc = 1;
|
||||
var c = bigInt(a.value, sign.positive), t = c.times(10);
|
||||
while (t.lesser(b)) {
|
||||
c = t;
|
||||
inc *= 10;
|
||||
t = t.times(10);
|
||||
}
|
||||
while (c.lesserOrEquals(b)) {
|
||||
b = b.minus(c);
|
||||
n += inc;
|
||||
}
|
||||
} while (a.lesserOrEquals(b));
|
||||
|
||||
return {
|
||||
remainder: b.value,
|
||||
result: n
|
||||
};
|
||||
};
|
||||
|
||||
var bigInt = function (value, s) {
|
||||
var self = {
|
||||
value: value,
|
||||
sign: s
|
||||
};
|
||||
var o = {
|
||||
value: value,
|
||||
sign: s,
|
||||
negate: function (m) {
|
||||
var first = m || self;
|
||||
return bigInt(first.value, !first.sign);
|
||||
},
|
||||
abs: function (m) {
|
||||
var first = m || self;
|
||||
return bigInt(first.value, sign.positive);
|
||||
},
|
||||
add: function (n, m) {
|
||||
var s, first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m));
|
||||
else second = parse(n, first);
|
||||
s = first.sign;
|
||||
if (first.sign !== second.sign) {
|
||||
first = bigInt(first.value, sign.positive);
|
||||
second = bigInt(second.value, sign.positive);
|
||||
return s === sign.positive ?
|
||||
o.subtract(first, second) :
|
||||
o.subtract(second, first);
|
||||
}
|
||||
normalize(first, second);
|
||||
var a = first.value, b = second.value;
|
||||
var result = [],
|
||||
carry = 0;
|
||||
for (var i = 0; i < a.length || carry > 0; i++) {
|
||||
var sum = (a[i] || 0) + (b[i] || 0) + carry;
|
||||
carry = sum >= base ? 1 : 0;
|
||||
sum -= carry * base;
|
||||
result.push(sum);
|
||||
}
|
||||
return bigInt(result, s);
|
||||
},
|
||||
plus: function (n, m) {
|
||||
return o.add(n, m);
|
||||
},
|
||||
subtract: function (n, m) {
|
||||
var first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m));
|
||||
else second = parse(n, first);
|
||||
if (first.sign !== second.sign) return o.add(first, o.negate(second));
|
||||
if (first.sign === sign.negative) return o.subtract(o.negate(second), o.negate(first));
|
||||
if (o.compare(first, second) === -1) return o.negate(o.subtract(second, first));
|
||||
var a = first.value, b = second.value;
|
||||
var result = [],
|
||||
borrow = 0;
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
var tmp = a[i] - borrow;
|
||||
borrow = tmp < b[i] ? 1 : 0;
|
||||
var minuend = (borrow * base) + tmp - b[i];
|
||||
result.push(minuend);
|
||||
}
|
||||
return bigInt(result, sign.positive);
|
||||
},
|
||||
minus: function (n, m) {
|
||||
return o.subtract(n, m);
|
||||
},
|
||||
multiply: function (n, m) {
|
||||
var s, first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m));
|
||||
else second = parse(n, first);
|
||||
s = first.sign !== second.sign;
|
||||
var a = first.value, b = second.value;
|
||||
var resultSum = [];
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
resultSum[i] = [];
|
||||
var j = i;
|
||||
while (j--) {
|
||||
resultSum[i].push(0);
|
||||
}
|
||||
}
|
||||
var carry = 0;
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
var x = a[i];
|
||||
for (var j = 0; j < b.length || carry > 0; j++) {
|
||||
var y = b[j];
|
||||
var product = y ? (x * y) + carry : carry;
|
||||
carry = product > base ? Math.floor(product / base) : 0;
|
||||
product -= carry * base;
|
||||
resultSum[i].push(product);
|
||||
}
|
||||
}
|
||||
var max = -1;
|
||||
for (var i = 0; i < resultSum.length; i++) {
|
||||
var len = resultSum[i].length;
|
||||
if (len > max) max = len;
|
||||
}
|
||||
var result = [], carry = 0;
|
||||
for (var i = 0; i < max || carry > 0; i++) {
|
||||
var sum = carry;
|
||||
for (var j = 0; j < resultSum.length; j++) {
|
||||
sum += resultSum[j][i] || 0;
|
||||
}
|
||||
carry = sum > base ? Math.floor(sum / base) : 0;
|
||||
sum -= carry * base;
|
||||
result.push(sum);
|
||||
}
|
||||
return bigInt(result, s);
|
||||
},
|
||||
times: function (n, m) {
|
||||
return o.multiply(n, m);
|
||||
},
|
||||
divmod: function (n, m) {
|
||||
var s, first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m));
|
||||
else second = parse(n, first);
|
||||
s = first.sign !== second.sign;
|
||||
if (bigInt(first.value, first.sign).equals(0)) return {
|
||||
quotient: bigInt([0], sign.positive),
|
||||
remainder: bigInt([0], sign.positive)
|
||||
};
|
||||
if (second.equals(0)) throw new Error("Cannot divide by zero");
|
||||
var a = first.value, b = second.value;
|
||||
var result = [], remainder = [];
|
||||
for (var i = a.length - 1; i >= 0; i--) {
|
||||
var n = [a[i]].concat(remainder);
|
||||
var quotient = goesInto(b, n);
|
||||
result.push(quotient.result);
|
||||
remainder = quotient.remainder;
|
||||
}
|
||||
result.reverse();
|
||||
return {
|
||||
quotient: bigInt(result, s),
|
||||
remainder: bigInt(remainder, first.sign)
|
||||
};
|
||||
},
|
||||
divide: function (n, m) {
|
||||
return o.divmod(n, m).quotient;
|
||||
},
|
||||
over: function (n, m) {
|
||||
return o.divide(n, m);
|
||||
},
|
||||
mod: function (n, m) {
|
||||
return o.divmod(n, m).remainder;
|
||||
},
|
||||
pow: function (n, m) {
|
||||
var first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m));
|
||||
else second = parse(n, first);
|
||||
var a = first, b = second;
|
||||
if (b.lesser(0)) return ZERO;
|
||||
if (b.equals(0)) return ONE;
|
||||
var result = bigInt(a.value, a.sign);
|
||||
|
||||
if (b.mod(2).equals(0)) {
|
||||
var c = result.pow(b.over(2));
|
||||
return c.times(c);
|
||||
} else {
|
||||
return result.times(result.pow(b.minus(1)));
|
||||
}
|
||||
},
|
||||
next: function (m) {
|
||||
var first = m || self;
|
||||
return o.add(first, 1);
|
||||
},
|
||||
prev: function (m) {
|
||||
var first = m || self;
|
||||
return o.subtract(first, 1);
|
||||
},
|
||||
compare: function (n, m) {
|
||||
var first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m, first));
|
||||
else second = parse(n, first);
|
||||
normalize(first, second);
|
||||
if (first.value.length === 1 && second.value.length === 1 && first.value[0] === 0 && second.value[0] === 0) return 0;
|
||||
if (second.sign !== first.sign) return first.sign === sign.positive ? 1 : -1;
|
||||
var multiplier = first.sign === sign.positive ? 1 : -1;
|
||||
var a = first.value, b = second.value;
|
||||
for (var i = a.length - 1; i >= 0; i--) {
|
||||
if (a[i] > b[i]) return 1 * multiplier;
|
||||
if (b[i] > a[i]) return -1 * multiplier;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
compareAbs: function (n, m) {
|
||||
var first = self, second;
|
||||
if (m) (first = parse(n)) && (second = parse(m, first));
|
||||
else second = parse(n, first);
|
||||
first.sign = second.sign = sign.positive;
|
||||
return o.compare(first, second);
|
||||
},
|
||||
equals: function (n, m) {
|
||||
return o.compare(n, m) === 0;
|
||||
},
|
||||
notEquals: function (n, m) {
|
||||
return !o.equals(n, m);
|
||||
},
|
||||
lesser: function (n, m) {
|
||||
return o.compare(n, m) < 0;
|
||||
},
|
||||
greater: function (n, m) {
|
||||
return o.compare(n, m) > 0;
|
||||
},
|
||||
greaterOrEquals: function (n, m) {
|
||||
return o.compare(n, m) >= 0;
|
||||
},
|
||||
lesserOrEquals: function (n, m) {
|
||||
return o.compare(n, m) <= 0;
|
||||
},
|
||||
isPositive: function (m) {
|
||||
var first = m || self;
|
||||
return first.sign === sign.positive;
|
||||
},
|
||||
isNegative: function (m) {
|
||||
var first = m || self;
|
||||
return first.sign === sign.negative;
|
||||
},
|
||||
isEven: function (m) {
|
||||
var first = m || self;
|
||||
return first.value[0] % 2 === 0;
|
||||
},
|
||||
isOdd: function (m) {
|
||||
var first = m || self;
|
||||
return first.value[0] % 2 === 1;
|
||||
},
|
||||
toString: function (m) {
|
||||
var first = m || self;
|
||||
var str = "", len = first.value.length;
|
||||
while (len--) {
|
||||
if (first.value[len].toString().length === 8) str += first.value[len];
|
||||
else str += (base.toString() + first.value[len]).slice(-logBase);
|
||||
}
|
||||
while (str[0] === "0") {
|
||||
str = str.slice(1);
|
||||
}
|
||||
if (!str.length) str = "0";
|
||||
var s = (first.sign === sign.positive || str == "0") ? "" : "-";
|
||||
return s + str;
|
||||
},
|
||||
toHex: function (m) {
|
||||
var first = m || self;
|
||||
var str = "";
|
||||
var l = this.abs();
|
||||
while (l > 0) {
|
||||
var qr = l.divmod(256);
|
||||
var b = qr.remainder.toJSNumber();
|
||||
str = (b >> 4).toString(16) + (b & 15).toString(16) + str;
|
||||
l = qr.quotient;
|
||||
}
|
||||
return (this.isNegative() ? "-" : "") + "0x" + str;
|
||||
},
|
||||
toJSNumber: function (m) {
|
||||
return +o.toString(m);
|
||||
},
|
||||
valueOf: function (m) {
|
||||
return o.toJSNumber(m);
|
||||
}
|
||||
};
|
||||
return o;
|
||||
};
|
||||
|
||||
var ZERO = bigInt([0], sign.positive);
|
||||
var ONE = bigInt([1], sign.positive);
|
||||
var MINUS_ONE = bigInt([1], sign.negative);
|
||||
|
||||
var fnReturn = function (a) {
|
||||
if (typeof a === "undefined") return ZERO;
|
||||
return parse(a);
|
||||
};
|
||||
fnReturn.zero = ZERO;
|
||||
fnReturn.one = ONE;
|
||||
fnReturn.minusOne = MINUS_ONE;
|
||||
return fnReturn;
|
||||
})();
|
||||
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = bigInt;
|
||||
}
|
||||
|
142
ethereal/assets/ext/ethereum.js
Normal file
@ -0,0 +1,142 @@
|
||||
// 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);
|
||||
},
|
||||
|
||||
getStateKeyVals: function(address, cb){
|
||||
postData({call: "getStateKeyVals", args: [address]}, cb);
|
||||
},
|
||||
|
||||
getKey: function(cb) {
|
||||
postData({call: "getKey"}, cb);
|
||||
},
|
||||
|
||||
getTxCountAt: function(address, cb) {
|
||||
postData({call: "getTxCountAt", args: [address]}, cb);
|
||||
},
|
||||
getIsMining: function(cb){
|
||||
postData({call: "getIsMining"}, cb)
|
||||
},
|
||||
getIsListening: function(cb){
|
||||
postData({call: "getIsListening"}, cb)
|
||||
},
|
||||
getCoinBase: function(cb){
|
||||
postData({call: "getCoinBase"}, cb);
|
||||
},
|
||||
getPeerCount: function(cb){
|
||||
postData({call: "getPeerCount"}, cb);
|
||||
},
|
||||
getBalanceAt: function(address, cb) {
|
||||
postData({call: "getBalance", args: [address]}, cb);
|
||||
},
|
||||
|
||||
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 = {}
|
||||
|
58
ethereal/assets/ext/pre.js
Normal file
@ -0,0 +1,58 @@
|
||||
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.onerror = function(message, file, lineNumber, column, errorObj) {
|
||||
debug(file, message, lineNumber+":"+column, errorObj);
|
||||
|
||||
return false;
|
||||
}
|
58
ethereal/assets/ext/string.js
Normal file
@ -0,0 +1,58 @@
|
||||
String.prototype.pad = function(l, r) {
|
||||
if (r === undefined) {
|
||||
r = l
|
||||
if (!(this.substr(0, 2) == "0x" || /^\d+$/.test(this)))
|
||||
l = 0
|
||||
}
|
||||
var ret = this.bin();
|
||||
while (ret.length < l)
|
||||
ret = "\0" + ret
|
||||
while (ret.length < r)
|
||||
ret = ret + "\0"
|
||||
return ret;
|
||||
}
|
||||
|
||||
String.prototype.unpad = function() {
|
||||
var i = this.length;
|
||||
while (i && this[i - 1] == "\0")
|
||||
--i
|
||||
return this.substr(0, i)
|
||||
}
|
||||
|
||||
String.prototype.bin = function() {
|
||||
if (this.substr(0, 2) == "0x") {
|
||||
bytes = []
|
||||
var i = 2;
|
||||
|
||||
// Check if it's odd - pad with a zero if so.
|
||||
if (this.length % 2)
|
||||
bytes.push(parseInt(this.substr(i++, 1), 16))
|
||||
|
||||
for (; i < this.length - 1; i += 2)
|
||||
bytes.push(parseInt(this.substr(i, 2), 16));
|
||||
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
} else if (/^\d+$/.test(this))
|
||||
return bigInt(this.substr(0)).toHex().bin()
|
||||
|
||||
// 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.dec = function() {
|
||||
return bigInt(this.substr(0)).toString()
|
||||
}
|
||||
|
||||
String.prototype.hex = function() {
|
||||
return bigInt(this.substr(0)).toHex()
|
||||
}
|
BIN
ethereal/assets/facet.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
ethereal/assets/heart.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
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
@ -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
@ -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
@ -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
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
@ -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
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
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 = lib.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 = lib.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
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
1067
ethereal/assets/qml/wallet.qml
Normal file
240
ethereal/assets/qml/webapp.qml
Normal file
@ -0,0 +1,240 @@
|
||||
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("ext/pre.js"), ui.assetPath("ext/big.js"), ui.assetPath("ext/string.js"), ui.assetPath("ext/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 "getCoinBase":
|
||||
postData(data._seed, eth.getCoinBase())
|
||||
|
||||
break
|
||||
case "getIsListening":
|
||||
postData(data._seed, eth.getIsListening())
|
||||
|
||||
break
|
||||
case "getIsMining":
|
||||
postData(data._seed, eth.getIsMining())
|
||||
|
||||
break
|
||||
case "getPeerCount":
|
||||
postData(data._seed, eth.getPeerCount())
|
||||
|
||||
break
|
||||
|
||||
case "getTxCountAt":
|
||||
require(1)
|
||||
postData(data._seed, eth.getTxCountAt(data.args[0]))
|
||||
|
||||
break
|
||||
case "getBlockByNumber":
|
||||
var block = eth.getBlock(data.args[0])
|
||||
postData(data._seed, block)
|
||||
|
||||
break
|
||||
case "getBlockByHash":
|
||||
var block = eth.getBlock(data.args[0])
|
||||
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 "getStateKeyVals":
|
||||
require(1);
|
||||
var stateObject = eth.getStateObject(data.args[0]).stateKeyVal(true)
|
||||
postData(data._seed,stateObject)
|
||||
|
||||
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":
|
||||
console.log("'Set' has been depcrecated")
|
||||
/*
|
||||
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: toggleInspector
|
||||
color: "#bcbcbc"
|
||||
visible: true
|
||||
height: 12
|
||||
width: 12
|
||||
anchors {
|
||||
right: root.right
|
||||
}
|
||||
MouseArea {
|
||||
onClicked: {
|
||||
if(inspector.visible == true){
|
||||
inspector.visible = false
|
||||
}else{
|
||||
inspector.visible = true
|
||||
}
|
||||
}
|
||||
onDoubleClicked: {
|
||||
console.log('refreshing')
|
||||
webView.reload()
|
||||
}
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
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.min.css
vendored
Executable file
BIN
ethereal/assets/samplecoin/icon.png
Normal file
After Width: | Height: | Size: 85 KiB |
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;
|
||||
}
|
68
ethereal/assets/samplecoin/samplecoin.html
Normal file
@ -0,0 +1,68 @@
|
||||
<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">
|
||||
<meta name="viewport" content="minimum-scale=1; maximum-scale=1; initial-scale=1;">
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var jefcoinAddr = "22fa3ebce6ef9ca661a960104d3087eec040011e"
|
||||
var mAddr = ""
|
||||
|
||||
function createTransaction() {
|
||||
var addr = ("0x" + document.querySelector("#addr").value).pad(32);
|
||||
var amount = document.querySelector("#amount").value.pad(32);
|
||||
|
||||
var data = (addr + amount).unbin();
|
||||
eth.transact(mAddr, jefcoinAddr, 0, "50000", "1000000", data, function(receipt) {
|
||||
debug("received tx hash:", reciept.address)
|
||||
})
|
||||
}
|
||||
|
||||
function init() {
|
||||
eth.getKey(function(sec) {
|
||||
mAddr = sec;
|
||||
eth.getSecretToAddress(sec, function(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>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
43
ethereal/assets/util/test.html
Normal file
@ -0,0 +1,43 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Utils</title>
|
||||
</head>
|
||||
<body onload="init();">
|
||||
<label>Nonce for 2ef47100e0787b915105fd5e3f4ff6752079d5cb</label>
|
||||
<p id="nonce"></p>
|
||||
|
||||
<label>Connected peers</label>
|
||||
<p id="peers"></p>
|
||||
|
||||
<label>Is mining</label>
|
||||
<p id="isMining"></p>
|
||||
|
||||
<label>Is listening</label>
|
||||
<p id="isListen"></p>
|
||||
|
||||
<label>Coinbase</label>
|
||||
<p id="coinbase"></p>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function init() {
|
||||
eth.getTxCountAt("2ef47100e0787b915105fd5e3f4ff6752079d5cb", function(nonce){
|
||||
document.querySelector("#nonce").innerHTML = nonce;
|
||||
})
|
||||
eth.getPeerCount(function(peerLength){
|
||||
document.querySelector("#peers").innerHTML = peerLength;
|
||||
})
|
||||
eth.getIsMining(function(mining){
|
||||
document.querySelector("#isMining").innerHTML = mining;
|
||||
})
|
||||
eth.getIsListening(function(listen){
|
||||
document.querySelector("#isListen").innerHTML = listen;
|
||||
})
|
||||
eth.getCoinBase(function(address){
|
||||
document.querySelector("#coinbase").innerHTML = address;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
43
ethereal/config.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
var Identifier string
|
||||
|
||||
//var StartMining bool
|
||||
var StartRpc bool
|
||||
var RpcPort int
|
||||
var UseUPnP bool
|
||||
var OutboundPort string
|
||||
var ShowGenesis bool
|
||||
var AddPeer string
|
||||
var MaxPeer int
|
||||
var GenAddr bool
|
||||
var UseSeed bool
|
||||
var ImportKey string
|
||||
var ExportKey bool
|
||||
var AssetPath string
|
||||
|
||||
var Datadir string
|
||||
|
||||
func Init() {
|
||||
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.StringVar(&AssetPath, "asset_path", "", "absolute path to GUI assets directory")
|
||||
|
||||
flag.BoolVar(&ShowGenesis, "genesis", false, "prints genesis header and exits")
|
||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||
flag.BoolVar(&ExportKey, "export", false, "export private key")
|
||||
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
|
||||
|
||||
flag.StringVar(&Datadir, "datadir", ".ethereal", "specifies the datadir to use. Takes precedence over config file.")
|
||||
|
||||
flag.Parse()
|
||||
}
|
142
ethereal/ethereum.go
Normal file
@ -0,0 +1,142 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/go-ethereum/ethereal/ui"
|
||||
"github.com/ethereum/go-ethereum/utils"
|
||||
"github.com/go-qml/qml"
|
||||
"github.com/rakyll/globalconf"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
const Debug = true
|
||||
|
||||
// Register interrupt handlers so we can stop the ethereum
|
||||
func RegisterInterupts(s *eth.Ethereum) {
|
||||
// Buffered chan of one is enough
|
||||
c := make(chan os.Signal, 1)
|
||||
// Notify about interrupts for now
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
fmt.Printf("Shutting down (%v) ... \n", sig)
|
||||
|
||||
s.Stop()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
Init()
|
||||
|
||||
qml.Init(nil)
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
g, err := globalconf.NewWithOptions(&globalconf.Options{
|
||||
Filename: path.Join(ethutil.ApplicationFolder(Datadir), "conf.ini"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
g.ParseAll()
|
||||
}
|
||||
ethutil.ReadConfig(Datadir, ethutil.LogFile|ethutil.LogStd, g, Identifier)
|
||||
|
||||
// Instantiated a eth stack
|
||||
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
|
||||
if err != nil {
|
||||
log.Println("eth start err:", err)
|
||||
return
|
||||
}
|
||||
ethereum.Port = OutboundPort
|
||||
|
||||
if GenAddr {
|
||||
fmt.Println("This action overwrites your old private key. 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)
|
||||
}
|
||||
}
|
||||
|
||||
if r == "y" {
|
||||
utils.CreateKeyPair(true)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else {
|
||||
if len(ImportKey) > 0 {
|
||||
fmt.Println("This action overwrites your old private key. 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)
|
||||
}
|
||||
}
|
||||
|
||||
if r == "y" {
|
||||
utils.ImportPrivateKey(ImportKey)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ExportKey {
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
save these words so you can restore your account later: %s
|
||||
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey)
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if ShowGenesis {
|
||||
fmt.Println(ethereum.BlockChain().Genesis())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
/*
|
||||
if StartMining {
|
||||
utils.DoMining(ethereum)
|
||||
}
|
||||
*/
|
||||
|
||||
if StartRpc {
|
||||
utils.DoRpc(ethereum, RpcPort)
|
||||
}
|
||||
|
||||
log.Printf("Starting Ethereum GUI v%s\n", ethutil.Config.Ver)
|
||||
|
||||
// Set the max peers
|
||||
ethereum.MaxPeers = MaxPeer
|
||||
|
||||
gui := ethui.New(ethereum)
|
||||
|
||||
ethereum.Start(UseSeed)
|
||||
|
||||
gui.Start(AssetPath)
|
||||
|
||||
// Wait for shutdown
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
226
ethereal/ui/debugger.go
Normal file
@ -0,0 +1,226 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/go-qml/qml"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DebuggerWindow struct {
|
||||
win *qml.Window
|
||||
engine *qml.Engine
|
||||
lib *UiLib
|
||||
Db *Debugger
|
||||
}
|
||||
|
||||
func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
||||
engine := qml.NewEngine()
|
||||
component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
win := component.CreateWindow(nil)
|
||||
db := &Debugger{win, make(chan bool), make(chan bool), true, false}
|
||||
|
||||
return &DebuggerWindow{engine: engine, win: win, lib: lib, Db: db}
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) Show() {
|
||||
context := self.engine.Context()
|
||||
context.SetVar("dbg", self)
|
||||
|
||||
go func() {
|
||||
self.win.Show()
|
||||
self.win.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) SetCode(code string) {
|
||||
self.win.Set("codeText", code)
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) SetData(data string) {
|
||||
self.win.Set("dataText", data)
|
||||
}
|
||||
func (self *DebuggerWindow) SetAsm(data string) {
|
||||
dis := ethchain.Disassemble(ethutil.FromHex(data))
|
||||
for _, str := range dis {
|
||||
self.win.Root().Call("setAsm", str)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
|
||||
if !self.Db.done {
|
||||
self.Db.Q <- true
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
self.Logf("compile FAULT: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) {
|
||||
slice := strings.Split(dataStr, "\n")
|
||||
for _, dataItem := range slice {
|
||||
d := ethutil.FormatData(dataItem)
|
||||
ret = append(ret, d...)
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
var err error
|
||||
script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
|
||||
ret, err = ethutil.Compile(s)
|
||||
return
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
self.Logln(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
dis := ethchain.Disassemble(script)
|
||||
self.win.Root().Call("clearAsm")
|
||||
self.win.Root().Call("clearLog")
|
||||
|
||||
for _, str := range dis {
|
||||
self.win.Root().Call("setAsm", str)
|
||||
}
|
||||
|
||||
gas := ethutil.Big(gasStr)
|
||||
gasPrice := ethutil.Big(gasPriceStr)
|
||||
// Contract addr as test address
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), gas, gasPrice, script)
|
||||
callerTx.Sign(keyPair.PrivateKey)
|
||||
|
||||
state := self.lib.eth.BlockChain().CurrentBlock.State()
|
||||
account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
|
||||
contract := ethchain.MakeContract(callerTx, state)
|
||||
callerClosure := ethchain.NewClosure(account, contract, script, state, gas, gasPrice)
|
||||
|
||||
block := self.lib.eth.BlockChain().CurrentBlock
|
||||
vm := ethchain.NewVm(state, self.lib.eth.StateManager(), ethchain.RuntimeVars{
|
||||
Block: block,
|
||||
Origin: account.Address(),
|
||||
BlockNumber: block.Number,
|
||||
PrevHash: block.PrevHash,
|
||||
Coinbase: block.Coinbase,
|
||||
Time: block.Time,
|
||||
Diff: block.Difficulty,
|
||||
Value: ethutil.Big(valueStr),
|
||||
})
|
||||
vm.Verbose = true
|
||||
|
||||
self.Db.done = false
|
||||
self.Logf("callsize %d", len(script))
|
||||
go func() {
|
||||
ret, g, err := callerClosure.Call(vm, data, self.Db.halting)
|
||||
tot := new(big.Int).Mul(g, gasPrice)
|
||||
self.Logf("gas usage %v total price = %v (%v)", g, tot, ethutil.CurrencyToString(tot))
|
||||
if err != nil {
|
||||
self.Logln("exited with errors:", err)
|
||||
} else {
|
||||
if len(ret) > 0 {
|
||||
self.Logf("exited: % x", ret)
|
||||
} else {
|
||||
self.Logf("exited: nil")
|
||||
}
|
||||
}
|
||||
|
||||
state.Reset()
|
||||
|
||||
if !self.Db.interrupt {
|
||||
self.Db.done = true
|
||||
} else {
|
||||
self.Db.interrupt = false
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) Logf(format string, v ...interface{}) {
|
||||
self.win.Root().Call("setLog", fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) Logln(v ...interface{}) {
|
||||
str := fmt.Sprintln(v...)
|
||||
self.Logf("%s", str[:len(str)-1])
|
||||
}
|
||||
|
||||
func (self *DebuggerWindow) Next() {
|
||||
self.Db.Next()
|
||||
}
|
||||
|
||||
type Debugger struct {
|
||||
win *qml.Window
|
||||
N chan bool
|
||||
Q chan bool
|
||||
done, interrupt bool
|
||||
}
|
||||
|
||||
type storeVal struct {
|
||||
Key, Value string
|
||||
}
|
||||
|
||||
func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack, stateObject *ethchain.StateObject) bool {
|
||||
d.win.Root().Call("setInstruction", pc)
|
||||
d.win.Root().Call("clearMem")
|
||||
d.win.Root().Call("clearStack")
|
||||
d.win.Root().Call("clearStorage")
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
stateObject.State().EachStorage(func(key string, node *ethutil.Value) {
|
||||
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
|
||||
})
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-d.N:
|
||||
break out
|
||||
case <-d.Q:
|
||||
d.interrupt = true
|
||||
d.clearBuffers()
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *Debugger) clearBuffers() {
|
||||
out:
|
||||
// drain
|
||||
for {
|
||||
select {
|
||||
case <-d.N:
|
||||
case <-d.Q:
|
||||
default:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Debugger) Next() {
|
||||
if !d.done {
|
||||
d.N <- true
|
||||
}
|
||||
}
|
132
ethereal/ui/ext_app.go
Normal file
@ -0,0 +1,132 @@
|
||||
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)
|
||||
NewWatcher(chan bool)
|
||||
}
|
||||
|
||||
type ExtApplication struct {
|
||||
*ethpub.PEthereum
|
||||
|
||||
blockChan chan ethutil.React
|
||||
changeChan chan ethutil.React
|
||||
quitChan chan bool
|
||||
watcherQuitChan chan bool
|
||||
|
||||
container AppContainer
|
||||
lib *UiLib
|
||||
registeredEvents []string
|
||||
}
|
||||
|
||||
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
|
||||
app := &ExtApplication{
|
||||
ethpub.NewPEthereum(lib.eth),
|
||||
make(chan ethutil.React, 1),
|
||||
make(chan ethutil.React, 1),
|
||||
make(chan bool),
|
||||
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)
|
||||
|
||||
app.container.NewWatcher(app.watcherQuitChan)
|
||||
|
||||
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
|
||||
app.watcherQuitChan <- 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)
|
||||
}
|
362
ethereal/ui/gui.go
Normal file
@ -0,0 +1,362 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/go-ethereum/utils"
|
||||
"github.com/go-qml/qml"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Gui struct {
|
||||
// The main application window
|
||||
win *qml.Window
|
||||
// QML Engine
|
||||
engine *qml.Engine
|
||||
component *qml.Common
|
||||
// The ethereum interface
|
||||
eth *eth.Ethereum
|
||||
|
||||
// The public Ethereum library
|
||||
lib *EthLib
|
||||
uiLib *UiLib
|
||||
|
||||
txDb *ethdb.LDBDatabase
|
||||
|
||||
addr []byte
|
||||
|
||||
pub *ethpub.PEthereum
|
||||
}
|
||||
|
||||
// Create GUI, but doesn't start it
|
||||
func New(ethereum *eth.Ethereum) *Gui {
|
||||
lib := &EthLib{stateManager: ethereum.StateManager(), blockChain: ethereum.BlockChain(), txPool: ethereum.TxPool()}
|
||||
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 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 ethutil.GetKeyRing().Len() != 0 {
|
||||
addr = ethutil.GetKeyRing().Get(0).Address()
|
||||
}
|
||||
|
||||
pub := ethpub.NewPEthereum(ethereum)
|
||||
|
||||
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr, pub: pub}
|
||||
}
|
||||
|
||||
func (gui *Gui) Start(assetPath string) {
|
||||
const version = "0.5.0 RC14"
|
||||
|
||||
defer gui.txDb.Close()
|
||||
|
||||
// Register ethereum functions
|
||||
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||
Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
||||
}, {
|
||||
Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
|
||||
}, {
|
||||
Init: func(p *ethpub.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
|
||||
}})
|
||||
|
||||
ethutil.Config.SetClientString("Ethereal")
|
||||
|
||||
// Create a new QML engine
|
||||
gui.engine = qml.NewEngine()
|
||||
context := gui.engine.Context()
|
||||
|
||||
// Expose the eth library and the ui library to QML
|
||||
context.SetVar("eth", gui)
|
||||
context.SetVar("pub", gui.pub)
|
||||
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
|
||||
context.SetVar("ui", gui.uiLib)
|
||||
|
||||
// Load the main QML interface
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
|
||||
var win *qml.Window
|
||||
var err error
|
||||
if len(data) == 0 {
|
||||
win, err = gui.showKeyImport(context)
|
||||
} else {
|
||||
win, err = gui.showWallet(context)
|
||||
|
||||
ethutil.Config.Log.AddLogSystem(gui)
|
||||
}
|
||||
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'", err)
|
||||
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
|
||||
|
||||
win.Show()
|
||||
win.Wait()
|
||||
|
||||
gui.eth.Stop()
|
||||
}
|
||||
|
||||
func (gui *Gui) ToggleMining() {
|
||||
var txt string
|
||||
if gui.eth.Mining {
|
||||
utils.StopMining(gui.eth)
|
||||
txt = "Start mining"
|
||||
} else {
|
||||
utils.StartMining(gui.eth)
|
||||
txt = "Stop mining"
|
||||
}
|
||||
|
||||
gui.win.Root().Set("miningButtonText", txt)
|
||||
}
|
||||
|
||||
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
|
||||
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
win := gui.createWindow(component)
|
||||
|
||||
gui.setInitialBlockChain()
|
||||
gui.loadAddressBook()
|
||||
gui.readPreviousTransactions()
|
||||
gui.setPeerInfo()
|
||||
|
||||
go gui.update()
|
||||
|
||||
return win, nil
|
||||
}
|
||||
|
||||
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
|
||||
context.SetVar("lib", gui.lib)
|
||||
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gui.createWindow(component), nil
|
||||
}
|
||||
|
||||
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
|
||||
win := comp.CreateWindow(nil)
|
||||
|
||||
gui.win = win
|
||||
gui.uiLib.win = win
|
||||
|
||||
db := &Debugger{gui.win, make(chan bool), make(chan bool), true, false}
|
||||
gui.lib.Db = db
|
||||
gui.uiLib.Db = db
|
||||
|
||||
return gui.win
|
||||
}
|
||||
func (gui *Gui) setInitialBlockChain() {
|
||||
sBlk := gui.eth.BlockChain().LastBlockHash
|
||||
blk := gui.eth.BlockChain().GetBlock(sBlk)
|
||||
for ; blk != nil; blk = gui.eth.BlockChain().GetBlock(sBlk) {
|
||||
sBlk = blk.PrevHash
|
||||
|
||||
// Loop through all transactions to see if we missed any while being offline
|
||||
for _, tx := range blk.Transactions() {
|
||||
if bytes.Compare(tx.Sender(), gui.addr) == 0 || bytes.Compare(tx.Recipient, gui.addr) == 0 {
|
||||
if ok, _ := gui.txDb.Get(tx.Hash()); ok == nil {
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
gui.processBlock(blk, true)
|
||||
}
|
||||
}
|
||||
|
||||
type address struct {
|
||||
Name, Address string
|
||||
}
|
||||
|
||||
var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010")
|
||||
|
||||
func (gui *Gui) loadAddressBook() {
|
||||
gui.win.Root().Call("clearAddress")
|
||||
stateObject := gui.eth.StateManager().CurrentState().GetStateObject(namereg)
|
||||
if stateObject != nil {
|
||||
stateObject.State().EachStorage(func(name string, value *ethutil.Value) {
|
||||
gui.win.Root().Call("addAddress", struct{ Name, Address string }{name, ethutil.Hex(value.Bytes())})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) readPreviousTransactions() {
|
||||
it := gui.txDb.Db().NewIterator(nil, nil)
|
||||
for it.Next() {
|
||||
tx := ethchain.NewTransactionFromBytes(it.Value())
|
||||
|
||||
var inout string
|
||||
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
|
||||
inout = "send"
|
||||
} else {
|
||||
inout = "recv"
|
||||
}
|
||||
|
||||
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), inout)
|
||||
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
func (gui *Gui) processBlock(block *ethchain.Block, initial bool) {
|
||||
gui.win.Root().Call("addBlock", ethpub.NewPBlock(block), initial)
|
||||
}
|
||||
|
||||
func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
|
||||
var str string
|
||||
if unconfirmedFunds != nil {
|
||||
pos := "+"
|
||||
if unconfirmedFunds.Cmp(big.NewInt(0)) < 0 {
|
||||
pos = "-"
|
||||
}
|
||||
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
|
||||
str = fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(amount), pos, val)
|
||||
} else {
|
||||
str = fmt.Sprintf("%v", ethutil.CurrencyToString(amount))
|
||||
}
|
||||
|
||||
gui.win.Root().Call("setWalletValue", str)
|
||||
}
|
||||
|
||||
// Simple go routine function that updates the list of peers in the GUI
|
||||
func (gui *Gui) update() {
|
||||
reactor := gui.eth.Reactor()
|
||||
|
||||
blockChan := make(chan ethutil.React, 1)
|
||||
txChan := make(chan ethutil.React, 1)
|
||||
objectChan := make(chan ethutil.React, 1)
|
||||
peerChan := make(chan ethutil.React, 1)
|
||||
|
||||
reactor.Subscribe("newBlock", blockChan)
|
||||
reactor.Subscribe("newTx:pre", txChan)
|
||||
reactor.Subscribe("newTx:post", txChan)
|
||||
reactor.Subscribe("object:"+string(namereg), objectChan)
|
||||
reactor.Subscribe("peerList", peerChan)
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
|
||||
state := gui.eth.StateManager().TransState()
|
||||
|
||||
unconfirmedFunds := new(big.Int)
|
||||
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.addr).Amount)))
|
||||
|
||||
for {
|
||||
select {
|
||||
case b := <-blockChan:
|
||||
block := b.Resource.(*ethchain.Block)
|
||||
gui.processBlock(block, false)
|
||||
if bytes.Compare(block.Coinbase, gui.addr) == 0 {
|
||||
gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.addr).Amount, nil)
|
||||
}
|
||||
|
||||
case txMsg := <-txChan:
|
||||
tx := txMsg.Resource.(*ethchain.Transaction)
|
||||
|
||||
if txMsg.Event == "newTx:pre" {
|
||||
object := state.GetAccount(gui.addr)
|
||||
|
||||
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
|
||||
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "send")
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
|
||||
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
|
||||
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
|
||||
gui.win.Root().Call("addTx", ethpub.NewPTx(tx), "recv")
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
|
||||
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Amount, unconfirmedFunds)
|
||||
} else {
|
||||
object := state.GetAccount(gui.addr)
|
||||
if bytes.Compare(tx.Sender(), gui.addr) == 0 {
|
||||
object.SubAmount(tx.Value)
|
||||
} else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
|
||||
object.AddAmount(tx.Value)
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Amount, nil)
|
||||
|
||||
state.UpdateStateObject(object)
|
||||
}
|
||||
case <-objectChan:
|
||||
gui.loadAddressBook()
|
||||
case <-peerChan:
|
||||
gui.setPeerInfo()
|
||||
case <-ticker.C:
|
||||
gui.setPeerInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) setPeerInfo() {
|
||||
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
|
||||
|
||||
gui.win.Root().Call("resetPeers")
|
||||
for _, peer := range gui.pub.GetPeers() {
|
||||
gui.win.Root().Call("addPeer", peer)
|
||||
}
|
||||
}
|
||||
|
||||
// Logging functions that log directly to the GUI interface
|
||||
func (gui *Gui) Println(v ...interface{}) {
|
||||
str := strings.TrimRight(fmt.Sprintln(v...), "\n")
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
gui.win.Root().Call("addLog", line)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) Printf(format string, v ...interface{}) {
|
||||
str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
gui.win.Root().Call("addLog", line)
|
||||
}
|
||||
}
|
||||
func (gui *Gui) RegisterName(name string) {
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
name = fmt.Sprintf("\"%s\"\n1", name)
|
||||
gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), "namereg", "1000", "1000000", "150", name)
|
||||
}
|
||||
|
||||
func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
|
||||
return gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), recipient, value, gas, gasPrice, data)
|
||||
}
|
||||
|
||||
func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
|
||||
return gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), recipient, value, gas, gasPrice, data)
|
||||
}
|
||||
|
||||
func (gui *Gui) ChangeClientId(id string) {
|
||||
ethutil.Config.SetIdentifier(id)
|
||||
}
|
||||
|
||||
func (gui *Gui) ClientId() string {
|
||||
return ethutil.Config.Identifier
|
||||
}
|
||||
|
||||
func (gui *Gui) SetLogLevel(level int) {
|
||||
ethutil.Config.Log.SetLevel(level)
|
||||
}
|
133
ethereal/ui/html_container.go
Normal file
@ -0,0 +1,133 @@
|
||||
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"
|
||||
"github.com/howeyc/fsnotify"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type HtmlApplication struct {
|
||||
win *qml.Window
|
||||
webView qml.Object
|
||||
engine *qml.Engine
|
||||
lib *UiLib
|
||||
path string
|
||||
watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
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) RootFolder() string {
|
||||
folder, err := url.Parse(app.path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path.Dir(folder.RequestURI())
|
||||
}
|
||||
func (app *HtmlApplication) RecursiveFolders() []os.FileInfo {
|
||||
files, _ := ioutil.ReadDir(app.RootFolder())
|
||||
var folders []os.FileInfo
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
folders = append(folders, file)
|
||||
}
|
||||
}
|
||||
return folders
|
||||
}
|
||||
|
||||
func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
|
||||
var err error
|
||||
|
||||
app.watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = app.watcher.Watch(app.RootFolder())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, folder := range app.RecursiveFolders() {
|
||||
fullPath := app.RootFolder() + "/" + folder.Name()
|
||||
app.watcher.Watch(fullPath)
|
||||
}
|
||||
|
||||
go func() {
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-quitChan:
|
||||
app.watcher.Close()
|
||||
break out
|
||||
case <-app.watcher.Event:
|
||||
//ethutil.Config.Log.Debugln("Got event:", ev)
|
||||
app.webView.Call("reload")
|
||||
case err := <-app.watcher.Error:
|
||||
// TODO: Do something here
|
||||
ethutil.Config.Log.Infoln("Watcher error:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
46
ethereal/ui/library.go
Normal file
@ -0,0 +1,46 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/go-ethereum/utils"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type EthLib struct {
|
||||
stateManager *ethchain.StateManager
|
||||
blockChain *ethchain.BlockChain
|
||||
txPool *ethchain.TxPool
|
||||
Db *Debugger
|
||||
}
|
||||
|
||||
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) {
|
||||
_, prv := secp256k1.GenerateKeyPair()
|
||||
keyPair, err := ethutil.GetKeyRing().NewKeyPair(prv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
|
||||
mnemonicString := strings.Join(mne, " ")
|
||||
return mnemonicString, fmt.Sprintf("%x", keyPair.Address()), ethutil.Hex(keyPair.PrivateKey), ethutil.Hex(keyPair.PublicKey)
|
||||
}
|
139
ethereal/ui/ui_lib.go
Normal file
@ -0,0 +1,139 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/go-qml/qml"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type memAddr struct {
|
||||
Num string
|
||||
Value string
|
||||
}
|
||||
|
||||
// UI Library that has some basic functionality exposed
|
||||
type UiLib struct {
|
||||
engine *qml.Engine
|
||||
eth *eth.Ethereum
|
||||
connected bool
|
||||
assetPath string
|
||||
// The main application window
|
||||
win *qml.Window
|
||||
Db *Debugger
|
||||
DbWindow *DebuggerWindow
|
||||
}
|
||||
|
||||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||
if assetPath == "" {
|
||||
assetPath = DefaultAssetPath()
|
||||
}
|
||||
return &UiLib{engine: engine, eth: eth, assetPath: assetPath}
|
||||
}
|
||||
|
||||
// Opens a QML file (external application)
|
||||
func (ui *UiLib) Open(path string) {
|
||||
component, err := ui.engine.LoadFile(path[7:])
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Debugln(err)
|
||||
}
|
||||
win := component.CreateWindow(nil)
|
||||
|
||||
go func() {
|
||||
win.Show()
|
||||
win.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (ui *UiLib) OpenHtml(path string) {
|
||||
container := NewHtmlApplication(path, ui)
|
||||
app := NewExtApplication(container, ui)
|
||||
|
||||
go app.run()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
win.Show()
|
||||
win.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (ui *UiLib) Connect(button qml.Object) {
|
||||
if !ui.connected {
|
||||
ui.eth.Start(true)
|
||||
ui.connected = true
|
||||
button.Set("enabled", false)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||
ui.eth.ConnectToPeer(addr)
|
||||
}
|
||||
|
||||
func (ui *UiLib) AssetPath(p string) string {
|
||||
return path.Join(ui.assetPath, p)
|
||||
}
|
||||
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
object := self.eth.StateManager().CurrentState().GetStateObject(ethutil.FromHex(contractHash))
|
||||
if len(object.Script()) > 0 {
|
||||
dbWindow.SetCode("0x" + ethutil.Hex(object.Script()))
|
||||
}
|
||||
dbWindow.SetData("0x" + data)
|
||||
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func (self *UiLib) StartDbWithCode(code string) {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
dbWindow.SetCode("0x" + code)
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func (self *UiLib) StartDebugger() {
|
||||
dbWindow := NewDebuggerWindow(self)
|
||||
//self.DbWindow = dbWindow
|
||||
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func DefaultAssetPath() string {
|
||||
var base string
|
||||
// If the current working directory is the go-ethereum dir
|
||||
// assume a debug build and use the source directory as
|
||||
// asset directory.
|
||||
pwd, _ := os.Getwd()
|
||||
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
|
||||
base = path.Join(pwd, "assets")
|
||||
} else {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
// Get Binary Directory
|
||||
exedir, _ := osext.ExecutableFolder()
|
||||
base = filepath.Join(exedir, "../Resources")
|
||||
case "linux":
|
||||
base = "/usr/share/ethereal"
|
||||
case "window":
|
||||
fallthrough
|
||||
default:
|
||||
base = "."
|
||||
}
|
||||
}
|
||||
|
||||
return base
|
||||
}
|
210
ethereum.go
@ -1,210 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"github.com/ethereum/go-ethereum/ui"
|
||||
"github.com/niemeyer/qml"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
const Debug = true
|
||||
|
||||
// Register interrupt handlers so we can stop the ethereum
|
||||
func RegisterInterupts(s *eth.Ethereum) {
|
||||
// Buffered chan of one is enough
|
||||
c := make(chan os.Signal, 1)
|
||||
// Notify about interrupts for now
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
fmt.Printf("Shutting down (%v) ... \n", sig)
|
||||
|
||||
s.Stop()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func CreateKeyPair(force bool) {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
if len(data) == 0 || force {
|
||||
pub, prv := secp256k1.GenerateKeyPair()
|
||||
pair := ðutil.Key{PrivateKey: prv, PublicKey: pub}
|
||||
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
||||
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
`, pair.Address(), prv, pub)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ImportPrivateKey(prvKey string) {
|
||||
key := ethutil.FromHex(prvKey)
|
||||
msg := []byte("tmp")
|
||||
// Couldn't think of a better way to get the pub key
|
||||
sig, _ := secp256k1.Sign(msg, key)
|
||||
pub, _ := secp256k1.RecoverPubkey(msg, sig)
|
||||
pair := ðutil.Key{PrivateKey: key, PublicKey: pub}
|
||||
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
||||
|
||||
fmt.Printf(`
|
||||
Importing private key
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
`, pair.Address(), key, pub)
|
||||
}
|
||||
|
||||
func main() {
|
||||
Init()
|
||||
|
||||
// Qt has to be initialized in the main thread or it will throw errors
|
||||
// It has to be called BEFORE setting the maximum procs.
|
||||
if UseGui {
|
||||
qml.Init(nil)
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
ethchain.InitFees()
|
||||
ethutil.ReadConfig(DataDir)
|
||||
ethutil.Config.Seed = UseSeed
|
||||
|
||||
// Instantiated a eth stack
|
||||
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
|
||||
if err != nil {
|
||||
log.Println("eth start err:", err)
|
||||
return
|
||||
}
|
||||
ethereum.Port = OutboundPort
|
||||
|
||||
if GenAddr {
|
||||
fmt.Println("This action overwrites your old private key. 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)
|
||||
}
|
||||
}
|
||||
|
||||
if r == "y" {
|
||||
CreateKeyPair(true)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else {
|
||||
if len(ImportKey) > 0 {
|
||||
fmt.Println("This action overwrites your old private key. 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)
|
||||
}
|
||||
}
|
||||
|
||||
if r == "y" {
|
||||
ImportPrivateKey(ImportKey)
|
||||
os.Exit(0)
|
||||
}
|
||||
} else {
|
||||
CreateKeyPair(false)
|
||||
}
|
||||
}
|
||||
|
||||
if ExportKey {
|
||||
key := ethutil.Config.Db.GetKeys()[0]
|
||||
fmt.Printf("%x\n", key.PrivateKey)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if ShowGenesis {
|
||||
fmt.Println(ethereum.BlockManager.BlockChain().Genesis())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
log.Printf("Starting Ethereum v%s\n", ethutil.Config.Ver)
|
||||
|
||||
// Set the max peers
|
||||
ethereum.MaxPeers = MaxPeer
|
||||
|
||||
if StartConsole {
|
||||
err := os.Mkdir(ethutil.Config.ExecPath, os.ModePerm)
|
||||
// Error is OK if the error is ErrExist
|
||||
if err != nil && !os.IsExist(err) {
|
||||
log.Panic("Unable to create EXECPATH:", err)
|
||||
}
|
||||
|
||||
console := NewConsole(ethereum)
|
||||
go console.Start()
|
||||
}
|
||||
|
||||
if UseGui {
|
||||
gui := ethui.New(ethereum)
|
||||
gui.Start()
|
||||
//ethereum.Stop()
|
||||
} else {
|
||||
RegisterInterupts(ethereum)
|
||||
ethereum.Start()
|
||||
|
||||
if StartMining {
|
||||
log.Printf("Miner started\n")
|
||||
|
||||
// Fake block mining. It broadcasts a new block every 5 seconds
|
||||
go func() {
|
||||
pow := ðchain.EasyPow{}
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
keyRing := ethutil.NewValueFromBytes(data)
|
||||
addr := keyRing.Get(1).Bytes()
|
||||
|
||||
for {
|
||||
txs := ethereum.TxPool.Flush()
|
||||
// Create a new block which we're going to mine
|
||||
block := ethereum.BlockManager.BlockChain().NewBlock(addr, txs)
|
||||
// Apply all transactions to the block
|
||||
ethereum.BlockManager.ApplyTransactions(block, block.Transactions())
|
||||
|
||||
ethereum.BlockManager.AccumelateRewards(block, block)
|
||||
|
||||
// Search the nonce
|
||||
block.Nonce = pow.Search(block)
|
||||
ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||
err := ethereum.BlockManager.ProcessBlock(block)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("\n+++++++ MINED BLK +++++++\n", ethereum.BlockManager.BlockChain().CurrentBlock)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for shutdown
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
||||
}
|
56
ethereum/config.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Identifier string
|
||||
var StartMining bool
|
||||
var StartRpc bool
|
||||
var RpcPort int
|
||||
var UseUPnP bool
|
||||
var OutboundPort string
|
||||
var ShowGenesis bool
|
||||
var AddPeer string
|
||||
var MaxPeer int
|
||||
var GenAddr bool
|
||||
var UseSeed bool
|
||||
var ImportKey string
|
||||
var ExportKey bool
|
||||
var LogFile string
|
||||
var NonInteractive bool
|
||||
var StartJsConsole bool
|
||||
var InputFile string
|
||||
|
||||
var Datadir string
|
||||
|
||||
func Init() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.BoolVar(&StartJsConsole, "js", false, "exp")
|
||||
|
||||
flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
|
||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||
flag.BoolVar(&ExportKey, "export", false, "export private key")
|
||||
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||
flag.StringVar(&ImportKey, "import", "", "imports the given private key (hex)")
|
||||
|
||||
flag.StringVar(&Datadir, "datadir", ".ethereum", "specifies the datadir to use. Takes precedence over config file.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
InputFile = flag.Arg(0)
|
||||
}
|
193
ethereum/ethereum.go
Normal file
@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/go-ethereum/utils"
|
||||
"github.com/rakyll/globalconf"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Debug = true
|
||||
|
||||
func RegisterInterrupt(cb func(os.Signal)) {
|
||||
go func() {
|
||||
// Buffered chan of one is enough
|
||||
c := make(chan os.Signal, 1)
|
||||
// Notify about interrupts for now
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
for sig := range c {
|
||||
cb(sig)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
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() {
|
||||
Init()
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
// set logger
|
||||
var logSys *log.Logger
|
||||
flags := log.LstdFlags
|
||||
|
||||
var lt ethutil.LoggerType
|
||||
if StartJsConsole || len(InputFile) > 0 {
|
||||
lt = ethutil.LogFile
|
||||
} else {
|
||||
lt = ethutil.LogFile | ethutil.LogStd
|
||||
}
|
||||
|
||||
g, err := globalconf.NewWithOptions(&globalconf.Options{
|
||||
Filename: path.Join(ethutil.ApplicationFolder(Datadir), "conf.ini"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
g.ParseAll()
|
||||
}
|
||||
ethutil.ReadConfig(Datadir, lt, g, Identifier)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Instantiated a eth stack
|
||||
ethereum, err := eth.New(eth.CapDefault, UseUPnP)
|
||||
if err != nil {
|
||||
log.Println("eth start err:", err)
|
||||
return
|
||||
}
|
||||
ethereum.Port = OutboundPort
|
||||
|
||||
// bookkeeping tasks
|
||||
switch {
|
||||
case GenAddr:
|
||||
if NonInteractive || confirm("This action overwrites your old private key.") {
|
||||
utils.CreateKeyPair(true)
|
||||
}
|
||||
os.Exit(0)
|
||||
case len(ImportKey) > 0:
|
||||
if NonInteractive || confirm("This action overwrites your old private key.") {
|
||||
mnemonic := strings.Split(ImportKey, " ")
|
||||
if len(mnemonic) == 24 {
|
||||
logSys.Println("Got mnemonic key, importing.")
|
||||
key := ethutil.MnemonicDecode(mnemonic)
|
||||
utils.ImportPrivateKey(key)
|
||||
} else if len(mnemonic) == 1 {
|
||||
logSys.Println("Got hex key, importing.")
|
||||
utils.ImportPrivateKey(ImportKey)
|
||||
} else {
|
||||
logSys.Println("Did not recognise format, exiting.")
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
case ExportKey:
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
save these words so you can restore your account later: %s
|
||||
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey)
|
||||
|
||||
os.Exit(0)
|
||||
case ShowGenesis:
|
||||
logSys.Println(ethereum.BlockChain().Genesis())
|
||||
os.Exit(0)
|
||||
default:
|
||||
// Creates a keypair if non exists
|
||||
utils.CreateKeyPair(false)
|
||||
}
|
||||
|
||||
// client
|
||||
logger.Infoln(fmt.Sprintf("Starting Ethereum v%s", ethutil.Config.Ver))
|
||||
|
||||
// Set the max peers
|
||||
ethereum.MaxPeers = MaxPeer
|
||||
|
||||
// Set Mining status
|
||||
ethereum.Mining = StartMining
|
||||
|
||||
if StartMining {
|
||||
utils.DoMining(ethereum)
|
||||
}
|
||||
|
||||
if StartJsConsole {
|
||||
repl := NewJSRepl(ethereum)
|
||||
|
||||
go repl.Start()
|
||||
|
||||
RegisterInterrupt(func(os.Signal) {
|
||||
repl.Stop()
|
||||
})
|
||||
} else if len(InputFile) > 0 {
|
||||
file, err := os.Open(InputFile)
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Fatal(err)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Fatal(err)
|
||||
}
|
||||
|
||||
re := NewJSRE(ethereum)
|
||||
RegisterInterrupt(func(os.Signal) {
|
||||
re.Stop()
|
||||
})
|
||||
re.Run(string(content))
|
||||
}
|
||||
|
||||
if StartRpc {
|
||||
utils.DoRpc(ethereum, RpcPort)
|
||||
}
|
||||
|
||||
RegisterInterrupt(func(sig os.Signal) {
|
||||
fmt.Printf("Shutting down (%v) ... \n", sig)
|
||||
ethereum.Stop()
|
||||
})
|
||||
|
||||
ethereum.Start(UseSeed)
|
||||
|
||||
// Wait for shutdown
|
||||
ethereum.WaitForShutdown()
|
||||
}
|
209
ethereum/javascript_runtime.go
Normal file
@ -0,0 +1,209 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/go-ethereum/utils"
|
||||
"github.com/obscuren/otto"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type JSRE struct {
|
||||
ethereum *eth.Ethereum
|
||||
vm *otto.Otto
|
||||
lib *ethpub.PEthereum
|
||||
|
||||
blockChan chan ethutil.React
|
||||
changeChan chan ethutil.React
|
||||
quitChan chan bool
|
||||
|
||||
objectCb map[string][]otto.Value
|
||||
}
|
||||
|
||||
func (jsre *JSRE) LoadExtFile(path string) {
|
||||
result, err := ioutil.ReadFile(path)
|
||||
if err == nil {
|
||||
jsre.vm.Run(result)
|
||||
} else {
|
||||
ethutil.Config.Log.Debugln("Could not load file:", path)
|
||||
}
|
||||
}
|
||||
|
||||
func (jsre *JSRE) LoadIntFile(file string) {
|
||||
assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal", "assets", "ext")
|
||||
jsre.LoadExtFile(path.Join(assetPath, file))
|
||||
}
|
||||
|
||||
func NewJSRE(ethereum *eth.Ethereum) *JSRE {
|
||||
re := &JSRE{
|
||||
ethereum,
|
||||
otto.New(),
|
||||
ethpub.NewPEthereum(ethereum),
|
||||
make(chan ethutil.React, 1),
|
||||
make(chan ethutil.React, 1),
|
||||
make(chan bool),
|
||||
make(map[string][]otto.Value),
|
||||
}
|
||||
|
||||
// Init the JS lib
|
||||
re.vm.Run(jsLib)
|
||||
|
||||
// Load extra javascript files
|
||||
re.LoadIntFile("string.js")
|
||||
re.LoadIntFile("big.js")
|
||||
|
||||
// We have to make sure that, whoever calls this, calls "Stop"
|
||||
go re.mainLoop()
|
||||
|
||||
re.Bind("eth", &JSEthereum{re.lib, re.vm})
|
||||
|
||||
re.initStdFuncs()
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
func (self *JSRE) Bind(name string, v interface{}) {
|
||||
self.vm.Set(name, v)
|
||||
}
|
||||
|
||||
func (self *JSRE) Run(code string) (otto.Value, error) {
|
||||
return self.vm.Run(code)
|
||||
}
|
||||
|
||||
func (self *JSRE) Require(file string) error {
|
||||
if len(filepath.Ext(file)) == 0 {
|
||||
file += ".js"
|
||||
}
|
||||
|
||||
fh, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, _ := ioutil.ReadAll(fh)
|
||||
self.Run("exports = {};(function() {" + string(content) + "})();")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *JSRE) Stop() {
|
||||
// Kill the main loop
|
||||
self.quitChan <- true
|
||||
|
||||
close(self.blockChan)
|
||||
close(self.quitChan)
|
||||
close(self.changeChan)
|
||||
}
|
||||
|
||||
func (self *JSRE) mainLoop() {
|
||||
// Subscribe to events
|
||||
reactor := self.ethereum.Reactor()
|
||||
reactor.Subscribe("newBlock", self.blockChan)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quitChan:
|
||||
break out
|
||||
case block := <-self.blockChan:
|
||||
if _, ok := block.Resource.(*ethchain.Block); ok {
|
||||
}
|
||||
case object := <-self.changeChan:
|
||||
if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
|
||||
for _, cb := range self.objectCb[ethutil.Hex(stateObject.Address())] {
|
||||
val, _ := self.vm.ToValue(ethpub.NewPStateObject(stateObject))
|
||||
cb.Call(cb, val)
|
||||
}
|
||||
} else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok {
|
||||
for _, cb := range self.objectCb[ethutil.Hex(storageObject.StateAddress)+ethutil.Hex(storageObject.Address)] {
|
||||
val, _ := self.vm.ToValue(ethpub.NewPStorageState(storageObject))
|
||||
cb.Call(cb, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *JSRE) initStdFuncs() {
|
||||
t, _ := self.vm.Get("eth")
|
||||
eth := t.Object()
|
||||
eth.Set("watch", self.watch)
|
||||
eth.Set("addPeer", self.addPeer)
|
||||
eth.Set("require", self.require)
|
||||
eth.Set("stopMining", self.stopMining)
|
||||
eth.Set("startMining", self.startMining)
|
||||
}
|
||||
|
||||
/*
|
||||
* The following methods are natively implemented javascript functions
|
||||
*/
|
||||
|
||||
func (self *JSRE) stopMining(call otto.FunctionCall) otto.Value {
|
||||
v, _ := self.vm.ToValue(utils.StopMining(self.ethereum))
|
||||
return v
|
||||
}
|
||||
|
||||
func (self *JSRE) startMining(call otto.FunctionCall) otto.Value {
|
||||
v, _ := self.vm.ToValue(utils.StartMining(self.ethereum))
|
||||
return v
|
||||
}
|
||||
|
||||
// eth.watch
|
||||
func (self *JSRE) watch(call otto.FunctionCall) otto.Value {
|
||||
addr, _ := call.Argument(0).ToString()
|
||||
var storageAddr string
|
||||
var cb otto.Value
|
||||
var storageCallback bool
|
||||
if len(call.ArgumentList) > 2 {
|
||||
storageCallback = true
|
||||
storageAddr, _ = call.Argument(1).ToString()
|
||||
cb = call.Argument(2)
|
||||
} else {
|
||||
cb = call.Argument(1)
|
||||
}
|
||||
|
||||
if storageCallback {
|
||||
self.objectCb[addr+storageAddr] = append(self.objectCb[addr+storageAddr], cb)
|
||||
|
||||
event := "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
|
||||
self.ethereum.Reactor().Subscribe(event, self.changeChan)
|
||||
} else {
|
||||
self.objectCb[addr] = append(self.objectCb[addr], cb)
|
||||
|
||||
event := "object:" + string(ethutil.FromHex(addr))
|
||||
self.ethereum.Reactor().Subscribe(event, self.changeChan)
|
||||
}
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
|
||||
host, err := call.Argument(0).ToString()
|
||||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
self.ethereum.ConnectToPeer(host)
|
||||
|
||||
return otto.TrueValue()
|
||||
}
|
||||
|
||||
func (self *JSRE) require(call otto.FunctionCall) otto.Value {
|
||||
file, err := call.Argument(0).ToString()
|
||||
if err != nil {
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
if err := self.Require(file); err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
t, _ := self.vm.Get("exports")
|
||||
|
||||
return t
|
||||
}
|
53
ethereum/js_lib.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
const jsLib = `
|
||||
function pp(object) {
|
||||
var str = "";
|
||||
|
||||
if(object instanceof Array) {
|
||||
str += "[ ";
|
||||
for(var i = 0, l = object.length; i < l; i++) {
|
||||
str += pp(object[i]);
|
||||
|
||||
if(i < l-1) {
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
str += " ]";
|
||||
} else if(typeof(object) === "object") {
|
||||
str += "{ ";
|
||||
var last = Object.keys(object).sort().pop()
|
||||
for(var k in object) {
|
||||
str += k + ": " + pp(object[k]);
|
||||
|
||||
if(k !== last) {
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
str += " }";
|
||||
} else if(typeof(object) === "string") {
|
||||
str += "\033[32m'" + object + "'";
|
||||
} else if(typeof(object) === "undefined") {
|
||||
str += "\033[1m\033[30m" + object;
|
||||
} else if(typeof(object) === "number") {
|
||||
str += "\033[31m" + object;
|
||||
} else if(typeof(object) === "function") {
|
||||
str += "\033[35m[Function]";
|
||||
} else {
|
||||
str += object;
|
||||
}
|
||||
|
||||
str += "\033[0m";
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function prettyPrint(/* */) {
|
||||
var args = arguments;
|
||||
for(var i = 0, l = args.length; i < l; i++) {
|
||||
console.log(pp(args[i]))
|
||||
}
|
||||
}
|
||||
|
||||
var print = prettyPrint;
|
||||
`
|
118
ethereum/repl.go
Normal file
@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
"github.com/obscuren/otto"
|
||||
)
|
||||
|
||||
type Repl interface {
|
||||
Start()
|
||||
Stop()
|
||||
}
|
||||
|
||||
type JSRepl struct {
|
||||
re *JSRE
|
||||
|
||||
prompt string
|
||||
}
|
||||
|
||||
func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
|
||||
return &JSRepl{re: NewJSRE(ethereum), prompt: "> "}
|
||||
}
|
||||
|
||||
func (self *JSRepl) Start() {
|
||||
self.read()
|
||||
}
|
||||
|
||||
func (self *JSRepl) Stop() {
|
||||
self.re.Stop()
|
||||
}
|
||||
|
||||
func (self *JSRepl) parseInput(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
|
||||
value, err := self.re.Run(code)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
self.PrintValue(value)
|
||||
}
|
||||
|
||||
// The JSEthereum object attempts to wrap the PEthereum object and returns
|
||||
// meaningful javascript objects
|
||||
type JSBlock struct {
|
||||
*ethpub.PBlock
|
||||
eth *JSEthereum
|
||||
}
|
||||
|
||||
func (self *JSBlock) GetTransaction(hash string) otto.Value {
|
||||
return self.eth.toVal(self.PBlock.GetTransaction(hash))
|
||||
}
|
||||
|
||||
type JSEthereum struct {
|
||||
*ethpub.PEthereum
|
||||
vm *otto.Otto
|
||||
}
|
||||
|
||||
func (self *JSEthereum) GetBlock(hash string) otto.Value {
|
||||
return self.toVal(&JSBlock{self.PEthereum.GetBlock(hash), self})
|
||||
}
|
||||
|
||||
func (self *JSEthereum) GetPeers() otto.Value {
|
||||
return self.toVal(self.PEthereum.GetPeers())
|
||||
}
|
||||
|
||||
func (self *JSEthereum) GetKey() otto.Value {
|
||||
return self.toVal(self.PEthereum.GetKey())
|
||||
}
|
||||
|
||||
func (self *JSEthereum) GetStateObject(addr string) otto.Value {
|
||||
return self.toVal(self.PEthereum.GetStateObject(addr))
|
||||
}
|
||||
|
||||
func (self *JSEthereum) GetStateKeyVals(addr string) otto.Value {
|
||||
return self.toVal(self.PEthereum.GetStateObject(addr).StateKeyVal(false))
|
||||
}
|
||||
|
||||
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
|
||||
r, err := self.PEthereum.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
return self.toVal(r)
|
||||
}
|
||||
|
||||
func (self *JSEthereum) Create(key, valueStr, gasStr, gasPriceStr, scriptStr string) otto.Value {
|
||||
r, err := self.PEthereum.Create(key, valueStr, gasStr, gasPriceStr, scriptStr)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
return self.toVal(r)
|
||||
}
|
||||
|
||||
func (self *JSEthereum) toVal(v interface{}) otto.Value {
|
||||
result, err := self.vm.ToValue(v)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Value unknown:", err)
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
121
ethereum/repl_darwin.go
Normal file
@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
|
||||
// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib
|
||||
// #cgo LDFLAGS: -lreadline
|
||||
// #include <stdio.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <readline/readline.h>
|
||||
// #include <readline/history.h>
|
||||
import "C"
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func initReadLine() {
|
||||
C.rl_catch_sigwinch = 0
|
||||
C.rl_catch_signals = 0
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGWINCH)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
switch sig {
|
||||
case syscall.SIGWINCH:
|
||||
C.rl_resize_terminal()
|
||||
|
||||
case os.Interrupt:
|
||||
C.rl_cleanup_after_signal()
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func readLine(prompt *string) *string {
|
||||
var p *C.char
|
||||
|
||||
//readline allows an empty prompt(NULL)
|
||||
if prompt != nil {
|
||||
p = C.CString(*prompt)
|
||||
}
|
||||
|
||||
ret := C.readline(p)
|
||||
|
||||
if p != nil {
|
||||
C.free(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
return nil
|
||||
} //EOF
|
||||
|
||||
s := C.GoString(ret)
|
||||
C.free(unsafe.Pointer(ret))
|
||||
return &s
|
||||
}
|
||||
|
||||
func addHistory(s string) {
|
||||
p := C.CString(s)
|
||||
C.add_history(p)
|
||||
C.free(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
var indentCount = 0
|
||||
var str = ""
|
||||
|
||||
func (self *JSRepl) setIndent() {
|
||||
open := strings.Count(str, "{")
|
||||
open += strings.Count(str, "(")
|
||||
closed := strings.Count(str, "}")
|
||||
closed += strings.Count(str, ")")
|
||||
indentCount = open - closed
|
||||
if indentCount <= 0 {
|
||||
self.prompt = "> "
|
||||
} else {
|
||||
self.prompt = strings.Join(make([]string, indentCount*2), "..")
|
||||
self.prompt += " "
|
||||
}
|
||||
}
|
||||
|
||||
func (self *JSRepl) read() {
|
||||
initReadLine()
|
||||
L:
|
||||
for {
|
||||
switch result := readLine(&self.prompt); true {
|
||||
case result == nil:
|
||||
break L
|
||||
|
||||
case *result != "":
|
||||
str += *result + "\n"
|
||||
|
||||
self.setIndent()
|
||||
|
||||
if indentCount <= 0 {
|
||||
if *result == "exit" {
|
||||
self.Stop()
|
||||
break L
|
||||
}
|
||||
|
||||
addHistory(str[:len(str)-1]) //allow user to recall this line
|
||||
|
||||
self.parseInput(str)
|
||||
|
||||
str = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *JSRepl) PrintValue(v interface{}) {
|
||||
method, _ := self.re.vm.Get("prettyPrint")
|
||||
v, err := self.re.vm.ToValue(v)
|
||||
if err == nil {
|
||||
method.Call(method, v)
|
||||
}
|
||||
}
|
1
ethereum/repl_linux.go
Symbolic link
@ -0,0 +1 @@
|
||||
repl_darwin.go
|
24
ethereum/repl_windows.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (self *JSRepl) read() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Printf(self.prompt)
|
||||
str, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Println("Error reading input", err)
|
||||
} else {
|
||||
self.parseInput(string(str))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *JSRepl) PrintValue(value otto.Value) {
|
||||
fmt.Println(value)
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestSource struct {
|
||||
Inputs map[string]string
|
||||
Expectation string
|
||||
}
|
||||
|
||||
func NewTestSource(source string) *TestSource {
|
||||
s := &TestSource{}
|
||||
err := json.Unmarshal([]byte(source), s)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type TestRunner struct {
|
||||
source *TestSource
|
||||
}
|
||||
|
||||
func NewTestRunner(t *testing.T) *TestRunner {
|
||||
return &TestRunner{}
|
||||
}
|
||||
|
||||
func (runner *TestRunner) RunFromString(input string, Cb func(*TestSource)) {
|
||||
source := NewTestSource(input)
|
||||
Cb(source)
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
import (
|
||||
"encoding/hex"
|
||||
_ "fmt"
|
||||
"github.com/ethereum/ethdb-go"
|
||||
"github.com/ethereum/ethutil-go"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testsource = `
|
||||
{
|
||||
"inputs":{
|
||||
"doe": "reindeer",
|
||||
"dog": "puppy",
|
||||
"dogglesworth": "cat"
|
||||
},
|
||||
"expectation":"e378927bfc1bd4f01a2e8d9f59bd18db8a208bb493ac0b00f93ce51d4d2af76c"
|
||||
}`
|
||||
|
||||
func TestTestRunner(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
trie := ethutil.NewTrie(db, "")
|
||||
|
||||
runner := NewTestRunner(t)
|
||||
runner.RunFromString(testsource, func(source *TestSource) {
|
||||
for key, value := range source.Inputs {
|
||||
trie.Update(key, value)
|
||||
}
|
||||
if hex.EncodeToString(trie.Root.([]byte)) != source.Expectation {
|
||||
t.Error("trie root did not match")
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
33
testing.go
@ -1,33 +0,0 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
|
||||
import (
|
||||
_"fmt"
|
||||
)
|
||||
|
||||
// This will eventually go away
|
||||
var Db *MemDatabase
|
||||
|
||||
func Testing() {
|
||||
db, _ := NewMemDatabase()
|
||||
Db = db
|
||||
|
||||
bm := NewBlockManager()
|
||||
|
||||
tx := NewTransaction("\x00", 20, []string{"PUSH"})
|
||||
txData := tx.RlpEncode()
|
||||
//fmt.Printf("%q\n", txData)
|
||||
|
||||
copyTx := &Transaction{}
|
||||
copyTx.RlpDecode(txData)
|
||||
//fmt.Println(tx)
|
||||
//fmt.Println(copyTx)
|
||||
|
||||
tx2 := NewTransaction("\x00", 20, []string{"SET 10 6", "LD 10 10"})
|
||||
|
||||
blck := CreateTestBlock([]*Transaction{tx2, tx})
|
||||
|
||||
bm.ProcessBlock( blck )
|
||||
}
|
||||
*/
|
217
ui/gui.go
@ -1,217 +0,0 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/niemeyer/qml"
|
||||
"math/big"
|
||||
"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 {
|
||||
// The main application window
|
||||
win *qml.Window
|
||||
// QML Engine
|
||||
engine *qml.Engine
|
||||
component *qml.Common
|
||||
// The ethereum interface
|
||||
eth *eth.Ethereum
|
||||
|
||||
// The public Ethereum library
|
||||
lib *EthLib
|
||||
|
||||
txDb *ethdb.LDBDatabase
|
||||
|
||||
addr []byte
|
||||
}
|
||||
|
||||
// Create GUI, but doesn't start it
|
||||
func New(ethereum *eth.Ethereum) *Gui {
|
||||
lib := &EthLib{blockManager: ethereum.BlockManager, blockChain: ethereum.BlockManager.BlockChain(), txPool: ethereum.TxPool}
|
||||
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
key := ethutil.Config.Db.GetKeys()[0]
|
||||
addr := key.Address()
|
||||
|
||||
ethereum.BlockManager.WatchAddr(addr)
|
||||
|
||||
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
|
||||
}
|
||||
|
||||
func (ui *Gui) Start() {
|
||||
defer ui.txDb.Close()
|
||||
|
||||
// Register ethereum functions
|
||||
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||
Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
||||
}, {
|
||||
Init: func(p *Tx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
|
||||
}})
|
||||
|
||||
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
|
||||
// Create a new QML engine
|
||||
ui.engine = qml.NewEngine()
|
||||
// Load the main QML interface
|
||||
component, err := ui.engine.LoadFile("wallet.qml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ui.engine.LoadFile("transactions.qml")
|
||||
|
||||
ui.win = component.CreateWindow(nil)
|
||||
|
||||
context := ui.engine.Context()
|
||||
|
||||
// Expose the eth library and the ui library to QML
|
||||
context.SetVar("eth", ui.lib)
|
||||
context.SetVar("ui", &UiLib{engine: ui.engine, eth: ui.eth})
|
||||
|
||||
// Register the ui as a block processor
|
||||
ui.eth.BlockManager.SecondaryBlockProcessor = ui
|
||||
//ui.eth.TxPool.SecondaryProcessor = ui
|
||||
|
||||
// Add the ui as a log system so we can log directly to the UGI
|
||||
ethutil.Config.Log.AddLogSystem(ui)
|
||||
|
||||
// Loads previous blocks
|
||||
go ui.setInitialBlockChain()
|
||||
go ui.readPreviousTransactions()
|
||||
go ui.update()
|
||||
|
||||
ui.win.Show()
|
||||
ui.win.Wait()
|
||||
|
||||
ui.eth.Stop()
|
||||
}
|
||||
|
||||
func (ui *Gui) setInitialBlockChain() {
|
||||
// Load previous 10 blocks
|
||||
chain := ui.eth.BlockManager.BlockChain().GetChain(ui.eth.BlockManager.BlockChain().CurrentBlock.Hash(), 10)
|
||||
for _, block := range chain {
|
||||
ui.ProcessBlock(block)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ui *Gui) readPreviousTransactions() {
|
||||
it := ui.txDb.Db().NewIterator(nil, nil)
|
||||
for it.Next() {
|
||||
tx := ethchain.NewTransactionFromBytes(it.Value())
|
||||
|
||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
func (ui *Gui) ProcessBlock(block *ethchain.Block) {
|
||||
ui.win.Root().Call("addBlock", NewBlockFromBlock(block))
|
||||
}
|
||||
|
||||
// Simple go routine function that updates the list of peers in the GUI
|
||||
func (ui *Gui) update() {
|
||||
txChan := make(chan ethchain.TxMsg, 1)
|
||||
ui.eth.TxPool.Subscribe(txChan)
|
||||
|
||||
account := ui.eth.BlockManager.GetAddrState(ui.addr).Account
|
||||
unconfirmedFunds := new(big.Int)
|
||||
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
|
||||
for {
|
||||
select {
|
||||
case txMsg := <-txChan:
|
||||
tx := txMsg.Tx
|
||||
|
||||
if txMsg.Type == ethchain.TxPre {
|
||||
if bytes.Compare(tx.Sender(), ui.addr) == 0 {
|
||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
||||
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
|
||||
ui.eth.BlockManager.GetAddrState(ui.addr).Nonce += 1
|
||||
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
|
||||
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
|
||||
ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
|
||||
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
|
||||
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
|
||||
}
|
||||
|
||||
pos := "+"
|
||||
if unconfirmedFunds.Cmp(big.NewInt(0)) >= 0 {
|
||||
pos = "-"
|
||||
}
|
||||
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
|
||||
str := fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(account.Amount), pos, val)
|
||||
|
||||
ui.win.Root().Call("setWalletValue", str)
|
||||
} else {
|
||||
amount := account.Amount
|
||||
if bytes.Compare(tx.Sender(), ui.addr) == 0 {
|
||||
amount.Sub(account.Amount, tx.Value)
|
||||
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
|
||||
amount.Add(account.Amount, tx.Value)
|
||||
}
|
||||
|
||||
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(amount)))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
accountAmount := ui.eth.BlockManager.GetAddrState(ui.addr).Account.Amount
|
||||
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", accountAmount))
|
||||
|
||||
ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers))
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Logging functions that log directly to the GUI interface
|
||||
func (ui *Gui) Println(v ...interface{}) {
|
||||
str := strings.TrimRight(fmt.Sprintln(v...), "\n")
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
ui.win.Root().Call("addLog", line)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *Gui) Printf(format string, v ...interface{}) {
|
||||
str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
ui.win.Root().Call("addLog", line)
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type EthLib struct {
|
||||
blockManager *ethchain.BlockManager
|
||||
blockChain *ethchain.BlockChain
|
||||
txPool *ethchain.TxPool
|
||||
}
|
||||
|
||||
func (lib *EthLib) CreateTx(receiver, a, data string) string {
|
||||
var hash []byte
|
||||
if len(receiver) == 0 {
|
||||
hash = ethchain.ContractAddr
|
||||
} else {
|
||||
var err error
|
||||
hash, err = hex.DecodeString(receiver)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
k, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
keyRing := ethutil.NewValueFromBytes(k)
|
||||
|
||||
amount := ethutil.Big(a)
|
||||
code := ethchain.Compile(strings.Split(data, "\n"))
|
||||
tx := ethchain.NewTransaction(hash, amount, code)
|
||||
tx.Nonce = lib.blockManager.GetAddrState(keyRing.Get(1).Bytes()).Nonce
|
||||
|
||||
tx.Sign(keyRing.Get(0).Bytes())
|
||||
|
||||
lib.txPool.QueueTransaction(tx)
|
||||
|
||||
if len(receiver) == 0 {
|
||||
ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
|
||||
} else {
|
||||
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
|
||||
}
|
||||
|
||||
return ethutil.Hex(tx.Hash())
|
||||
}
|
||||
|
||||
func (lib *EthLib) GetBlock(hexHash string) *Block {
|
||||
hash, err := hex.DecodeString(hexHash)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
block := lib.blockChain.GetBlock(hash)
|
||||
fmt.Println(block)
|
||||
|
||||
return &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
|
||||
}
|
40
ui/ui_lib.go
@ -1,40 +0,0 @@
|
||||
package ethui
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/niemeyer/qml"
|
||||
)
|
||||
|
||||
// UI Library that has some basic functionality exposed
|
||||
type UiLib struct {
|
||||
engine *qml.Engine
|
||||
eth *eth.Ethereum
|
||||
connected bool
|
||||
}
|
||||
|
||||
// Opens a QML file (external application)
|
||||
func (ui *UiLib) Open(path string) {
|
||||
component, err := ui.engine.LoadFile(path[7:])
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Debugln(err)
|
||||
}
|
||||
win := component.CreateWindow(nil)
|
||||
|
||||
go func() {
|
||||
win.Show()
|
||||
win.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (ui *UiLib) Connect(button qml.Object) {
|
||||
if !ui.connected {
|
||||
ui.eth.Start()
|
||||
ui.connected = true
|
||||
button.Set("enabled", false)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||
ui.eth.ConnectToPeer(addr)
|
||||
}
|
74
utils/cmd.go
Normal file
@ -0,0 +1,74 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go"
|
||||
"github.com/ethereum/eth-go/ethminer"
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
"github.com/ethereum/eth-go/ethrpc"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DoRpc(ethereum *eth.Ethereum, RpcPort int) {
|
||||
var err error
|
||||
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpub.NewPEthereum(ethereum), RpcPort)
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Infoln("Could not start RPC interface:", err)
|
||||
} else {
|
||||
go ethereum.RpcServer.Start()
|
||||
}
|
||||
}
|
||||
|
||||
var miner ethminer.Miner
|
||||
|
||||
func DoMining(ethereum *eth.Ethereum) {
|
||||
// Set Mining status
|
||||
ethereum.Mining = true
|
||||
|
||||
if ethutil.GetKeyRing().Len() == 0 {
|
||||
ethutil.Config.Log.Infoln("No address found, can't start mining")
|
||||
return
|
||||
}
|
||||
keyPair := ethutil.GetKeyRing().Get(0)
|
||||
addr := keyPair.Address()
|
||||
|
||||
go func() {
|
||||
miner = ethminer.NewDefaultMiner(addr, ethereum)
|
||||
|
||||
// Give it some time to connect with peers
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
for ethereum.IsUpToDate() == false {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
ethutil.Config.Log.Infoln("Miner started")
|
||||
|
||||
miner := ethminer.NewDefaultMiner(addr, ethereum)
|
||||
miner.Start()
|
||||
}()
|
||||
}
|
||||
|
||||
func StopMining(ethereum *eth.Ethereum) bool {
|
||||
if ethereum.Mining {
|
||||
miner.Stop()
|
||||
|
||||
ethutil.Config.Log.Infoln("Miner stopped")
|
||||
|
||||
ethereum.Mining = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func StartMining(ethereum *eth.Ethereum) bool {
|
||||
if !ethereum.Mining {
|
||||
DoMining(ethereum)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
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.PreParse(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
|
||||
}
|
108
utils/keys.go
Normal file
@ -0,0 +1,108 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
)
|
||||
|
||||
func CreateKeyPair(force bool) {
|
||||
if force {
|
||||
ethutil.GetKeyRing().Reset()
|
||||
fmt.Println("resetting")
|
||||
}
|
||||
|
||||
if ethutil.GetKeyRing().Get(0) == nil {
|
||||
_, prv := secp256k1.GenerateKeyPair()
|
||||
|
||||
keyPair, err := ethutil.GetKeyRing().NewKeyPair(prv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
|
||||
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
save these words so you can restore your account later: %s
|
||||
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey, mne)
|
||||
}
|
||||
}
|
||||
|
||||
func ImportPrivateKey(sec string) {
|
||||
ethutil.GetKeyRing().Reset()
|
||||
|
||||
keyPair, err := ethutil.GetKeyRing().NewKeyPair(ethutil.FromHex(sec))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mne := ethutil.MnemonicEncode(ethutil.Hex(keyPair.PrivateKey))
|
||||
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
save these words so you can restore your account later: %s
|
||||
`, keyPair.Address(), keyPair.PrivateKey, keyPair.PublicKey, mne)
|
||||
}
|
||||
|
||||
/*
|
||||
func CreateKeyPair(force bool) {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
if len(data) == 0 || force {
|
||||
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))
|
||||
|
||||
fmt.Printf(`
|
||||
Generating new address and keypair.
|
||||
Please keep your keys somewhere save.
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
save these words so you can restore your account later: %s
|
||||
`, pair.Address(), prv, pub, mne)
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func ImportPrivateKey(prvKey string) {
|
||||
key := ethutil.FromHex(prvKey)
|
||||
msg := []byte("tmp")
|
||||
// Couldn't think of a better way to get the pub key
|
||||
sig, _ := secp256k1.Sign(msg, key)
|
||||
pub, _ := secp256k1.RecoverPubkey(msg, sig)
|
||||
pair := ðutil.Key{PrivateKey: key, PublicKey: pub}
|
||||
ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
|
||||
|
||||
fmt.Printf(`
|
||||
Importing private key
|
||||
|
||||
++++++++++++++++ KeyRing +++++++++++++++++++
|
||||
addr: %x
|
||||
prvk: %x
|
||||
pubk: %x
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
`, pair.Address(), key, pub)
|
||||
}
|
||||
*/
|
357
wallet.qml
@ -1,357 +0,0 @@
|
||||
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
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
|
||||
width: 900
|
||||
height: 600
|
||||
minimumHeight: 300
|
||||
|
||||
title: "Ethereal"
|
||||
|
||||
MenuBar {
|
||||
Menu {
|
||||
title: "File"
|
||||
MenuItem {
|
||||
text: "Import App"
|
||||
shortcut: "Ctrl+o"
|
||||
onTriggered: openAppDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Network"
|
||||
MenuItem {
|
||||
text: "Add Peer"
|
||||
shortcut: "Ctrl+p"
|
||||
onTriggered: {
|
||||
addPeerWin.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Start"
|
||||
onTriggered: ui.connect()
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Help"
|
||||
MenuItem {
|
||||
text: "About"
|
||||
onTriggered: {
|
||||
aboutWin.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
property var blockModel: ListModel {
|
||||
id: blockModel
|
||||
}
|
||||
|
||||
function setView(view) {
|
||||
networkView.visible = false
|
||||
historyView.visible = false
|
||||
newTxView.visible = false
|
||||
view.visible = true
|
||||
//root.title = "Ethereal - " = view.title
|
||||
}
|
||||
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
resizing: false
|
||||
|
||||
Rectangle {
|
||||
id: menu
|
||||
Layout.minimumWidth: 80
|
||||
Layout.maximumWidth: 80
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
//color: "#D9DDE7"
|
||||
color: "#252525"
|
||||
|
||||
ColumnLayout {
|
||||
y: 50
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 200
|
||||
Image {
|
||||
source: "tx.png"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
setView(historyView)
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
source: "new.png"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
setView(newTxView)
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
source: "net.png"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
setView(networkView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
property var txModel: ListModel {
|
||||
id: txModel
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: historyView
|
||||
property var title: "Transactions"
|
||||
anchors.right: parent.right
|
||||
anchors.left: menu.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors.fill: parent
|
||||
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
|
||||
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
|
||||
|
||||
model: txModel
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: newTxView
|
||||
property var title: "New transaction"
|
||||
visible: false
|
||||
anchors.right: parent.right
|
||||
anchors.left: menu.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
color: "#00000000"
|
||||
|
||||
ColumnLayout {
|
||||
width: 400
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 5
|
||||
anchors.topMargin: 5
|
||||
TextField {
|
||||
id: txAmount
|
||||
width: 200
|
||||
placeholderText: "Amount"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: txReceiver
|
||||
placeholderText: "Receiver Address (or empty for contract)"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Transaction data"
|
||||
}
|
||||
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 {
|
||||
id: networkView
|
||||
property var title: "Network"
|
||||
visible: false
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
|
||||
TableView {
|
||||
id: blockTable
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: logView.top
|
||||
TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
|
||||
TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
|
||||
|
||||
model: blockModel
|
||||
|
||||
onDoubleClicked: {
|
||||
popup.visible = true
|
||||
popup.block = eth.getBlock(blockModel.get(row).hash)
|
||||
popup.hashLabel.text = popup.block.hash
|
||||
}
|
||||
}
|
||||
|
||||
property var logModel: ListModel {
|
||||
id: logModel
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: logView
|
||||
width: parent.width
|
||||
height: 150
|
||||
anchors.bottom: parent.bottom
|
||||
TableViewColumn{ role: "description" ; title: "log" }
|
||||
|
||||
model: logModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: openAppDialog
|
||||
title: "Open QML Application"
|
||||
onAccepted: {
|
||||
ui.open(openAppDialog.fileUrl.toString())
|
||||
}
|
||||
}
|
||||
|
||||
statusBar: StatusBar {
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Button {
|
||||
property var enabled: true
|
||||
id: connectButton
|
||||
onClicked: {
|
||||
if(this.enabled) {
|
||||
ui.connect(this)
|
||||
}
|
||||
}
|
||||
text: "Connect"
|
||||
}
|
||||
Button {
|
||||
id: importAppButton
|
||||
anchors.left: connectButton.right
|
||||
anchors.leftMargin: 5
|
||||
onClicked: openAppDialog.open()
|
||||
text: "Import App"
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.left: importAppButton.right
|
||||
anchors.leftMargin: 5
|
||||
id: walletValueLabel
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.right: peerImage.left
|
||||
anchors.rightMargin: 5
|
||||
id: peerLabel
|
||||
font.pixelSize: 8
|
||||
text: "0 / 0"
|
||||
}
|
||||
Image {
|
||||
id: peerImage
|
||||
anchors.right: parent.right
|
||||
width: 10; height: 10
|
||||
source: "network.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
id: popup
|
||||
visible: false
|
||||
property var block
|
||||
Label {
|
||||
id: hashLabel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
id: addPeerWin
|
||||
visible: false
|
||||
minimumWidth: 230
|
||||
maximumWidth: 230
|
||||
maximumHeight: 50
|
||||
minimumHeight: 50
|
||||
|
||||
TextField {
|
||||
id: addrField
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
placeholderText: "address:port"
|
||||
}
|
||||
Button {
|
||||
anchors.left: addrField.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 5
|
||||
text: "Add"
|
||||
onClicked: {
|
||||
ui.connectToPeer(addrField.text)
|
||||
addPeerWin.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
id: aboutWin
|
||||
visible: false
|
||||
title: "About"
|
||||
minimumWidth: 300
|
||||
maximumWidth: 300
|
||||
maximumHeight: 200
|
||||
minimumHeight: 200
|
||||
|
||||
Text {
|
||||
font.pointSize: 18
|
||||
text: "Eth Go"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setWalletValue(value) {
|
||||
walletValueLabel.text = value
|
||||
}
|
||||
|
||||
function addTx(tx) {
|
||||
txModel.insert(0, {hash: tx.hash, address: tx.address, value: tx.value})
|
||||
}
|
||||
|
||||
function addBlock(block) {
|
||||
blockModel.insert(0, {number: block.number, hash: block.hash})
|
||||
}
|
||||
|
||||
function addLog(str) {
|
||||
if(str.len != 0) {
|
||||
logModel.append({description: str})
|
||||
}
|
||||
}
|
||||
|
||||
function setPeers(text) {
|
||||
peerLabel.text = text
|
||||
}
|
||||
}
|