@ -1,4 +1,25 @@ |
||||
before_install: sudo apt-get install libgmp3-dev |
||||
language: go |
||||
go: |
||||
- 1.2 |
||||
- 1.3 |
||||
before_install: |
||||
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y |
||||
- sudo apt-get update -qq |
||||
- sudo apt-get install -yqq libgmp3-dev qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev libreadline6-dev |
||||
install: |
||||
- go get code.google.com/p/go.tools/cmd/goimports |
||||
- go get github.com/golang/lint/golint |
||||
# - go get code.google.com/p/go.tools/cmd/vet |
||||
- go get code.google.com/p/go.tools/cmd/cover |
||||
- go get github.com/mattn/goveralls |
||||
- ./install_deps.sh |
||||
before_script: |
||||
- gofmt -l -w . |
||||
- goimports -l -w . |
||||
- golint . |
||||
# - go vet ./... |
||||
# - go test -race ./... |
||||
script: |
||||
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN |
||||
env: |
||||
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64=" |
||||
|
||||
|
@ -0,0 +1,41 @@ |
||||
FROM ubuntu:14.04 |
||||
|
||||
## Environment setup |
||||
ENV HOME /root |
||||
ENV GOPATH /root/go |
||||
ENV PATH /go/bin:/root/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games |
||||
|
||||
RUN mkdir -p /root/go |
||||
ENV DEBIAN_FRONTEND noninteractive |
||||
|
||||
## Install base dependencies |
||||
RUN apt-get update && apt-get upgrade -y |
||||
RUN apt-get install -y git mercurial build-essential software-properties-common pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev |
||||
|
||||
## Build and install Go |
||||
RUN hg clone -u release https://code.google.com/p/go |
||||
RUN cd go && hg update go1.4 |
||||
RUN cd go/src && ./all.bash && go version |
||||
|
||||
## Install GUI dependencies |
||||
RUN add-apt-repository ppa:ubuntu-sdk-team/ppa -y |
||||
RUN apt-get update -y |
||||
RUN apt-get install -y qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev |
||||
|
||||
## Fetch and install serpent-go |
||||
RUN go get -v -d github.com/ethereum/serpent-go |
||||
WORKDIR $GOPATH/src/github.com/ethereum/serpent-go |
||||
RUN git checkout master |
||||
RUN git submodule update --init |
||||
RUN go install -v |
||||
|
||||
# Fetch and install go-ethereum |
||||
RUN go get -v -d github.com/ethereum/go-ethereum/... |
||||
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum |
||||
RUN git checkout poc8 |
||||
RUN ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi |
||||
RUN go install -v ./cmd/ethereum |
||||
|
||||
# Run JSON RPC |
||||
ENTRYPOINT ["ethereum", "-rpc=true", "-rpcport=8080"] |
||||
EXPOSE 8080 |
@ -1,16 +1,16 @@ |
||||
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU General Public |
||||
modify it under the terms of the GNU Lesser General Public |
||||
License as published by the Free Software Foundation; either |
||||
version 2.1 of the License, or (at your option) any later version. |
||||
|
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
General Public License for more details. |
||||
Lesser General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this library; if not, write to the Free Software |
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
MA 02110-1301 USA |
||||
|
@ -0,0 +1,351 @@ |
||||
package eth |
||||
|
||||
import ( |
||||
"bytes" |
||||
"container/list" |
||||
"fmt" |
||||
"math" |
||||
"math/big" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/wire" |
||||
) |
||||
|
||||
var poollogger = logger.NewLogger("BPOOL") |
||||
|
||||
type block struct { |
||||
from *Peer |
||||
peer *Peer |
||||
block *types.Block |
||||
reqAt time.Time |
||||
requested int |
||||
} |
||||
|
||||
type BlockPool struct { |
||||
mut sync.Mutex |
||||
|
||||
eth *Ethereum |
||||
|
||||
hashes [][]byte |
||||
pool map[string]*block |
||||
|
||||
td *big.Int |
||||
quit chan bool |
||||
|
||||
fetchingHashes bool |
||||
downloadStartedAt time.Time |
||||
|
||||
ChainLength, BlocksProcessed int |
||||
|
||||
peer *Peer |
||||
} |
||||
|
||||
func NewBlockPool(eth *Ethereum) *BlockPool { |
||||
return &BlockPool{ |
||||
eth: eth, |
||||
pool: make(map[string]*block), |
||||
td: ethutil.Big0, |
||||
quit: make(chan bool), |
||||
} |
||||
} |
||||
|
||||
func (self *BlockPool) Len() int { |
||||
return len(self.hashes) |
||||
} |
||||
|
||||
func (self *BlockPool) Reset() { |
||||
self.pool = make(map[string]*block) |
||||
self.hashes = nil |
||||
} |
||||
|
||||
func (self *BlockPool) HasLatestHash() bool { |
||||
self.mut.Lock() |
||||
defer self.mut.Unlock() |
||||
|
||||
return self.pool[string(self.eth.ChainManager().CurrentBlock().Hash())] != nil |
||||
} |
||||
|
||||
func (self *BlockPool) HasCommonHash(hash []byte) bool { |
||||
return self.eth.ChainManager().GetBlock(hash) != nil |
||||
} |
||||
|
||||
func (self *BlockPool) Blocks() (blocks types.Blocks) { |
||||
for _, item := range self.pool { |
||||
if item.block != nil { |
||||
blocks = append(blocks, item.block) |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func (self *BlockPool) FetchHashes(peer *Peer) bool { |
||||
highestTd := self.eth.HighestTDPeer() |
||||
|
||||
if (self.peer == nil && peer.td.Cmp(highestTd) >= 0) || (self.peer != nil && peer.td.Cmp(self.peer.td) > 0) || self.peer == peer { |
||||
if self.peer != peer { |
||||
poollogger.Infof("Found better suitable peer (%v vs %v)\n", self.td, peer.td) |
||||
|
||||
if self.peer != nil { |
||||
self.peer.doneFetchingHashes = true |
||||
} |
||||
} |
||||
|
||||
self.peer = peer |
||||
self.td = peer.td |
||||
|
||||
if !self.HasLatestHash() { |
||||
self.fetchHashes() |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
func (self *BlockPool) fetchHashes() { |
||||
peer := self.peer |
||||
|
||||
peer.doneFetchingHashes = false |
||||
|
||||
const amount = 256 |
||||
peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4]) |
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)})) |
||||
} |
||||
|
||||
func (self *BlockPool) AddHash(hash []byte, peer *Peer) { |
||||
self.mut.Lock() |
||||
defer self.mut.Unlock() |
||||
|
||||
if self.pool[string(hash)] == nil { |
||||
self.pool[string(hash)] = &block{peer, nil, nil, time.Now(), 0} |
||||
|
||||
self.hashes = append([][]byte{hash}, self.hashes...) |
||||
} |
||||
} |
||||
|
||||
func (self *BlockPool) Add(b *types.Block, peer *Peer) { |
||||
self.addBlock(b, peer, false) |
||||
} |
||||
|
||||
func (self *BlockPool) AddNew(b *types.Block, peer *Peer) { |
||||
self.addBlock(b, peer, true) |
||||
} |
||||
|
||||
func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) { |
||||
self.mut.Lock() |
||||
defer self.mut.Unlock() |
||||
|
||||
hash := string(b.Hash()) |
||||
|
||||
if self.pool[hash] == nil && !self.eth.ChainManager().HasBlock(b.Hash()) { |
||||
poollogger.Infof("Got unrequested block (%x...)\n", hash[0:4]) |
||||
|
||||
self.hashes = append(self.hashes, b.Hash()) |
||||
self.pool[hash] = &block{peer, peer, b, time.Now(), 0} |
||||
|
||||
// The following is only performed on an unrequested new block
|
||||
if newBlock { |
||||
fmt.Println("1.", !self.eth.ChainManager().HasBlock(b.PrevHash), ethutil.Bytes2Hex(b.Hash()[0:4]), ethutil.Bytes2Hex(b.PrevHash[0:4])) |
||||
fmt.Println("2.", self.pool[string(b.PrevHash)] == nil) |
||||
fmt.Println("3.", !self.fetchingHashes) |
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) /*&& self.pool[string(b.PrevHash)] == nil*/ && !self.fetchingHashes { |
||||
poollogger.Infof("Unknown chain, requesting (%x...)\n", b.PrevHash[0:4]) |
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{b.Hash(), uint32(256)})) |
||||
} |
||||
} |
||||
} else if self.pool[hash] != nil { |
||||
self.pool[hash].block = b |
||||
} |
||||
|
||||
self.BlocksProcessed++ |
||||
} |
||||
|
||||
func (self *BlockPool) Remove(hash []byte) { |
||||
self.mut.Lock() |
||||
defer self.mut.Unlock() |
||||
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash) |
||||
delete(self.pool, string(hash)) |
||||
} |
||||
|
||||
func (self *BlockPool) DistributeHashes() { |
||||
self.mut.Lock() |
||||
defer self.mut.Unlock() |
||||
|
||||
var ( |
||||
peerLen = self.eth.peers.Len() |
||||
amount = 256 * peerLen |
||||
dist = make(map[*Peer][][]byte) |
||||
) |
||||
|
||||
num := int(math.Min(float64(amount), float64(len(self.pool)))) |
||||
for i, j := 0, 0; i < len(self.hashes) && j < num; i++ { |
||||
hash := self.hashes[i] |
||||
item := self.pool[string(hash)] |
||||
|
||||
if item != nil && item.block == nil { |
||||
var peer *Peer |
||||
lastFetchFailed := time.Since(item.reqAt) > 5*time.Second |
||||
|
||||
// Handle failed requests
|
||||
if lastFetchFailed && item.requested > 5 && item.peer != nil { |
||||
if item.requested < 100 { |
||||
// Select peer the hash was retrieved off
|
||||
peer = item.from |
||||
} else { |
||||
// Remove it
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash) |
||||
delete(self.pool, string(hash)) |
||||
} |
||||
} else if lastFetchFailed || item.peer == nil { |
||||
// Find a suitable, available peer
|
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) { |
||||
if peer == nil && len(dist[p]) < amount/peerLen && p.statusKnown { |
||||
peer = p |
||||
} |
||||
}) |
||||
} |
||||
|
||||
if peer != nil { |
||||
item.reqAt = time.Now() |
||||
item.peer = peer |
||||
item.requested++ |
||||
|
||||
dist[peer] = append(dist[peer], hash) |
||||
} |
||||
} |
||||
} |
||||
|
||||
for peer, hashes := range dist { |
||||
peer.FetchBlocks(hashes) |
||||
} |
||||
|
||||
if len(dist) > 0 { |
||||
self.downloadStartedAt = time.Now() |
||||
} |
||||
} |
||||
|
||||
func (self *BlockPool) Start() { |
||||
go self.downloadThread() |
||||
go self.chainThread() |
||||
} |
||||
|
||||
func (self *BlockPool) Stop() { |
||||
close(self.quit) |
||||
} |
||||
|
||||
func (self *BlockPool) downloadThread() { |
||||
serviceTimer := time.NewTicker(100 * time.Millisecond) |
||||
out: |
||||
for { |
||||
select { |
||||
case <-self.quit: |
||||
break out |
||||
case <-serviceTimer.C: |
||||
// Check if we're catching up. If not distribute the hashes to
|
||||
// the peers and download the blockchain
|
||||
self.fetchingHashes = false |
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) { |
||||
if p.statusKnown && p.FetchingHashes() { |
||||
self.fetchingHashes = true |
||||
} |
||||
}) |
||||
|
||||
if len(self.hashes) > 0 { |
||||
self.DistributeHashes() |
||||
} |
||||
|
||||
if self.ChainLength < len(self.hashes) { |
||||
self.ChainLength = len(self.hashes) |
||||
} |
||||
|
||||
if self.peer != nil && |
||||
!self.peer.doneFetchingHashes && |
||||
time.Since(self.peer.lastHashAt) > 10*time.Second && |
||||
time.Since(self.peer.lastHashRequestedAt) > 5*time.Second { |
||||
self.fetchHashes() |
||||
} |
||||
|
||||
/* |
||||
if !self.fetchingHashes { |
||||
blocks := self.Blocks() |
||||
chain.BlockBy(chain.Number).Sort(blocks) |
||||
|
||||
if len(blocks) > 0 { |
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) && self.pool[string(b.PrevHash)] == nil && !self.fetchingHashes { |
||||
} |
||||
} |
||||
} |
||||
*/ |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (self *BlockPool) chainThread() { |
||||
procTimer := time.NewTicker(500 * time.Millisecond) |
||||
out: |
||||
for { |
||||
select { |
||||
case <-self.quit: |
||||
break out |
||||
case <-procTimer.C: |
||||
blocks := self.Blocks() |
||||
types.BlockBy(types.Number).Sort(blocks) |
||||
|
||||
// Find common block
|
||||
for i, block := range blocks { |
||||
if self.eth.ChainManager().HasBlock(block.PrevHash) { |
||||
blocks = blocks[i:] |
||||
break |
||||
} |
||||
} |
||||
|
||||
if len(blocks) > 0 { |
||||
if self.eth.ChainManager().HasBlock(blocks[0].PrevHash) { |
||||
for i, block := range blocks[1:] { |
||||
// NOTE: The Ith element in this loop refers to the previous block in
|
||||
// outer "blocks"
|
||||
if bytes.Compare(block.PrevHash, blocks[i].Hash()) != 0 { |
||||
blocks = blocks[:i] |
||||
|
||||
break |
||||
} |
||||
} |
||||
} else { |
||||
blocks = nil |
||||
} |
||||
} |
||||
|
||||
if len(blocks) > 0 { |
||||
chainman := self.eth.ChainManager() |
||||
|
||||
err := chainman.InsertChain(blocks) |
||||
if err != nil { |
||||
poollogger.Debugln(err) |
||||
|
||||
self.Reset() |
||||
|
||||
if self.peer != nil && self.peer.conn != nil { |
||||
poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr()) |
||||
} |
||||
|
||||
// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
|
||||
self.eth.BlacklistPeer(self.peer) |
||||
self.peer.StopWithReason(DiscBadPeer) |
||||
self.td = ethutil.Big0 |
||||
self.peer = nil |
||||
} |
||||
|
||||
for _, block := range blocks { |
||||
self.Remove(block.Hash()) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU General Public |
||||
License as published by the Free Software Foundation; either |
||||
version 2.1 of the License, or (at your option) any later version. |
||||
|
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
MA 02110-1301 USA |
@ -0,0 +1,52 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"io/ioutil" |
||||
"os" |
||||
|
||||
"github.com/ethereum/go-ethereum" |
||||
"github.com/ethereum/go-ethereum/cmd/ethereum/repl" |
||||
"github.com/ethereum/go-ethereum/cmd/utils" |
||||
"github.com/ethereum/go-ethereum/javascript" |
||||
) |
||||
|
||||
func InitJsConsole(ethereum *eth.Ethereum) { |
||||
repl := ethrepl.NewJSRepl(ethereum) |
||||
go repl.Start() |
||||
utils.RegisterInterrupt(func(os.Signal) { |
||||
repl.Stop() |
||||
}) |
||||
} |
||||
|
||||
func ExecJsFile(ethereum *eth.Ethereum, InputFile string) { |
||||
file, err := os.Open(InputFile) |
||||
if err != nil { |
||||
clilogger.Fatalln(err) |
||||
} |
||||
content, err := ioutil.ReadAll(file) |
||||
if err != nil { |
||||
clilogger.Fatalln(err) |
||||
} |
||||
re := javascript.NewJSRE(ethereum) |
||||
utils.RegisterInterrupt(func(os.Signal) { |
||||
re.Stop() |
||||
}) |
||||
re.Run(string(content)) |
||||
} |
@ -0,0 +1,97 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
/* Inspired by https://github.com/xuyu/logging/blob/master/colorful_win.go */ |
||||
|
||||
package ethrepl |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
type color uint16 |
||||
|
||||
const ( |
||||
green = color(0x0002) |
||||
red = color(0x0004) |
||||
yellow = color(0x000E) |
||||
) |
||||
|
||||
const ( |
||||
mask = uint16(yellow | green | red) |
||||
) |
||||
|
||||
var ( |
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||
procGetStdHandle = kernel32.NewProc("GetStdHandle") |
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") |
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") |
||||
hStdout uintptr |
||||
initScreenInfo *consoleScreenBufferInfo |
||||
) |
||||
|
||||
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { |
||||
ret, _, _ := procSetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes)) |
||||
return ret != 0 |
||||
} |
||||
|
||||
type coord struct { |
||||
X, Y int16 |
||||
} |
||||
|
||||
type smallRect struct { |
||||
Left, Top, Right, Bottom int16 |
||||
} |
||||
|
||||
type consoleScreenBufferInfo struct { |
||||
DwSize coord |
||||
DwCursorPosition coord |
||||
WAttributes uint16 |
||||
SrWindow smallRect |
||||
DwMaximumWindowSize coord |
||||
} |
||||
|
||||
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { |
||||
var csbi consoleScreenBufferInfo |
||||
ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi))) |
||||
if ret == 0 { |
||||
return nil |
||||
} |
||||
return &csbi |
||||
} |
||||
|
||||
const ( |
||||
stdOutputHandle = uint32(-11 & 0xFFFFFFFF) |
||||
) |
||||
|
||||
func init() { |
||||
hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutputHandle)) |
||||
initScreenInfo = getConsoleScreenBufferInfo(hStdout) |
||||
} |
||||
|
||||
func resetColorful() { |
||||
if initScreenInfo == nil { |
||||
return |
||||
} |
||||
setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) |
||||
} |
||||
|
||||
func changeColor(c color) { |
||||
attr := uint16(0) & ^mask | uint16(c) |
||||
setConsoleTextAttribute(hStdout, attr) |
||||
} |
@ -0,0 +1,92 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
package ethrepl |
||||
|
||||
import ( |
||||
"bufio" |
||||
"fmt" |
||||
"os" |
||||
"strings" |
||||
) |
||||
|
||||
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 { |
||||
if string(str) == "exit" { |
||||
self.Stop() |
||||
break |
||||
} else { |
||||
self.parseInput(string(str)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func addHistory(s string) { |
||||
} |
||||
|
||||
func printColored(outputVal string) { |
||||
for outputVal != "" { |
||||
codePart := "" |
||||
if strings.HasPrefix(outputVal, "\033[32m") { |
||||
codePart = "\033[32m" |
||||
changeColor(2) |
||||
} |
||||
if strings.HasPrefix(outputVal, "\033[1m\033[30m") { |
||||
codePart = "\033[1m\033[30m" |
||||
changeColor(8) |
||||
} |
||||
if strings.HasPrefix(outputVal, "\033[31m") { |
||||
codePart = "\033[31m" |
||||
changeColor(red) |
||||
} |
||||
if strings.HasPrefix(outputVal, "\033[35m") { |
||||
codePart = "\033[35m" |
||||
changeColor(5) |
||||
} |
||||
if strings.HasPrefix(outputVal, "\033[0m") { |
||||
codePart = "\033[0m" |
||||
resetColorful() |
||||
} |
||||
textPart := outputVal[len(codePart):len(outputVal)] |
||||
index := strings.Index(textPart, "\033") |
||||
if index == -1 { |
||||
outputVal = "" |
||||
} else { |
||||
outputVal = textPart[index:len(textPart)] |
||||
textPart = textPart[0:index] |
||||
} |
||||
fmt.Printf("%v", textPart) |
||||
} |
||||
} |
||||
|
||||
func (self *JSRepl) PrintValue(v interface{}) { |
||||
method, _ := self.re.Vm.Get("prettyPrint") |
||||
v, err := self.re.Vm.ToValue(v) |
||||
if err == nil { |
||||
val, err := method.Call(method, v) |
||||
if err == nil { |
||||
printColored(fmt.Sprintf("%v", val)) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,128 @@ |
||||
/* |
||||
This file is part of go-ethereum |
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
go-ethereum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
/** |
||||
* @authors: |
||||
* Jeffrey Wilcke <i@jev.io> |
||||
* @date 2014 |
||||
* |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"io/ioutil" |
||||
"log" |
||||
"os" |
||||
"strings" |
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/tests/helper" |
||||
) |
||||
|
||||
type Account struct { |
||||
Balance string |
||||
Code string |
||||
Nonce string |
||||
Storage map[string]string |
||||
} |
||||
|
||||
func StateObjectFromAccount(addr string, account Account) *state.StateObject { |
||||
obj := state.NewStateObject(ethutil.Hex2Bytes(addr)) |
||||
obj.SetBalance(ethutil.Big(account.Balance)) |
||||
|
||||
if ethutil.IsHex(account.Code) { |
||||
account.Code = account.Code[2:] |
||||
} |
||||
obj.Code = ethutil.Hex2Bytes(account.Code) |
||||
obj.Nonce = ethutil.Big(account.Nonce).Uint64() |
||||
|
||||
return obj |
||||
} |
||||
|
||||
type VmTest struct { |
||||
Callcreates interface{} |
||||
Env map[string]string |
||||
Exec map[string]string |
||||
Gas string |
||||
Out string |
||||
Post map[string]Account |
||||
Pre map[string]Account |
||||
} |
||||
|
||||
func RunVmTest(js string) (failed int) { |
||||
tests := make(map[string]VmTest) |
||||
|
||||
data, _ := ioutil.ReadAll(strings.NewReader(js)) |
||||
err := json.Unmarshal(data, &tests) |
||||
if err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
|
||||
for name, test := range tests { |
||||
state := state.New(helper.NewTrie()) |
||||
for addr, account := range test.Pre { |
||||
obj := StateObjectFromAccount(addr, account) |
||||
state.SetStateObject(obj) |
||||
} |
||||
|
||||
ret, _, gas, err := helper.RunVm(state, test.Env, test.Exec) |
||||
// When an error is returned it doesn't always mean the tests fails.
|
||||
// Have to come up with some conditional failing mechanism.
|
||||
if err != nil { |
||||
log.Println(err) |
||||
} |
||||
|
||||
rexp := helper.FromHex(test.Out) |
||||
if bytes.Compare(rexp, ret) != 0 { |
||||
log.Printf("%s's return failed. Expected %x, got %x\n", name, rexp, ret) |
||||
failed = 1 |
||||
} |
||||
|
||||
gexp := ethutil.Big(test.Gas) |
||||
if gexp.Cmp(gas) != 0 { |
||||
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas) |
||||
failed = 1 |
||||
} |
||||
|
||||
for addr, account := range test.Post { |
||||
obj := state.GetStateObject(helper.FromHex(addr)) |
||||
for addr, value := range account.Storage { |
||||
v := obj.GetState(helper.FromHex(addr)).Bytes() |
||||
vexp := helper.FromHex(value) |
||||
|
||||
if bytes.Compare(v, vexp) != 0 { |
||||
log.Printf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address()[0:4], addr, vexp, v, ethutil.BigD(vexp), ethutil.BigD(v)) |
||||
failed = 1 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func main() { |
||||
helper.Logger.SetLogLevel(5) |
||||
if len(os.Args) == 1 { |
||||
log.Fatalln("no json supplied") |
||||
} |
||||
|
||||
os.Exit(RunVmTest(os.Args[1])) |
||||
} |
@ -0,0 +1 @@ |
||||
60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50 |
@ -0,0 +1,164 @@ |
||||
/* |
||||
This file is part of go-ethereum |
||||
|
||||
go-ethereum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
go-ethereum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
/** |
||||
* @authors |
||||
* Jeffrey Wilcke <i@jev.io> |
||||
* @date 2014 |
||||
* |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"flag" |
||||
"fmt" |
||||
"log" |
||||
"math/big" |
||||
"os" |
||||
"runtime" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/trie" |
||||
"github.com/ethereum/go-ethereum/vm" |
||||
) |
||||
|
||||
var ( |
||||
code = flag.String("code", "", "evm code") |
||||
loglevel = flag.Int("log", 4, "log level") |
||||
gas = flag.String("gas", "1000000000", "gas amount") |
||||
price = flag.String("price", "0", "gas price") |
||||
value = flag.String("value", "0", "tx value") |
||||
dump = flag.Bool("dump", false, "dump state after run") |
||||
data = flag.String("data", "", "data") |
||||
) |
||||
|
||||
func perr(v ...interface{}) { |
||||
fmt.Println(v...) |
||||
//os.Exit(1)
|
||||
} |
||||
|
||||
func main() { |
||||
flag.Parse() |
||||
|
||||
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel))) |
||||
|
||||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "") |
||||
|
||||
db, _ := ethdb.NewMemDatabase() |
||||
statedb := state.New(trie.New(db, "")) |
||||
sender := statedb.NewStateObject([]byte("sender")) |
||||
receiver := statedb.NewStateObject([]byte("receiver")) |
||||
//receiver.SetCode([]byte(*code))
|
||||
receiver.SetCode(ethutil.Hex2Bytes(*code)) |
||||
|
||||
vmenv := NewEnv(statedb, []byte("evmuser"), ethutil.Big(*value)) |
||||
|
||||
tstart := time.Now() |
||||
|
||||
ret, e := vmenv.Call(sender, receiver.Address(), ethutil.Hex2Bytes(*data), ethutil.Big(*gas), ethutil.Big(*price), ethutil.Big(*value)) |
||||
|
||||
logger.Flush() |
||||
if e != nil { |
||||
perr(e) |
||||
} |
||||
|
||||
if *dump { |
||||
fmt.Println(string(statedb.Dump())) |
||||
} |
||||
|
||||
var mem runtime.MemStats |
||||
runtime.ReadMemStats(&mem) |
||||
fmt.Printf("vm took %v\n", time.Since(tstart)) |
||||
fmt.Printf(`alloc: %d |
||||
tot alloc: %d |
||||
no. malloc: %d |
||||
heap alloc: %d |
||||
heap objs: %d |
||||
num gc: %d |
||||
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC) |
||||
|
||||
fmt.Printf("%x\n", ret) |
||||
} |
||||
|
||||
type VMEnv struct { |
||||
state *state.StateDB |
||||
block *types.Block |
||||
|
||||
transactor []byte |
||||
value *big.Int |
||||
|
||||
depth int |
||||
Gas *big.Int |
||||
time int64 |
||||
} |
||||
|
||||
func NewEnv(state *state.StateDB, transactor []byte, value *big.Int) *VMEnv { |
||||
return &VMEnv{ |
||||
state: state, |
||||
transactor: transactor, |
||||
value: value, |
||||
time: time.Now().Unix(), |
||||
} |
||||
} |
||||
|
||||
func (self *VMEnv) State() *state.StateDB { return self.state } |
||||
func (self *VMEnv) Origin() []byte { return self.transactor } |
||||
func (self *VMEnv) BlockNumber() *big.Int { return ethutil.Big0 } |
||||
func (self *VMEnv) PrevHash() []byte { return make([]byte, 32) } |
||||
func (self *VMEnv) Coinbase() []byte { return self.transactor } |
||||
func (self *VMEnv) Time() int64 { return self.time } |
||||
func (self *VMEnv) Difficulty() *big.Int { return ethutil.Big1 } |
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } |
||||
func (self *VMEnv) Value() *big.Int { return self.value } |
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } |
||||
func (self *VMEnv) Depth() int { return 0 } |
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i } |
||||
func (self *VMEnv) AddLog(log state.Log) { |
||||
self.state.AddLog(log) |
||||
} |
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { |
||||
return vm.Transfer(from, to, amount) |
||||
} |
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution { |
||||
evm := vm.New(self, vm.DebugVmTy) |
||||
|
||||
return core.NewExecution(evm, addr, data, gas, price, value) |
||||
} |
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||
exe := self.vm(addr, data, gas, price, value) |
||||
ret, err := exe.Call(addr, caller) |
||||
self.Gas = exe.Gas |
||||
|
||||
return ret, err |
||||
} |
||||
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||
exe := self.vm(caller.Address(), data, gas, price, value) |
||||
return exe.Call(addr, caller) |
||||
} |
||||
|
||||
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { |
||||
exe := self.vm(addr, data, gas, price, value) |
||||
return exe.Create(caller) |
||||
} |
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
var bigInt = (function () { |
||||
var base = 10000000, logBase = 7; |
||||
var sign = { |
@ -0,0 +1,14 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
ethereum/ethereum |
||||
ethereal/ethereal |
||||
|
@ -0,0 +1,18 @@ |
||||
# Ethereum JavaScript API |
||||
|
||||
This is the Ethereum compatible JavaScript API using `Promise`s |
||||
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. |
||||
|
||||
For an example see `index.html`. |
||||
|
||||
**Please note this repo is in it's early stage.** |
||||
|
||||
If you'd like to run a WebSocket ethereum node check out |
||||
[go-ethereum](https://github.com/ethereum/go-ethereum). |
||||
|
||||
To install ethereum and spawn a node: |
||||
|
||||
``` |
||||
go get github.com/ethereum/go-ethereum/ethereum |
||||
ethereum -ws -loglevel=4 |
||||
``` |
@ -0,0 +1,70 @@ |
||||
(function () { |
||||
var HttpRpcProvider = function (host) { |
||||
this.handlers = []; |
||||
this.host = host; |
||||
}; |
||||
|
||||
function formatJsonRpcObject(object) { |
||||
return { |
||||
jsonrpc: '2.0', |
||||
method: object.call, |
||||
params: object.args, |
||||
id: object._id |
||||
} |
||||
}; |
||||
|
||||
function formatJsonRpcMessage(message) {
|
||||
var object = JSON.parse(message); |
||||
|
||||
return { |
||||
_id: object.id, |
||||
data: object.result |
||||
}; |
||||
}; |
||||
|
||||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) { |
||||
var data = formatJsonRpcObject(payload); |
||||
|
||||
var request = new XMLHttpRequest(); |
||||
request.open("POST", this.host, true); |
||||
request.send(JSON.stringify(data)); |
||||
request.onreadystatechange = function () { |
||||
if (request.readyState === 4 && cb) { |
||||
cb(request); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
HttpRpcProvider.prototype.send = function (payload) { |
||||
var self = this; |
||||
this.sendRequest(payload, function (request) { |
||||
self.handlers.forEach(function (handler) { |
||||
handler.call(self, formatJsonRpcMessage(request.responseText)); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
HttpRpcProvider.prototype.poll = function (payload, id) { |
||||
var self = this; |
||||
this.sendRequest(payload, function (request) { |
||||
var parsed = JSON.parse(request.responseText); |
||||
if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) { |
||||
return; |
||||
} |
||||
self.handlers.forEach(function (handler) { |
||||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { |
||||
set: function (handler) { |
||||
this.handlers.push(handler); |
||||
} |
||||
}); |
||||
|
||||
if (typeof(web3) !== "undefined" && web3.providers !== undefined) { |
||||
web3.providers.HttpRpcProvider = HttpRpcProvider; |
||||
} |
||||
})(); |
||||
|
@ -0,0 +1,33 @@ |
||||
<!doctype> |
||||
<html> |
||||
|
||||
<head> |
||||
<script type="text/javascript" src="main.js"></script> |
||||
<script type="text/javascript" src="websocket.js"></script> |
||||
<script type="text/javascript" src="qt.js"></script> |
||||
<script type="text/javascript" src="httprpc.js"></script> |
||||
<script type="text/javascript"> |
||||
function registerName() { |
||||
var name = document.querySelector("#name").value; |
||||
name = web3.fromAscii(name); |
||||
|
||||
var eth = web3.eth; |
||||
eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) { |
||||
document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through."; |
||||
}, function(err) { |
||||
console.log(err); |
||||
}); |
||||
} |
||||
</script> |
||||
</head> |
||||
|
||||
<body> |
||||
|
||||
<h1>std::name_reg</h1> |
||||
<input type="text" id="name"></input> |
||||
<input type="submit" onClick="registerName();"></input> |
||||
<div id="result"></div> |
||||
|
||||
</body> |
||||
|
||||
</html> |
@ -0,0 +1,432 @@ |
||||
(function(window) { |
||||
function isPromise(o) { |
||||
return o instanceof Promise |
||||
} |
||||
|
||||
function flattenPromise (obj) { |
||||
if (obj instanceof Promise) { |
||||
return Promise.resolve(obj); |
||||
} |
||||
|
||||
if (obj instanceof Array) { |
||||
return new Promise(function (resolve) { |
||||
var promises = obj.map(function (o) { |
||||
return flattenPromise(o); |
||||
}); |
||||
|
||||
return Promise.all(promises).then(function (res) { |
||||
for (var i = 0; i < obj.length; i++) { |
||||
obj[i] = res[i]; |
||||
} |
||||
resolve(obj); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
if (obj instanceof Object) { |
||||
return new Promise(function (resolve) { |
||||
var keys = Object.keys(obj); |
||||
var promises = keys.map(function (key) { |
||||
return flattenPromise(obj[key]); |
||||
}); |
||||
|
||||
return Promise.all(promises).then(function (res) { |
||||
for (var i = 0; i < keys.length; i++) { |
||||
obj[keys[i]] = res[i]; |
||||
} |
||||
resolve(obj); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
return Promise.resolve(obj); |
||||
}; |
||||
|
||||
var ethMethods = function () { |
||||
var blockCall = function (args) { |
||||
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber"; |
||||
}; |
||||
|
||||
var transactionCall = function (args) { |
||||
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
|
||||
}; |
||||
|
||||
var uncleCall = function (args) { |
||||
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
|
||||
}; |
||||
|
||||
var methods = [ |
||||
{ name: 'balanceAt', call: 'balanceAt' }, |
||||
{ name: 'stateAt', call: 'stateAt' }, |
||||
{ name: 'countAt', call: 'countAt'}, |
||||
{ name: 'codeAt', call: 'codeAt' }, |
||||
{ name: 'transact', call: 'transact' }, |
||||
{ name: 'call', call: 'call' }, |
||||
{ name: 'block', call: blockCall }, |
||||
{ name: 'transaction', call: transactionCall }, |
||||
{ name: 'uncle', call: uncleCall }, |
||||
{ name: 'compile', call: 'compile' } |
||||
]; |
||||
return methods; |
||||
}; |
||||
|
||||
var ethProperties = function () { |
||||
return [ |
||||
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' }, |
||||
{ name: 'listening', getter: 'listening', setter: 'setListening' }, |
||||
{ name: 'mining', getter: 'mining', setter: 'setMining' }, |
||||
{ name: 'gasPrice', getter: 'gasPrice' }, |
||||
{ name: 'account', getter: 'account' }, |
||||
{ name: 'accounts', getter: 'accounts' }, |
||||
{ name: 'peerCount', getter: 'peerCount' }, |
||||
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' }, |
||||
{ name: 'number', getter: 'number'} |
||||
]; |
||||
}; |
||||
|
||||
var dbMethods = function () { |
||||
return [ |
||||
{ name: 'put', call: 'put' }, |
||||
{ name: 'get', call: 'get' }, |
||||
{ name: 'putString', call: 'putString' }, |
||||
{ name: 'getString', call: 'getString' } |
||||
]; |
||||
}; |
||||
|
||||
var shhMethods = function () { |
||||
return [ |
||||
{ name: 'post', call: 'post' }, |
||||
{ name: 'newIdentity', call: 'newIdentity' }, |
||||
{ name: 'haveIdentity', call: 'haveIdentity' }, |
||||
{ name: 'newGroup', call: 'newGroup' }, |
||||
{ name: 'addToGroup', call: 'addToGroup' } |
||||
]; |
||||
}; |
||||
|
||||
var ethWatchMethods = function () { |
||||
var newFilter = function (args) { |
||||
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter'; |
||||
}; |
||||
|
||||
return [ |
||||
{ name: 'newFilter', call: newFilter }, |
||||
{ name: 'uninstallFilter', call: 'uninstallFilter' }, |
||||
{ name: 'getMessages', call: 'getMessages' } |
||||
]; |
||||
}; |
||||
|
||||
var shhWatchMethods = function () { |
||||
return [ |
||||
{ name: 'newFilter', call: 'shhNewFilter' }, |
||||
{ name: 'uninstallFilter', call: 'shhUninstallFilter' }, |
||||
{ name: 'getMessage', call: 'shhGetMessages' } |
||||
]; |
||||
}; |
||||
|
||||
var setupMethods = function (obj, methods) { |
||||
methods.forEach(function (method) { |
||||
obj[method.name] = function () { |
||||
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) { |
||||
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
||||
return {call: call, args: args}; |
||||
}).then(function (request) { |
||||
return new Promise(function (resolve, reject) { |
||||
web3.provider.send(request, function (result) { |
||||
//if (result || typeof result === "boolean") {
|
||||
resolve(result); |
||||
return; |
||||
//}
|
||||
//reject(result);
|
||||
}); |
||||
}); |
||||
}).catch(function( err) { |
||||
console.error(err); |
||||
}); |
||||
}; |
||||
}); |
||||
}; |
||||
|
||||
var setupProperties = function (obj, properties) { |
||||
properties.forEach(function (property) { |
||||
var proto = {}; |
||||
proto.get = function () { |
||||
return new Promise(function(resolve, reject) { |
||||
web3.provider.send({call: property.getter}, function(result) { |
||||
resolve(result); |
||||
}); |
||||
}); |
||||
}; |
||||
if (property.setter) { |
||||
proto.set = function (val) { |
||||
return flattenPromise([val]).then(function (args) { |
||||
return new Promise(function (resolve) { |
||||
web3.provider.send({call: property.setter, args: args}, function (result) { |
||||
resolve(result); |
||||
}); |
||||
}); |
||||
}).catch(function (err) { |
||||
console.error(err); |
||||
}); |
||||
} |
||||
} |
||||
Object.defineProperty(obj, property.name, proto); |
||||
}); |
||||
}; |
||||
|
||||
var web3 = { |
||||
_callbacks: {}, |
||||
_events: {}, |
||||
providers: {}, |
||||
toHex: function(str) { |
||||
var hex = ""; |
||||
for(var i = 0; i < str.length; i++) { |
||||
var n = str.charCodeAt(i).toString(16); |
||||
hex += n.length < 2 ? '0' + n : n; |
||||
} |
||||
|
||||
return hex; |
||||
}, |
||||
|
||||
toAscii: function(hex) { |
||||
// Find termination
|
||||
var str = ""; |
||||
var i = 0, l = hex.length; |
||||
for(; i < l; i+=2) { |
||||
var code = hex.charCodeAt(i) |
||||
if(code == 0) { |
||||
break; |
||||
} |
||||
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); |
||||
} |
||||
|
||||
return str; |
||||
}, |
||||
|
||||
toDecimal: function (val) { |
||||
return parseInt(val, 16);
|
||||
}, |
||||
|
||||
fromAscii: function(str, pad) { |
||||
pad = pad === undefined ? 32 : pad; |
||||
var hex = this.toHex(str); |
||||
while(hex.length < pad*2) |
||||
hex += "00"; |
||||
return hex |
||||
}, |
||||
|
||||
eth: { |
||||
prototype: Object(), |
||||
watch: function (params) { |
||||
return new Filter(params, ethWatch); |
||||
}, |
||||
}, |
||||
|
||||
db: { |
||||
prototype: Object() |
||||
}, |
||||
|
||||
shh: { |
||||
prototype: Object(), |
||||
watch: function (params) { |
||||
return new Filter(params, shhWatch); |
||||
} |
||||
}, |
||||
|
||||
on: function(event, id, cb) { |
||||
if(web3._events[event] === undefined) { |
||||
web3._events[event] = {}; |
||||
} |
||||
|
||||
web3._events[event][id] = cb; |
||||
return this |
||||
}, |
||||
|
||||
off: function(event, id) { |
||||
if(web3._events[event] !== undefined) { |
||||
delete web3._events[event][id]; |
||||
} |
||||
|
||||
return this |
||||
}, |
||||
|
||||
trigger: function(event, id, data) { |
||||
var callbacks = web3._events[event]; |
||||
if (!callbacks || !callbacks[id]) { |
||||
return; |
||||
} |
||||
var cb = callbacks[id]; |
||||
cb(data); |
||||
}, |
||||
}; |
||||
|
||||
var eth = web3.eth; |
||||
setupMethods(eth, ethMethods()); |
||||
setupProperties(eth, ethProperties()); |
||||
setupMethods(web3.db, dbMethods()); |
||||
setupMethods(web3.shh, shhMethods()); |
||||
|
||||
var ethWatch = { |
||||
changed: 'changed' |
||||
}; |
||||
setupMethods(ethWatch, ethWatchMethods()); |
||||
var shhWatch = { |
||||
changed: 'shhChanged' |
||||
}; |
||||
setupMethods(shhWatch, shhWatchMethods()); |
||||
|
||||
var ProviderManager = function() { |
||||
this.queued = []; |
||||
this.polls = []; |
||||
this.ready = false; |
||||
this.provider = undefined; |
||||
this.id = 1; |
||||
|
||||
var self = this; |
||||
var poll = function () { |
||||
if (self.provider && self.provider.poll) { |
||||
self.polls.forEach(function (data) { |
||||
data.data._id = self.id;
|
||||
self.id++; |
||||
self.provider.poll(data.data, data.id); |
||||
}); |
||||
} |
||||
setTimeout(poll, 12000); |
||||
}; |
||||
poll(); |
||||
}; |
||||
|
||||
ProviderManager.prototype.send = function(data, cb) { |
||||
data._id = this.id; |
||||
if (cb) { |
||||
web3._callbacks[data._id] = cb; |
||||
} |
||||
|
||||
data.args = data.args || []; |
||||
this.id++; |
||||
|
||||
if(this.provider !== undefined) { |
||||
this.provider.send(data); |
||||
} else { |
||||
console.warn("provider is not set"); |
||||
this.queued.push(data); |
||||
} |
||||
}; |
||||
|
||||
ProviderManager.prototype.set = function(provider) { |
||||
if(this.provider !== undefined && this.provider.unload !== undefined) { |
||||
this.provider.unload(); |
||||
} |
||||
|
||||
this.provider = provider; |
||||
this.ready = true; |
||||
}; |
||||
|
||||
ProviderManager.prototype.sendQueued = function() { |
||||
for(var i = 0; this.queued.length; i++) { |
||||
// Resend
|
||||
this.send(this.queued[i]); |
||||
} |
||||
}; |
||||
|
||||
ProviderManager.prototype.installed = function() { |
||||
return this.provider !== undefined; |
||||
}; |
||||
|
||||
ProviderManager.prototype.startPolling = function (data, pollId) { |
||||
if (!this.provider || !this.provider.poll) { |
||||
return; |
||||
} |
||||
this.polls.push({data: data, id: pollId}); |
||||
}; |
||||
|
||||
ProviderManager.prototype.stopPolling = function (pollId) { |
||||
for (var i = this.polls.length; i--;) { |
||||
var poll = this.polls[i]; |
||||
if (poll.id === pollId) { |
||||
this.polls.splice(i, 1); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
web3.provider = new ProviderManager(); |
||||
|
||||
web3.setProvider = function(provider) { |
||||
provider.onmessage = messageHandler; |
||||
web3.provider.set(provider); |
||||
web3.provider.sendQueued(); |
||||
}; |
||||
|
||||
var Filter = function(options, impl) { |
||||
this.impl = impl; |
||||
this.callbacks = []; |
||||
|
||||
var self = this;
|
||||
this.promise = impl.newFilter(options);
|
||||
this.promise.then(function (id) { |
||||
self.id = id; |
||||
web3.on(impl.changed, id, self.trigger.bind(self)); |
||||
web3.provider.startPolling({call: impl.changed, args: [id]}, id); |
||||
}); |
||||
}; |
||||
|
||||
Filter.prototype.arrived = function(callback) { |
||||
this.changed(callback); |
||||
} |
||||
|
||||
Filter.prototype.changed = function(callback) { |
||||
var self = this; |
||||
this.promise.then(function(id) { |
||||
self.callbacks.push(callback); |
||||
}); |
||||
}; |
||||
|
||||
Filter.prototype.trigger = function(messages) { |
||||
for(var i = 0; i < this.callbacks.length; i++) { |
||||
this.callbacks[i].call(this, messages); |
||||
} |
||||
}; |
||||
|
||||
Filter.prototype.uninstall = function() { |
||||
var self = this; |
||||
this.promise.then(function (id) { |
||||
self.impl.uninstallFilter(id); |
||||
web3.provider.stopPolling(id); |
||||
web3.off(impl.changed, id); |
||||
}); |
||||
}; |
||||
|
||||
Filter.prototype.messages = function() { |
||||
var self = this;
|
||||
return this.promise.then(function (id) { |
||||
return self.impl.getMessages(id); |
||||
}); |
||||
}; |
||||
|
||||
function messageHandler(data) { |
||||
if(data._event !== undefined) { |
||||
web3.trigger(data._event, data._id, data.data); |
||||
return; |
||||
} |
||||
|
||||
if(data._id) { |
||||
var cb = web3._callbacks[data._id]; |
||||
if (cb) { |
||||
cb.call(this, data.data) |
||||
delete web3._callbacks[data._id]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* |
||||
// Install default provider
|
||||
if(!web3.provider.installed()) { |
||||
var sock = new web3.WebSocket("ws://localhost:40404/eth"); |
||||
|
||||
web3.setProvider(sock); |
||||
} |
||||
*/ |
||||
|
||||
window.web3 = web3; |
||||
|
||||
})(this); |
@ -0,0 +1,27 @@ |
||||
(function() { |
||||
var QtProvider = function() { |
||||
this.handlers = []; |
||||
|
||||
var self = this; |
||||
navigator.qt.onmessage = function (message) { |
||||
self.handlers.forEach(function (handler) { |
||||
handler.call(self, JSON.parse(message.data)); |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
QtProvider.prototype.send = function(payload) { |
||||
navigator.qt.postMessage(JSON.stringify(payload)); |
||||
}; |
||||
|
||||
Object.defineProperty(QtProvider.prototype, "onmessage", { |
||||
set: function(handler) { |
||||
this.handlers.push(handler); |
||||
}, |
||||
});
|
||||
|
||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) { |
||||
web3.providers.QtProvider = QtProvider; |
||||
} |
||||
})(); |
||||
|
@ -0,0 +1,51 @@ |
||||
(function() { |
||||
var WebSocketProvider = function(host) { |
||||
// onmessage handlers
|
||||
this.handlers = []; |
||||
// queue will be filled with messages if send is invoked before the ws is ready
|
||||
this.queued = []; |
||||
this.ready = false; |
||||
|
||||
this.ws = new WebSocket(host); |
||||
|
||||
var self = this; |
||||
this.ws.onmessage = function(event) { |
||||
for(var i = 0; i < self.handlers.length; i++) { |
||||
self.handlers[i].call(self, JSON.parse(event.data), event) |
||||
} |
||||
}; |
||||
|
||||
this.ws.onopen = function() { |
||||
self.ready = true; |
||||
|
||||
for(var i = 0; i < self.queued.length; i++) { |
||||
// Resend
|
||||
self.send(self.queued[i]); |
||||
} |
||||
}; |
||||
}; |
||||
WebSocketProvider.prototype.send = function(payload) { |
||||
if(this.ready) { |
||||
var data = JSON.stringify(payload); |
||||
|
||||
this.ws.send(data); |
||||
} else { |
||||
this.queued.push(payload); |
||||
} |
||||
}; |
||||
|
||||
WebSocketProvider.prototype.onMessage = function(handler) { |
||||
this.handlers.push(handler); |
||||
}; |
||||
|
||||
WebSocketProvider.prototype.unload = function() { |
||||
this.ws.close(); |
||||
}; |
||||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { |
||||
set: function(provider) { this.onMessage(provider); } |
||||
}); |
||||
|
||||
if(typeof(web3) !== "undefined" && web3.providers !== undefined) { |
||||
web3.providers.WebSocketProvider = WebSocketProvider; |
||||
} |
||||
})(); |
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
// Main Ethereum library
|
||||
window.eth = { |
||||
prototype: Object(), |
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
var ethx = { |
||||
prototype: Object, |
||||
|
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
// The magic return variable. The magic return variable will be set during the execution of the QML call.
|
||||
(function(window) { |
||||
var Promise = window.Promise; |
@ -0,0 +1,30 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
// this function is included locally, but you can also include separately via a header definition
|
||||
function request(url, callback) { |
||||
var xhr = new XMLHttpRequest(); |
||||
xhr.onreadystatechange = (function(req) { |
||||
return function() { |
||||
if(req.readyState === 4) { |
||||
callback(req); |
||||
} |
||||
} |
||||
})(xhr); |
||||
xhr.open('GET', url, true); |
||||
xhr.send(''); |
||||
} |
@ -0,0 +1,30 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
function HandleMessage(data) { |
||||
var message; |
||||
try { message = JSON.parse(data) } catch(e) {}; |
||||
|
||||
if(message) { |
||||
switch(message.type) { |
||||
case "coinbase": |
||||
return eth.coinBase(); |
||||
case "block": |
||||
return eth.blockByNumber(0); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
window._messagingAdapter = function(data) { |
||||
navigator.qt.postMessage(data); |
||||
}; |
||||
|
||||
navigator.qt.onmessage = function(ev) { |
||||
var data = JSON.parse(ev.data) |
||||
|
||||
if(data._event !== undefined) { |
||||
eth.trigger(data._event, data.data); |
||||
} else { |
||||
if(data._seed) { |
||||
var cb = eth._callbacks[data._seed]; |
||||
if(cb) { |
||||
cb.call(this, data.data) |
||||
|
||||
// Remove the "trigger" callback
|
||||
delete eth._callbacks[ev._seed]; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
(function() { |
||||
if (typeof(Promise) === "undefined") |
||||
window.Promise = Q.Promise; |
||||
|
||||
var eth = web3.eth; |
||||
|
||||
web3.setProvider(new web3.providers.QtProvider()); |
||||
})() |
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
String.prototype.pad = function(l, r) { |
||||
if (r === undefined) { |
||||
r = l |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -1,3 +1,20 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
// 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) |
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 |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
@ -0,0 +1,254 @@ |
||||
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 |
||||
|
||||
Rectangle { |
||||
id: root |
||||
property var title: "Miner" |
||||
property var iconSource: "../miner.png" |
||||
property var menuItem |
||||
|
||||
color: "#00000000" |
||||
|
||||
ColumnLayout { |
||||
spacing: 10 |
||||
anchors.fill: parent |
||||
|
||||
Rectangle { |
||||
id: mainPane |
||||
color: "#00000000" |
||||
anchors { |
||||
top: parent.top |
||||
bottom: localTxPane.top |
||||
left: parent.left |
||||
right: parent.right |
||||
} |
||||
|
||||
Rectangle { |
||||
id: menu |
||||
height: 25 |
||||
anchors { |
||||
left: parent.left |
||||
} |
||||
|
||||
RowLayout { |
||||
id: tools |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
} |
||||
|
||||
Button { |
||||
text: "Start" |
||||
onClicked: { |
||||
eth.setGasPrice(minGasPrice.text || "10000000000000"); |
||||
if (eth.toggleMining()) { |
||||
this.text = "Stop"; |
||||
} else { |
||||
this.text = "Start"; |
||||
} |
||||
} |
||||
} |
||||
|
||||
Rectangle { |
||||
anchors.top: parent.top |
||||
anchors.topMargin: 2 |
||||
width: 200 |
||||
TextField { |
||||
id: minGasPrice |
||||
placeholderText: "Min Gas: 10000000000000" |
||||
width: 200 |
||||
validator: RegExpValidator { regExp: /\d*/ } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Column { |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
top: menu.bottom |
||||
topMargin: 5 |
||||
} |
||||
|
||||
Text { |
||||
text: "<b>Merged mining options</b>" |
||||
} |
||||
|
||||
TableView { |
||||
id: mergedMiningTable |
||||
height: 300 |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
} |
||||
Component { |
||||
id: checkBoxDelegate |
||||
|
||||
Item { |
||||
id: test |
||||
CheckBox { |
||||
anchors.fill: parent |
||||
checked: styleData.value |
||||
|
||||
onClicked: { |
||||
var model = mergedMiningModel.get(styleData.row) |
||||
|
||||
if (this.checked) { |
||||
model.id = txModel.createLocalTx(model.address, "0", "5000", "0", "") |
||||
} else { |
||||
txModel.removeWithId(model.id); |
||||
model.id = 0; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
TableViewColumn{ role: "checked" ; title: "" ; width: 40 ; delegate: checkBoxDelegate } |
||||
TableViewColumn{ role: "name" ; title: "Name" ; width: 480 } |
||||
model: ListModel { |
||||
objectName: "mergedMiningModel" |
||||
id: mergedMiningModel |
||||
function addMergedMiningOption(model) { |
||||
this.append(model); |
||||
} |
||||
} |
||||
Component.onCompleted: { |
||||
/* |
||||
// XXX Temp. replace with above eventually |
||||
var tmpItems = ["JEVCoin", "Some coin", "Other coin", "Etc coin"]; |
||||
var address = "e6716f9544a56c530d868e4bfbacb172315bdead"; |
||||
for (var i = 0; i < tmpItems.length; i++) { |
||||
mergedMiningModel.append({checked: false, name: tmpItems[i], address: address, id: 0, itemId: i}); |
||||
} |
||||
*/ |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Rectangle { |
||||
id: localTxPane |
||||
color: "#ececec" |
||||
border.color: "#cccccc" |
||||
border.width: 1 |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
bottom: parent.bottom |
||||
} |
||||
height: 300 |
||||
|
||||
ColumnLayout { |
||||
spacing: 10 |
||||
anchors.fill: parent |
||||
RowLayout { |
||||
id: newLocalTx |
||||
anchors { |
||||
left: parent.left |
||||
leftMargin: 5 |
||||
top: parent.top |
||||
topMargin: 5 |
||||
bottomMargin: 5 |
||||
} |
||||
|
||||
Text { |
||||
text: "Local tx" |
||||
} |
||||
|
||||
Rectangle { |
||||
width: 250 |
||||
color: "#00000000" |
||||
anchors.top: parent.top |
||||
anchors.topMargin: 2 |
||||
|
||||
TextField { |
||||
id: to |
||||
placeholderText: "To" |
||||
width: 250 |
||||
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ } |
||||
} |
||||
} |
||||
TextField { |
||||
property var defaultGas: "5000" |
||||
id: gas |
||||
placeholderText: "Gas" |
||||
text: defaultGas |
||||
validator: RegExpValidator { regExp: /\d*/ } |
||||
} |
||||
TextField { |
||||
id: gasPrice |
||||
placeholderText: "Price" |
||||
validator: RegExpValidator { regExp: /\d*/ } |
||||
} |
||||
TextField { |
||||
id: value |
||||
placeholderText: "Amount" |
||||
text: "0" |
||||
validator: RegExpValidator { regExp: /\d*/ } |
||||
} |
||||
TextField { |
||||
id: data |
||||
placeholderText: "Data" |
||||
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ } |
||||
} |
||||
Button { |
||||
text: "Create" |
||||
onClicked: { |
||||
if (to.text.length == 40 && gasPrice.text.length != 0 && value.text.length != 0 && gas.text.length != 0) { |
||||
txModel.createLocalTx(to.text, gasPrice.text, gas.text, value.text, data.text); |
||||
|
||||
to.text = ""; gasPrice.text = ""; |
||||
gas.text = gas.defaultGas; |
||||
value.text = "0" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
TableView { |
||||
id: txTableView |
||||
anchors { |
||||
top: newLocalTx.bottom |
||||
topMargin: 5 |
||||
left: parent.left |
||||
right: parent.right |
||||
bottom: parent.bottom |
||||
} |
||||
TableViewColumn{ role: "to" ; title: "To" ; width: 480 } |
||||
TableViewColumn{ role: "gas" ; title: "Gas" ; width: 100 } |
||||
TableViewColumn{ role: "gasPrice" ; title: "Gas Price" ; width: 100 } |
||||
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } |
||||
TableViewColumn{ role: "data" ; title: "Data" ; width: 100 } |
||||
|
||||
model: ListModel { |
||||
id: txModel |
||||
Component.onCompleted: { |
||||
} |
||||
function removeWithId(id) { |
||||
for (var i = 0; i < this.count; i++) { |
||||
if (txModel.get(i).id == id) { |
||||
this.remove(i); |
||||
eth.removeLocalTransaction(id) |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function createLocalTx(to, gasPrice, gas, value, data) { |
||||
var id = eth.addLocalTransaction(to, data, gas, gasPrice, value) |
||||
txModel.insert(0, {to: to, gas: gas, gasPrice: gasPrice, value: value, data: data, id: id}); |
||||
|
||||
return id |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,413 @@ |
||||
import QtQuick 2.0 |
||||
import QtWebKit 3.0 |
||||
import QtWebKit.experimental 1.0 |
||||
import QtQuick.Controls 1.0; |
||||
import QtQuick.Controls.Styles 1.0 |
||||
import QtQuick.Layouts 1.0; |
||||
import QtQuick.Window 2.1; |
||||
import Ethereum 1.0 |
||||
|
||||
Rectangle { |
||||
id: window |
||||
property var title: "Browser" |
||||
property var iconSource: "../browser.png" |
||||
property var menuItem |
||||
|
||||
property alias url: webview.url |
||||
property alias webView: webview |
||||
|
||||
property var cleanPath: false |
||||
property var open: function(url) { |
||||
if(!window.cleanPath) { |
||||
var uri = url; |
||||
if(!/.*\:\/\/.*/.test(uri)) { |
||||
uri = "http://" + uri; |
||||
} |
||||
|
||||
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ |
||||
|
||||
if(reg.test(uri)) { |
||||
uri.replace(reg, function(match, pre, domain, path) { |
||||
uri = pre; |
||||
|
||||
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); |
||||
var ip = []; |
||||
for(var i = 0, l = lookup.length; i < l; i++) { |
||||
ip.push(lookup.charCodeAt(i)) |
||||
} |
||||
|
||||
if(ip.length != 0) { |
||||
uri += lookup; |
||||
} else { |
||||
uri += domain; |
||||
} |
||||
|
||||
uri += path; |
||||
}); |
||||
} |
||||
|
||||
window.cleanPath = true; |
||||
|
||||
webview.url = uri; |
||||
|
||||
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>"); |
||||
uriNav.text = uri; |
||||
} else { |
||||
// Prevent inf loop. |
||||
window.cleanPath = false; |
||||
} |
||||
} |
||||
|
||||
Component.onCompleted: { |
||||
webview.url = "http://etherian.io" |
||||
} |
||||
|
||||
signal messages(var messages, int id); |
||||
onMessages: { |
||||
// Bit of a cheat to get proper JSON |
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages))) |
||||
webview.postEvent("messages", [m, id]); |
||||
} |
||||
|
||||
Item { |
||||
objectName: "root" |
||||
id: root |
||||
anchors.fill: parent |
||||
state: "inspectorShown" |
||||
|
||||
RowLayout { |
||||
id: navBar |
||||
height: 40 |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
leftMargin: 7 |
||||
} |
||||
|
||||
Button { |
||||
id: back |
||||
onClicked: { |
||||
webview.goBack() |
||||
} |
||||
style: ButtonStyle { |
||||
background: Image { |
||||
source: "../back.png" |
||||
width: 30 |
||||
height: 30 |
||||
} |
||||
} |
||||
} |
||||
|
||||
TextField { |
||||
anchors { |
||||
left: back.right |
||||
right: toggleInspector.left |
||||
leftMargin: 5 |
||||
rightMargin: 5 |
||||
} |
||||
text: "http://etherian.io" |
||||
id: uriNav |
||||
y: parent.height / 2 - this.height / 2 |
||||
|
||||
Keys.onReturnPressed: { |
||||
webview.url = this.text; |
||||
} |
||||
} |
||||
|
||||
Button { |
||||
id: toggleInspector |
||||
anchors { |
||||
right: parent.right |
||||
} |
||||
iconSource: "../bug.png" |
||||
onClicked: { |
||||
if(inspector.visible == true){ |
||||
inspector.visible = false |
||||
}else{ |
||||
inspector.visible = true |
||||
inspector.url = webview.experimental.remoteInspectorUrl |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
WebView { |
||||
objectName: "webView" |
||||
id: webview |
||||
anchors { |
||||
left: parent.left |
||||
right: parent.right |
||||
bottom: parent.bottom |
||||
top: navBar.bottom |
||||
} |
||||
|
||||
//property var cleanPath: false |
||||
onNavigationRequested: { |
||||
window.open(request.url.toString()); |
||||
} |
||||
|
||||
function sendMessage(data) { |
||||
webview.experimental.postMessage(JSON.stringify(data)) |
||||
} |
||||
|
||||
|
||||
experimental.preferences.javascriptEnabled: true |
||||
experimental.preferences.navigatorQtObjectEnabled: true |
||||
experimental.preferences.developerExtrasEnabled: true |
||||
//experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] |
||||
experimental.userScripts: ["../ext/q.js", "../ext/eth.js/main.js", "../ext/eth.js/qt.js", "../ext/setup.js"] |
||||
experimental.onMessageReceived: { |
||||
console.log("[onMessageReceived]: ", message.data) |
||||
// TODO move to messaging.js |
||||
var data = JSON.parse(message.data) |
||||
|
||||
try { |
||||
switch(data.call) { |
||||
case "compile": |
||||
postData(data._id, eth.compile(data.args[0])) |
||||
break |
||||
|
||||
case "coinbase": |
||||
postData(data._id, eth.coinBase()) |
||||
|
||||
case "account": |
||||
postData(data._id, eth.key().address); |
||||
|
||||
case "isListening": |
||||
postData(data._id, eth.isListening()) |
||||
|
||||
break |
||||
|
||||
case "isMining": |
||||
postData(data._id, eth.isMining()) |
||||
|
||||
break |
||||
|
||||
case "peerCount": |
||||
postData(data._id, eth.peerCount()) |
||||
|
||||
break |
||||
|
||||
case "countAt": |
||||
require(1) |
||||
postData(data._id, eth.txCountAt(data.args[0])) |
||||
|
||||
break |
||||
|
||||
case "codeAt": |
||||
require(1) |
||||
var code = eth.codeAt(data.args[0]) |
||||
postData(data._id, code); |
||||
|
||||
break |
||||
|
||||
case "blockByNumber": |
||||
require(1) |
||||
var block = eth.blockByNumber(data.args[0]) |
||||
postData(data._id, block) |
||||
break |
||||
|
||||
case "blockByHash": |
||||
require(1) |
||||
var block = eth.blockByHash(data.args[0]) |
||||
postData(data._id, block) |
||||
break |
||||
|
||||
require(2) |
||||
var block = eth.blockByHash(data.args[0]) |
||||
postData(data._id, block.transactions[data.args[1]]) |
||||
break |
||||
|
||||
case "transactionByHash": |
||||
case "transactionByNumber": |
||||
require(2) |
||||
|
||||
var block; |
||||
if (data.call === "transactionByHash") |
||||
block = eth.blockByHash(data.args[0]) |
||||
else |
||||
block = eth.blockByNumber(data.args[0]) |
||||
|
||||
var tx = block.transactions.get(data.args[1]) |
||||
|
||||
postData(data._id, tx) |
||||
break |
||||
|
||||
case "uncleByHash": |
||||
case "uncleByNumber": |
||||
require(2) |
||||
|
||||
var block; |
||||
if (data.call === "uncleByHash") |
||||
block = eth.blockByHash(data.args[0]) |
||||
else |
||||
block = eth.blockByNumber(data.args[0]) |
||||
|
||||
var uncle = block.uncles.get(data.args[1]) |
||||
|
||||
postData(data._id, uncle) |
||||
|
||||
break |
||||
|
||||
case "transact": |
||||
require(5) |
||||
|
||||
var tx = eth.transact(data.args) |
||||
postData(data._id, tx) |
||||
|
||||
break |
||||
|
||||
case "stateAt": |
||||
require(2); |
||||
|
||||
var storage = eth.storageAt(data.args[0], data.args[1]); |
||||
postData(data._id, storage) |
||||
|
||||
break |
||||
|
||||
case "call": |
||||
require(1); |
||||
var ret = eth.call(data.args) |
||||
postData(data._id, ret) |
||||
break |
||||
|
||||
case "balanceAt": |
||||
require(1); |
||||
|
||||
postData(data._id, eth.balanceAt(data.args[0])); |
||||
break |
||||
|
||||
case "watch": |
||||
require(2) |
||||
eth.watch(data.args[0], data.args[1]) |
||||
|
||||
case "disconnect": |
||||
require(1) |
||||
postData(data._id, null) |
||||
break; |
||||
|
||||
case "messages": |
||||
require(1); |
||||
|
||||
var messages = JSON.parse(eth.getMessages(data.args[0])) |
||||
postData(data._id, messages) |
||||
break |
||||
|
||||
case "mutan": |
||||
require(1) |
||||
|
||||
var code = eth.compileMutan(data.args[0]) |
||||
postData(data._id, "0x"+code) |
||||
break; |
||||
|
||||
case "newFilterString": |
||||
require(1) |
||||
var id = eth.newFilterString(data.args[0]) |
||||
postData(data._id, id); |
||||
break; |
||||
|
||||
case "newFilter": |
||||
require(1) |
||||
var id = eth.newFilter(data.args[0]) |
||||
|
||||
postData(data._id, id); |
||||
break; |
||||
|
||||
case "getMessages": |
||||
require(1); |
||||
|
||||
var messages = eth.messages(data.args[0]); |
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages))) |
||||
postData(data._id, m); |
||||
|
||||
break; |
||||
|
||||
case "deleteFilter": |
||||
require(1); |
||||
eth.uninstallFilter(data.args[0]) |
||||
break; |
||||
} |
||||
} catch(e) { |
||||
console.log(data.call + ": " + e) |
||||
|
||||
postData(data._id, null); |
||||
} |
||||
} |
||||
|
||||
|
||||
function post(seed, data) { |
||||
postData(data._id, data) |
||||
} |
||||
|
||||
function require(args, num) { |
||||
if(args.length < num) { |
||||
throw("required argument count of "+num+" got "+args.length); |
||||
} |
||||
} |
||||
function postData(seed, data) { |
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed})) |
||||
} |
||||
function postEvent(event, data) { |
||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) |
||||
} |
||||
|
||||
function onWatchedCb(data, id) { |
||||
var messages = JSON.parse(data) |
||||
postEvent("watched:"+id, messages) |
||||
} |
||||
|
||||
function onNewBlockCb(block) { |
||||
postEvent("block:new", block) |
||||
} |
||||
function onObjectChangeCb(stateObject) { |
||||
postEvent("object:"+stateObject.address(), stateObject) |
||||
} |
||||
function onStorageChangeCb(storageObject) { |
||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); |
||||
postEvent(ev, [storageObject.address, storageObject.value]) |
||||
} |
||||
} |
||||
|
||||
|
||||
Rectangle { |
||||
id: sizeGrip |
||||
color: "gray" |
||||
visible: false |
||||
height: 10 |
||||
anchors { |
||||
left: root.left |
||||
right: root.right |
||||
} |
||||
y: Math.round(root.height * 2 / 3) |
||||
|
||||
MouseArea { |
||||
anchors.fill: parent |
||||
drag.target: sizeGrip |
||||
drag.minimumY: 0 |
||||
drag.maximumY: root.height |
||||
drag.axis: Drag.YAxis |
||||
} |
||||
} |
||||
|
||||
WebView { |
||||
id: inspector |
||||
visible: false |
||||
anchors { |
||||
left: root.left |
||||
right: root.right |
||||
top: sizeGrip.bottom |
||||
bottom: root.bottom |
||||
} |
||||
} |
||||
|
||||
states: [ |
||||
State { |
||||
name: "inspectorShown" |
||||
PropertyChanges { |
||||
target: inspector |
||||
} |
||||
} |
||||
] |
||||
} |
||||
} |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,53 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
|
||||
"gopkg.in/qml.v1" |
||||
) |
||||
|
||||
func ErrorWindow(err error) { |
||||
engine := qml.NewEngine() |
||||
component, e := engine.LoadString("local", qmlErr) |
||||
if e != nil { |
||||
fmt.Println("err:", err) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
win := component.CreateWindow(nil) |
||||
win.Root().ObjectByName("label").Set("text", err.Error()) |
||||
win.Show() |
||||
win.Wait() |
||||
} |
||||
|
||||
const qmlErr = ` |
||||
import QtQuick 2.0; import QtQuick.Controls 1.0; |
||||
ApplicationWindow { |
||||
width: 600; height: 150; |
||||
flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint |
||||
title: "Error" |
||||
Text { |
||||
x: parent.width / 2 - this.width / 2; |
||||
y: parent.height / 2 - this.height / 2; |
||||
objectName: "label"; |
||||
} |
||||
} |
||||
` |
@ -0,0 +1,143 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
|
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/javascript" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/ui/qt" |
||||
"github.com/ethereum/go-ethereum/xeth" |
||||
"gopkg.in/qml.v1" |
||||
) |
||||
|
||||
type AppContainer interface { |
||||
Create() error |
||||
Destroy() |
||||
|
||||
Window() *qml.Window |
||||
Engine() *qml.Engine |
||||
|
||||
NewBlock(*types.Block) |
||||
NewWatcher(chan bool) |
||||
Messages(state.Messages, string) |
||||
Post(string, int) |
||||
} |
||||
|
||||
type ExtApplication struct { |
||||
*xeth.JSXEth |
||||
eth core.EthManager |
||||
|
||||
events event.Subscription |
||||
watcherQuitChan chan bool |
||||
|
||||
filters map[string]*core.Filter |
||||
|
||||
container AppContainer |
||||
lib *UiLib |
||||
} |
||||
|
||||
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication { |
||||
return &ExtApplication{ |
||||
JSXEth: xeth.NewJSXEth(lib.eth), |
||||
eth: lib.eth, |
||||
watcherQuitChan: make(chan bool), |
||||
filters: make(map[string]*core.Filter), |
||||
container: container, |
||||
lib: lib, |
||||
} |
||||
} |
||||
|
||||
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 { |
||||
guilogger.Errorln(err) |
||||
return |
||||
} |
||||
|
||||
// Subscribe to events
|
||||
mux := app.lib.eth.EventMux() |
||||
app.events = mux.Subscribe(core.NewBlockEvent{}, state.Messages(nil)) |
||||
|
||||
// Call the main loop
|
||||
go app.mainLoop() |
||||
|
||||
app.container.NewWatcher(app.watcherQuitChan) |
||||
|
||||
win := app.container.Window() |
||||
win.Show() |
||||
win.Wait() |
||||
|
||||
app.stop() |
||||
} |
||||
|
||||
func (app *ExtApplication) stop() { |
||||
app.events.Unsubscribe() |
||||
|
||||
// Kill the main loop
|
||||
app.watcherQuitChan <- true |
||||
|
||||
app.container.Destroy() |
||||
} |
||||
|
||||
func (app *ExtApplication) mainLoop() { |
||||
for ev := range app.events.Chan() { |
||||
switch ev := ev.(type) { |
||||
case core.NewBlockEvent: |
||||
app.container.NewBlock(ev.Block) |
||||
|
||||
case state.Messages: |
||||
for id, filter := range app.filters { |
||||
msgs := filter.FilterMessages(ev) |
||||
if len(msgs) > 0 { |
||||
app.container.Messages(msgs, id) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (self *ExtApplication) Watch(filterOptions map[string]interface{}, identifier string) { |
||||
self.filters[identifier] = qt.NewFilterFromMap(filterOptions, self.eth) |
||||
} |
||||
|
||||
func (self *ExtApplication) GetMessages(object map[string]interface{}) string { |
||||
filter := qt.NewFilterFromMap(object, self.eth) |
||||
|
||||
messages := filter.Find() |
||||
var msgs []javascript.JSMessage |
||||
for _, m := range messages { |
||||
msgs = append(msgs, javascript.NewJSMessage(m)) |
||||
} |
||||
|
||||
b, err := json.Marshal(msgs) |
||||
if err != nil { |
||||
return "{\"error\":" + err.Error() + "}" |
||||
} |
||||
|
||||
return string(b) |
||||
} |
@ -0,0 +1,85 @@ |
||||
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"runtime" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/xeth" |
||||
"gopkg.in/qml.v1" |
||||
) |
||||
|
||||
type QmlApplication struct { |
||||
win *qml.Window |
||||
engine *qml.Engine |
||||
lib *UiLib |
||||
path string |
||||
} |
||||
|
||||
func NewQmlApplication(path string, lib *UiLib) *QmlApplication { |
||||
engine := qml.NewEngine() |
||||
return &QmlApplication{engine: engine, path: path, lib: lib} |
||||
} |
||||
|
||||
func (app *QmlApplication) Create() error { |
||||
path := string(app.path) |
||||
|
||||
// For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it
|
||||
if app.path[0] == '/' && runtime.GOOS == "windows" { |
||||
path = app.path[1:] |
||||
} |
||||
|
||||
component, err := app.engine.LoadFile(path) |
||||
if err != nil { |
||||
guilogger.Warnln(err) |
||||
} |
||||
app.win = component.CreateWindow(nil) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (app *QmlApplication) Destroy() { |
||||
app.engine.Destroy() |
||||
} |
||||
|
||||
func (app *QmlApplication) NewWatcher(quitChan chan bool) { |
||||
} |
||||
|
||||
// Events
|
||||
func (app *QmlApplication) NewBlock(block *types.Block) { |
||||
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} |
||||
app.win.Call("onNewBlockCb", pblock) |
||||
} |
||||
|
||||
func (self *QmlApplication) Messages(msgs state.Messages, id string) { |
||||
fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD") |
||||
} |
||||
|
||||
// Getters
|
||||
func (app *QmlApplication) Engine() *qml.Engine { |
||||
return app.engine |
||||
} |
||||
func (app *QmlApplication) Window() *qml.Window { |
||||
return app.win |
||||
} |
||||
|
||||
func (app *QmlApplication) Post(data string, s int) {} |
@ -0,0 +1,40 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"crypto/elliptic" |
||||
"fmt" |
||||
"log" |
||||
"net" |
||||
"os" |
||||
|
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/p2p" |
||||
) |
||||
|
||||
func main() { |
||||
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel)) |
||||
key, _ := crypto.GenerateKey() |
||||
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) |
||||
|
||||
srv := p2p.Server{ |
||||
MaxPeers: 10, |
||||
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)), |
||||
ListenAddr: ":30301", |
||||
NAT: p2p.UPNP(), |
||||
} |
||||
if err := srv.Start(); err != nil { |
||||
fmt.Println("could not start server:", err) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// add seed peers
|
||||
seed, err := net.ResolveTCPAddr("tcp", "poc-7.ethdev.com:30300") |
||||
if err != nil { |
||||
fmt.Println("couldn't resolve:", err) |
||||
os.Exit(1) |
||||
} |
||||
srv.SuggestPeer(seed.IP, seed.Port, nil) |
||||
|
||||
select {} |
||||
} |
@ -0,0 +1,70 @@ |
||||
package utils |
||||
|
||||
import ( |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/vm" |
||||
) |
||||
|
||||
type VMEnv struct { |
||||
state *state.StateDB |
||||
block *types.Block |
||||
|
||||
transactor []byte |
||||
value *big.Int |
||||
|
||||
depth int |
||||
Gas *big.Int |
||||
} |
||||
|
||||
func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *big.Int) *VMEnv { |
||||
return &VMEnv{ |
||||
state: state, |
||||
block: block, |
||||
transactor: transactor, |
||||
value: value, |
||||
} |
||||
} |
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.transactor } |
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } |
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } |
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } |
||||
func (self *VMEnv) Time() int64 { return self.block.Time } |
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } |
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } |
||||
func (self *VMEnv) Value() *big.Int { return self.value } |
||||
func (self *VMEnv) State() *state.StateDB { return self.state } |
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit } |
||||
func (self *VMEnv) Depth() int { return self.depth } |
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i } |
||||
func (self *VMEnv) AddLog(log state.Log) { |
||||
self.state.AddLog(log) |
||||
} |
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { |
||||
return vm.Transfer(from, to, amount) |
||||
} |
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution { |
||||
return core.NewExecution(self, addr, data, gas, price, value) |
||||
} |
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||
exe := self.vm(addr, data, gas, price, value) |
||||
ret, err := exe.Call(addr, caller) |
||||
self.Gas = exe.Gas |
||||
|
||||
return ret, err |
||||
} |
||||
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||
exe := self.vm(caller.Address(), data, gas, price, value) |
||||
return exe.Call(addr, caller) |
||||
} |
||||
|
||||
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { |
||||
exe := self.vm(addr, data, gas, price, value) |
||||
return exe.Create(caller) |
||||
} |
@ -0,0 +1,84 @@ |
||||
package rle |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
|
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
) |
||||
|
||||
const ( |
||||
token byte = 0xfe |
||||
emptyShaToken = 0xfd |
||||
emptyListShaToken = 0xfe |
||||
tokenToken = 0xff |
||||
) |
||||
|
||||
var empty = crypto.Sha3([]byte("")) |
||||
var emptyList = crypto.Sha3([]byte{0x80}) |
||||
|
||||
func Decompress(dat []byte) ([]byte, error) { |
||||
buf := new(bytes.Buffer) |
||||
|
||||
for i := 0; i < len(dat); i++ { |
||||
if dat[i] == token { |
||||
if i+1 < len(dat) { |
||||
switch dat[i+1] { |
||||
case emptyShaToken: |
||||
buf.Write(empty) |
||||
case emptyListShaToken: |
||||
buf.Write(emptyList) |
||||
case tokenToken: |
||||
buf.WriteByte(token) |
||||
default: |
||||
buf.Write(make([]byte, int(dat[i+1]-2))) |
||||
} |
||||
i++ |
||||
} else { |
||||
return nil, errors.New("error reading bytes. token encountered without proceeding bytes") |
||||
} |
||||
} else { |
||||
buf.WriteByte(dat[i]) |
||||
} |
||||
} |
||||
|
||||
return buf.Bytes(), nil |
||||
} |
||||
|
||||
func compressChunk(dat []byte) (ret []byte, n int) { |
||||
switch { |
||||
case dat[0] == token: |
||||
return []byte{token, tokenToken}, 1 |
||||
case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0: |
||||
j := 0 |
||||
for j <= 254 && j < len(dat) { |
||||
if dat[j] != 0 { |
||||
break |
||||
} |
||||
j++ |
||||
} |
||||
return []byte{token, byte(j + 2)}, j |
||||
case len(dat) >= 32: |
||||
if dat[0] == empty[0] && bytes.Compare(dat[:32], empty) == 0 { |
||||
return []byte{token, emptyShaToken}, 32 |
||||
} else if dat[0] == emptyList[0] && bytes.Compare(dat[:32], emptyList) == 0 { |
||||
return []byte{token, emptyListShaToken}, 32 |
||||
} |
||||
fallthrough |
||||
default: |
||||
return dat[:1], 1 |
||||
} |
||||
} |
||||
|
||||
func Compress(dat []byte) []byte { |
||||
buf := new(bytes.Buffer) |
||||
|
||||
i := 0 |
||||
for i < len(dat) { |
||||
b, n := compressChunk(dat[i:]) |
||||
buf.Write(b) |
||||
i += n |
||||
} |
||||
|
||||
return buf.Bytes() |
||||
} |
@ -0,0 +1,118 @@ |
||||
package rle |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
checker "gopkg.in/check.v1" |
||||
) |
||||
|
||||
func Test(t *testing.T) { checker.TestingT(t) } |
||||
|
||||
type CompressionRleSuite struct{} |
||||
|
||||
var _ = checker.Suite(&CompressionRleSuite{}) |
||||
|
||||
func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) { |
||||
exp := []byte{0xc5, 0xd2, 0x46, 0x1, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x3, 0xc0, 0xe5, 0x0, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x4, 0x5d, 0x85, 0xa4, 0x70} |
||||
res, err := Decompress([]byte{token, 0xfd}) |
||||
c.Assert(err, checker.IsNil) |
||||
c.Assert(res, checker.DeepEquals, exp) |
||||
// if bytes.Compare(res, exp) != 0 {
|
||||
// t.Error("empty sha3", res)
|
||||
// }
|
||||
|
||||
exp = []byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x1, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21} |
||||
res, err = Decompress([]byte{token, 0xfe}) |
||||
c.Assert(err, checker.IsNil) |
||||
c.Assert(res, checker.DeepEquals, exp) |
||||
// if bytes.Compare(res, exp) != 0 {
|
||||
// t.Error("0x80 sha3", res)
|
||||
// }
|
||||
|
||||
res, err = Decompress([]byte{token, 0xff}) |
||||
c.Assert(err, checker.IsNil) |
||||
c.Assert(res, checker.DeepEquals, []byte{token}) |
||||
// if bytes.Compare(res, []byte{token}) != 0 {
|
||||
// t.Error("token", res)
|
||||
// }
|
||||
|
||||
res, err = Decompress([]byte{token, 12}) |
||||
c.Assert(err, checker.IsNil) |
||||
c.Assert(res, checker.DeepEquals, make([]byte, 10)) |
||||
// if bytes.Compare(res, make([]byte, 10)) != 0 {
|
||||
// t.Error("10 * zero", res)
|
||||
// }
|
||||
} |
||||
|
||||
// func TestDecompressMulti(t *testing.T) {
|
||||
// res, err := Decompress([]byte{token, 0xfd, token, 0xfe, token, 12})
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
|
||||
// var exp []byte
|
||||
// exp = append(exp, crypto.Sha3([]byte(""))...)
|
||||
// exp = append(exp, crypto.Sha3([]byte{0x80})...)
|
||||
// exp = append(exp, make([]byte, 10)...)
|
||||
|
||||
// if bytes.Compare(res, res) != 0 {
|
||||
// t.Error("Expected", exp, "result", res)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestCompressSimple(t *testing.T) {
|
||||
// res := Compress([]byte{0, 0, 0, 0, 0})
|
||||
// if bytes.Compare(res, []byte{token, 7}) != 0 {
|
||||
// t.Error("5 * zero", res)
|
||||
// }
|
||||
|
||||
// res = Compress(crypto.Sha3([]byte("")))
|
||||
// if bytes.Compare(res, []byte{token, emptyShaToken}) != 0 {
|
||||
// t.Error("empty sha", res)
|
||||
// }
|
||||
|
||||
// res = Compress(crypto.Sha3([]byte{0x80}))
|
||||
// if bytes.Compare(res, []byte{token, emptyListShaToken}) != 0 {
|
||||
// t.Error("empty list sha", res)
|
||||
// }
|
||||
|
||||
// res = Compress([]byte{token})
|
||||
// if bytes.Compare(res, []byte{token, tokenToken}) != 0 {
|
||||
// t.Error("token", res)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestCompressMulti(t *testing.T) {
|
||||
// in := []byte{0, 0, 0, 0, 0}
|
||||
// in = append(in, crypto.Sha3([]byte(""))...)
|
||||
// in = append(in, crypto.Sha3([]byte{0x80})...)
|
||||
// in = append(in, token)
|
||||
// res := Compress(in)
|
||||
|
||||
// exp := []byte{token, 7, token, emptyShaToken, token, emptyListShaToken, token, tokenToken}
|
||||
// if bytes.Compare(res, exp) != 0 {
|
||||
// t.Error("expected", exp, "got", res)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestCompressDecompress(t *testing.T) {
|
||||
// var in []byte
|
||||
|
||||
// for i := 0; i < 20; i++ {
|
||||
// in = append(in, []byte{0, 0, 0, 0, 0}...)
|
||||
// in = append(in, crypto.Sha3([]byte(""))...)
|
||||
// in = append(in, crypto.Sha3([]byte{0x80})...)
|
||||
// in = append(in, []byte{123, 2, 19, 89, 245, 254, 255, token, 98, 233}...)
|
||||
// in = append(in, token)
|
||||
// }
|
||||
|
||||
// c := Compress(in)
|
||||
// d, err := Decompress(c)
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
|
||||
// if bytes.Compare(d, in) != 0 {
|
||||
// t.Error("multi failed\n", d, "\n", in)
|
||||
// }
|
||||
// }
|
@ -0,0 +1,12 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
|
@ -0,0 +1,50 @@ |
||||
package core |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/vm" |
||||
) |
||||
|
||||
func Disassemble(script []byte) (asm []string) { |
||||
pc := new(big.Int) |
||||
for { |
||||
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { |
||||
return |
||||
} |
||||
|
||||
// Get the memory location of pc
|
||||
val := script[pc.Int64()] |
||||
// Get the opcode (it must be an opcode!)
|
||||
op := vm.OpCode(val) |
||||
|
||||
asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) |
||||
|
||||
switch op { |
||||
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, |
||||
vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, |
||||
vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, |
||||
vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, |
||||
vm.PUSH30, vm.PUSH31, vm.PUSH32: |
||||
pc.Add(pc, ethutil.Big1) |
||||
a := int64(op) - int64(vm.PUSH1) + 1 |
||||
if int(pc.Int64()+a) > len(script) { |
||||
return |
||||
} |
||||
|
||||
data := script[pc.Int64() : pc.Int64()+a] |
||||
if len(data) == 0 { |
||||
data = []byte{0} |
||||
} |
||||
asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) |
||||
|
||||
pc.Add(pc, big.NewInt(a-1)) |
||||
} |
||||
|
||||
pc.Add(pc, ethutil.Big1) |
||||
} |
||||
|
||||
return asm |
||||
} |
@ -0,0 +1,371 @@ |
||||
package core |
||||
|
||||
import ( |
||||
"bytes" |
||||
"container/list" |
||||
"errors" |
||||
"fmt" |
||||
"math/big" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/pow" |
||||
"github.com/ethereum/go-ethereum/pow/ezp" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
"github.com/ethereum/go-ethereum/wire" |
||||
) |
||||
|
||||
var statelogger = logger.NewLogger("BLOCK") |
||||
|
||||
type Peer interface { |
||||
Inbound() bool |
||||
LastSend() time.Time |
||||
LastPong() int64 |
||||
Host() []byte |
||||
Port() uint16 |
||||
Version() string |
||||
PingTime() string |
||||
Connected() *int32 |
||||
Caps() *ethutil.Value |
||||
} |
||||
|
||||
type EthManager interface { |
||||
BlockManager() *BlockManager |
||||
ChainManager() *ChainManager |
||||
TxPool() *TxPool |
||||
Broadcast(msgType wire.MsgType, data []interface{}) |
||||
PeerCount() int |
||||
IsMining() bool |
||||
IsListening() bool |
||||
Peers() *list.List |
||||
KeyManager() *crypto.KeyManager |
||||
ClientIdentity() wire.ClientIdentity |
||||
Db() ethutil.Database |
||||
EventMux() *event.TypeMux |
||||
} |
||||
|
||||
type BlockManager struct { |
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex |
||||
// Canonical block chain
|
||||
bc *ChainManager |
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int |
||||
// Proof of work used for validating
|
||||
Pow pow.PoW |
||||
|
||||
txpool *TxPool |
||||
|
||||
// The last attempted block is mainly used for debugging purposes
|
||||
// This does not have to be a valid block and will be set during
|
||||
// 'Process' & canonical validation.
|
||||
lastAttemptedBlock *types.Block |
||||
|
||||
events event.Subscription |
||||
|
||||
eventMux *event.TypeMux |
||||
} |
||||
|
||||
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager { |
||||
sm := &BlockManager{ |
||||
mem: make(map[string]*big.Int), |
||||
Pow: ezp.New(), |
||||
bc: chainManager, |
||||
eventMux: eventMux, |
||||
txpool: txpool, |
||||
} |
||||
|
||||
return sm |
||||
} |
||||
|
||||
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) { |
||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase) |
||||
coinbase.SetGasPool(block.CalcGasLimit(parent)) |
||||
|
||||
// Process the transactions on to current block
|
||||
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return receipts, nil |
||||
} |
||||
|
||||
func (self *BlockManager) ApplyTransactions(coinbase *state.StateObject, state *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) { |
||||
var ( |
||||
receipts types.Receipts |
||||
handled, unhandled types.Transactions |
||||
erroneous types.Transactions |
||||
totalUsedGas = big.NewInt(0) |
||||
err error |
||||
cumulativeSum = new(big.Int) |
||||
) |
||||
|
||||
done: |
||||
for i, tx := range txs { |
||||
// If we are mining this block and validating we want to set the logs back to 0
|
||||
state.EmptyLogs() |
||||
|
||||
txGas := new(big.Int).Set(tx.Gas()) |
||||
|
||||
cb := state.GetStateObject(coinbase.Address()) |
||||
st := NewStateTransition(cb, tx, state, block) |
||||
_, err = st.TransitionState() |
||||
if err != nil { |
||||
switch { |
||||
case IsNonceErr(err): |
||||
err = nil // ignore error
|
||||
continue |
||||
case IsGasLimitErr(err): |
||||
unhandled = txs[i:] |
||||
|
||||
break done |
||||
default: |
||||
statelogger.Infoln(err) |
||||
erroneous = append(erroneous, tx) |
||||
err = nil |
||||
} |
||||
} |
||||
|
||||
txGas.Sub(txGas, st.gas) |
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice())) |
||||
|
||||
// Update the state with pending changes
|
||||
state.Update(txGas) |
||||
|
||||
cumulative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas)) |
||||
receipt := types.NewReceipt(state.Root(), cumulative) |
||||
receipt.SetLogs(state.Logs()) |
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) |
||||
chainlogger.Debugln(receipt) |
||||
|
||||
// Notify all subscribers
|
||||
if !transientProcess { |
||||
go self.eventMux.Post(TxPostEvent{tx}) |
||||
} |
||||
|
||||
receipts = append(receipts, receipt) |
||||
handled = append(handled, tx) |
||||
|
||||
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" { |
||||
state.CreateOutputForDiff() |
||||
} |
||||
} |
||||
|
||||
block.Reward = cumulativeSum |
||||
block.GasUsed = totalUsedGas |
||||
|
||||
return receipts, handled, unhandled, erroneous, err |
||||
} |
||||
|
||||
func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) { |
||||
// Processing a blocks may never happen simultaneously
|
||||
sm.mutex.Lock() |
||||
defer sm.mutex.Unlock() |
||||
|
||||
if sm.bc.HasBlock(block.Hash()) { |
||||
return nil, nil, &KnownBlockError{block.Number, block.Hash()} |
||||
} |
||||
|
||||
if !sm.bc.HasBlock(block.PrevHash) { |
||||
return nil, nil, ParentError(block.PrevHash) |
||||
} |
||||
parent := sm.bc.GetBlock(block.PrevHash) |
||||
|
||||
return sm.ProcessWithParent(block, parent) |
||||
} |
||||
|
||||
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) { |
||||
sm.lastAttemptedBlock = block |
||||
|
||||
state := parent.State().Copy() |
||||
|
||||
// Defer the Undo on the Trie. If the block processing happened
|
||||
// we don't want to undo but since undo only happens on dirty
|
||||
// nodes this won't happen because Commit would have been called
|
||||
// before that.
|
||||
defer state.Reset() |
||||
|
||||
// Block validation
|
||||
if err = sm.ValidateBlock(block, parent); err != nil { |
||||
return |
||||
} |
||||
|
||||
receipts, err := sm.TransitionState(state, parent, block) |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
rbloom := types.CreateBloom(receipts) |
||||
if bytes.Compare(rbloom, block.LogsBloom) != 0 { |
||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) |
||||
return |
||||
} |
||||
|
||||
txSha := types.DeriveSha(block.Transactions()) |
||||
if bytes.Compare(txSha, block.TxSha) != 0 { |
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha) |
||||
return |
||||
} |
||||
|
||||
receiptSha := types.DeriveSha(receipts) |
||||
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 { |
||||
//chainlogger.Debugf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||
fmt.Printf("%x\n", ethutil.Encode(receipts)) |
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha) |
||||
return |
||||
} |
||||
|
||||
if err = sm.AccumelateRewards(state, block, parent); err != nil { |
||||
return |
||||
} |
||||
|
||||
state.Update(ethutil.Big0) |
||||
|
||||
if !block.State().Cmp(state) { |
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root()) |
||||
return |
||||
} |
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if td, ok := sm.CalculateTD(block); ok { |
||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||
state.Sync() |
||||
|
||||
messages := state.Manifest().Messages |
||||
state.Manifest().Reset() |
||||
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4]) |
||||
|
||||
sm.txpool.RemoveSet(block.Transactions()) |
||||
|
||||
return td, messages, nil |
||||
} else { |
||||
return nil, nil, errors.New("total diff failed") |
||||
} |
||||
} |
||||
|
||||
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) { |
||||
uncleDiff := new(big.Int) |
||||
for _, uncle := range block.Uncles { |
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) |
||||
} |
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int) |
||||
td = td.Add(sm.bc.Td(), uncleDiff) |
||||
td = td.Add(td, block.Difficulty) |
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(sm.bc.Td()) > 0 { |
||||
return td, true |
||||
} |
||||
|
||||
return nil, false |
||||
} |
||||
|
||||
// Validates the current block. Returns an error if the block was invalid,
|
||||
// an uncle or anything that isn't on the current block chain.
|
||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error { |
||||
expd := CalcDifficulty(block, parent) |
||||
if expd.Cmp(block.Difficulty) < 0 { |
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) |
||||
} |
||||
|
||||
diff := block.Time - parent.Time |
||||
if diff < 0 { |
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock().Time) |
||||
} |
||||
|
||||
/* XXX |
||||
// New blocks must be within the 15 minute range of the last block.
|
||||
if diff > int64(15*time.Minute) { |
||||
return ValidationError("Block is too far in the future of last block (> 15 minutes)") |
||||
} |
||||
*/ |
||||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !sm.Pow.Verify(block /*block.HashNoNonce(), block.Difficulty, block.Nonce*/) { |
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce)) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error { |
||||
reward := new(big.Int).Set(BlockReward) |
||||
|
||||
knownUncles := ethutil.Set(parent.Uncles) |
||||
nonces := ethutil.NewSet(block.Nonce) |
||||
for _, uncle := range block.Uncles { |
||||
if nonces.Include(uncle.Nonce) { |
||||
// Error not unique
|
||||
return UncleError("Uncle not unique") |
||||
} |
||||
|
||||
uncleParent := sm.bc.GetBlock(uncle.PrevHash) |
||||
if uncleParent == nil { |
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4])) |
||||
} |
||||
|
||||
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { |
||||
return UncleError("Uncle too old") |
||||
} |
||||
|
||||
if knownUncles.Include(uncle.Hash()) { |
||||
return UncleError("Uncle in chain") |
||||
} |
||||
|
||||
nonces.Insert(uncle.Nonce) |
||||
|
||||
r := new(big.Int) |
||||
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) |
||||
|
||||
uncleAccount := statedb.GetAccount(uncle.Coinbase) |
||||
uncleAccount.AddAmount(r) |
||||
|
||||
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) |
||||
} |
||||
|
||||
// Get the account associated with the coinbase
|
||||
account := statedb.GetAccount(block.Coinbase) |
||||
// Reward amount of ether to the coinbase address
|
||||
account.AddAmount(reward) |
||||
|
||||
statedb.Manifest().AddMessage(&state.Message{ |
||||
To: block.Coinbase, |
||||
Input: nil, |
||||
Origin: nil, |
||||
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number, |
||||
Value: new(big.Int).Add(reward, block.Reward), |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) { |
||||
if !sm.bc.HasBlock(block.PrevHash) { |
||||
return nil, ParentError(block.PrevHash) |
||||
} |
||||
|
||||
sm.lastAttemptedBlock = block |
||||
|
||||
var ( |
||||
parent = sm.bc.GetBlock(block.PrevHash) |
||||
state = parent.State().Copy() |
||||
) |
||||
|
||||
defer state.Reset() |
||||
|
||||
sm.TransitionState(state, parent, block) |
||||
sm.AccumelateRewards(state, block, parent) |
||||
|
||||
return state.Manifest().Messages, nil |
||||
} |
@ -0,0 +1,355 @@ |
||||
package core |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/big" |
||||
"sync" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethutil" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/logger" |
||||
"github.com/ethereum/go-ethereum/state" |
||||
) |
||||
|
||||
var chainlogger = logger.NewLogger("CHAIN") |
||||
|
||||
func AddTestNetFunds(block *types.Block) { |
||||
for _, addr := range []string{ |
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99", |
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78", |
||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be", |
||||
"6c386a4b26f73c802f34673f7248bb118f97424a", |
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826", |
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", |
||||
"e6716f9544a56c530d868e4bfbacb172315bdead", |
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", |
||||
} { |
||||
codedAddr := ethutil.Hex2Bytes(addr) |
||||
account := block.State().GetAccount(codedAddr) |
||||
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||
block.State().UpdateStateObject(account) |
||||
} |
||||
} |
||||
|
||||
func CalcDifficulty(block, parent *types.Block) *big.Int { |
||||
diff := new(big.Int) |
||||
|
||||
adjust := new(big.Int).Rsh(parent.Difficulty, 10) |
||||
if block.Time >= parent.Time+5 { |
||||
diff.Sub(parent.Difficulty, adjust) |
||||
} else { |
||||
diff.Add(parent.Difficulty, adjust) |
||||
} |
||||
|
||||
return diff |
||||
} |
||||
|
||||
type ChainManager struct { |
||||
//eth EthManager
|
||||
processor types.BlockProcessor |
||||
eventMux *event.TypeMux |
||||
genesisBlock *types.Block |
||||
// Last known total difficulty
|
||||
mu sync.RWMutex |
||||
td *big.Int |
||||
lastBlockNumber uint64 |
||||
currentBlock *types.Block |
||||
lastBlockHash []byte |
||||
|
||||
transState *state.StateDB |
||||
} |
||||
|
||||
func (self *ChainManager) Td() *big.Int { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
return self.td |
||||
} |
||||
|
||||
func (self *ChainManager) LastBlockNumber() uint64 { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
return self.lastBlockNumber |
||||
} |
||||
|
||||
func (self *ChainManager) LastBlockHash() []byte { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
return self.lastBlockHash |
||||
} |
||||
|
||||
func (self *ChainManager) CurrentBlock() *types.Block { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
return self.currentBlock |
||||
} |
||||
|
||||
func NewChainManager(mux *event.TypeMux) *ChainManager { |
||||
bc := &ChainManager{} |
||||
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis)) |
||||
bc.eventMux = mux |
||||
|
||||
bc.setLastBlock() |
||||
|
||||
bc.transState = bc.State().Copy() |
||||
|
||||
return bc |
||||
} |
||||
|
||||
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { |
||||
self.processor = proc |
||||
} |
||||
|
||||
func (self *ChainManager) State() *state.StateDB { |
||||
return self.CurrentBlock().State() |
||||
} |
||||
|
||||
func (self *ChainManager) TransState() *state.StateDB { |
||||
return self.transState |
||||
} |
||||
|
||||
func (bc *ChainManager) setLastBlock() { |
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) |
||||
if len(data) != 0 { |
||||
// Prep genesis
|
||||
AddTestNetFunds(bc.genesisBlock) |
||||
|
||||
block := types.NewBlockFromBytes(data) |
||||
bc.currentBlock = block |
||||
bc.lastBlockHash = block.Hash() |
||||
bc.lastBlockNumber = block.Number.Uint64() |
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) |
||||
} else { |
||||
bc.Reset() |
||||
} |
||||
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash()) |
||||
} |
||||
|
||||
// Block creation & chain handling
|
||||
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { |
||||
bc.mu.RLock() |
||||
defer bc.mu.RUnlock() |
||||
|
||||
var root interface{} |
||||
hash := ZeroHash256 |
||||
|
||||
if bc.CurrentBlock != nil { |
||||
root = bc.currentBlock.Root() |
||||
hash = bc.lastBlockHash |
||||
} |
||||
|
||||
block := types.CreateBlock( |
||||
root, |
||||
hash, |
||||
coinbase, |
||||
ethutil.BigPow(2, 32), |
||||
nil, |
||||
"") |
||||
|
||||
parent := bc.currentBlock |
||||
if parent != nil { |
||||
block.Difficulty = CalcDifficulty(block, parent) |
||||
block.Number = new(big.Int).Add(bc.currentBlock.Number, ethutil.Big1) |
||||
block.GasLimit = block.CalcGasLimit(bc.currentBlock) |
||||
|
||||
} |
||||
|
||||
return block |
||||
} |
||||
|
||||
func (bc *ChainManager) Reset() { |
||||
bc.mu.Lock() |
||||
defer bc.mu.Unlock() |
||||
|
||||
AddTestNetFunds(bc.genesisBlock) |
||||
|
||||
bc.genesisBlock.Trie().Sync() |
||||
// Prepare the genesis block
|
||||
bc.write(bc.genesisBlock) |
||||
bc.insert(bc.genesisBlock) |
||||
bc.currentBlock = bc.genesisBlock |
||||
|
||||
bc.setTotalDifficulty(ethutil.Big("0")) |
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) |
||||
} |
||||
|
||||
func (self *ChainManager) Export() []byte { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Number) |
||||
|
||||
blocks := make([]*types.Block, int(self.currentBlock.Number.Int64())+1) |
||||
for block := self.currentBlock; block != nil; block = self.GetBlock(block.PrevHash) { |
||||
blocks[block.Number.Int64()] = block |
||||
} |
||||
|
||||
return ethutil.Encode(blocks) |
||||
} |
||||
|
||||
func (bc *ChainManager) insert(block *types.Block) { |
||||
encodedBlock := block.RlpEncode() |
||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) |
||||
bc.currentBlock = block |
||||
bc.lastBlockHash = block.Hash() |
||||
} |
||||
|
||||
func (bc *ChainManager) write(block *types.Block) { |
||||
bc.writeBlockInfo(block) |
||||
|
||||
encodedBlock := block.RlpEncode() |
||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock) |
||||
} |
||||
|
||||
// Accessors
|
||||
func (bc *ChainManager) Genesis() *types.Block { |
||||
return bc.genesisBlock |
||||
} |
||||
|
||||
// Block fetching methods
|
||||
func (bc *ChainManager) HasBlock(hash []byte) bool { |
||||
data, _ := ethutil.Config.Db.Get(hash) |
||||
return len(data) != 0 |
||||
} |
||||
|
||||
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) { |
||||
block := self.GetBlock(hash) |
||||
if block == nil { |
||||
return |
||||
} |
||||
|
||||
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||
for i := uint64(0); i < max; i++ { |
||||
chain = append(chain, block.Hash()) |
||||
|
||||
if block.Number.Cmp(ethutil.Big0) <= 0 { |
||||
break |
||||
} |
||||
|
||||
block = self.GetBlock(block.PrevHash) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func (self *ChainManager) GetBlock(hash []byte) *types.Block { |
||||
data, _ := ethutil.Config.Db.Get(hash) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
|
||||
return types.NewBlockFromBytes(data) |
||||
} |
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { |
||||
self.mu.RLock() |
||||
defer self.mu.RUnlock() |
||||
|
||||
block := self.currentBlock |
||||
for ; block != nil; block = self.GetBlock(block.PrevHash) { |
||||
if block.Number.Uint64() == num { |
||||
break |
||||
} |
||||
} |
||||
|
||||
if block != nil && block.Number.Uint64() == 0 && num != 0 { |
||||
return nil |
||||
} |
||||
|
||||
return block |
||||
} |
||||
|
||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) { |
||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes()) |
||||
bc.td = td |
||||
} |
||||
|
||||
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { |
||||
parent := self.GetBlock(block.PrevHash) |
||||
if parent == nil { |
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash) |
||||
} |
||||
|
||||
parentTd := parent.BlockInfo().TD |
||||
|
||||
uncleDiff := new(big.Int) |
||||
for _, uncle := range block.Uncles { |
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) |
||||
} |
||||
|
||||
td := new(big.Int) |
||||
td = td.Add(parentTd, uncleDiff) |
||||
td = td.Add(td, block.Difficulty) |
||||
|
||||
return td, nil |
||||
} |
||||
|
||||
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo { |
||||
bi := types.BlockInfo{} |
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) |
||||
bi.RlpDecode(data) |
||||
|
||||
return bi |
||||
} |
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *ChainManager) writeBlockInfo(block *types.Block) { |
||||
bc.lastBlockNumber++ |
||||
bi := types.BlockInfo{Number: bc.lastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.td} |
||||
|
||||
// For now we use the block hash with the words "info" appended as key
|
||||
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode()) |
||||
} |
||||
|
||||
func (bc *ChainManager) Stop() { |
||||
if bc.CurrentBlock != nil { |
||||
chainlogger.Infoln("Stopped") |
||||
} |
||||
} |
||||
|
||||
func (self *ChainManager) InsertChain(chain types.Blocks) error { |
||||
for _, block := range chain { |
||||
td, messages, err := self.processor.Process(block) |
||||
if err != nil { |
||||
if IsKnownBlockErr(err) { |
||||
continue |
||||
} |
||||
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4]) |
||||
chainlogger.Infoln(block) |
||||
chainlogger.Infoln(err) |
||||
return err |
||||
} |
||||
|
||||
self.mu.Lock() |
||||
{ |
||||
|
||||
self.write(block) |
||||
if td.Cmp(self.td) > 0 { |
||||
if block.Number.Cmp(new(big.Int).Add(self.currentBlock.Number, ethutil.Big1)) < 0 { |
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Number, block.Hash()[:4], self.currentBlock.Number, self.currentBlock.Hash()[:4]) |
||||
} |
||||
|
||||
self.setTotalDifficulty(td) |
||||
self.insert(block) |
||||
self.transState = self.currentBlock.State().Copy() |
||||
} |
||||
|
||||
} |
||||
self.mu.Unlock() |
||||
|
||||
self.eventMux.Post(NewBlockEvent{block}) |
||||
self.eventMux.Post(messages) |
||||
} |
||||
|
||||
return nil |
||||
} |