@ -1,4 +1,25 @@ |
|||||||
before_install: sudo apt-get install libgmp3-dev |
|
||||||
language: go |
language: go |
||||||
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. |
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. |
||||||
|
|
||||||
This library is free software; you can redistribute it and/or |
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 |
License as published by the Free Software Foundation; either |
||||||
version 2.1 of the License, or (at your option) any later version. |
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, |
This library is distributed in the hope that it will be useful, |
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
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 |
You should have received a copy of the GNU Lesser General Public |
||||||
along with this library; if not, write to the Free Software |
License along with this library; if not, write to the Free Software |
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||||
MA 02110-1301 USA |
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 bigInt = (function () { |
||||||
var base = 10000000, logBase = 7; |
var base = 10000000, logBase = 7; |
||||||
var sign = { |
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
|
// Main Ethereum library
|
||||||
window.eth = { |
window.eth = { |
||||||
prototype: Object(), |
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 = { |
var ethx = { |
||||||
prototype: Object, |
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.
|
// The magic return variable. The magic return variable will be set during the execution of the QML call.
|
||||||
(function(window) { |
(function(window) { |
||||||
var Promise = window.Promise; |
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) { |
String.prototype.pad = function(l, r) { |
||||||
if (r === undefined) { |
if (r === undefined) { |
||||||
r = l |
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
|
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
||||||
function postData(data, cb) { |
function postData(data, cb) { |
||||||
data._seed = Math.floor(Math.random() * 1000000) |
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 |
||||||
|
} |