Merge branch 'poc8' into docbranch

This commit is contained in:
obscuren 2015-01-02 10:30:27 +01:00
commit 0fb1bcd321
373 changed files with 18527 additions and 7543 deletions

View File

@ -1,6 +1,6 @@
language: go language: go
go: go:
- 1.3 - tip
before_install: before_install:
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y - sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
- sudo apt-get update -qq - sudo apt-get update -qq
@ -8,10 +8,10 @@ before_install:
install: install:
- go get code.google.com/p/go.tools/cmd/goimports - go get code.google.com/p/go.tools/cmd/goimports
- go get github.com/golang/lint/golint - go get github.com/golang/lint/golint
# - go get code.google.com/p/go.tools/cmd/vet # - go get golang.org/x/tools/cmd/vet
- go get code.google.com/p/go.tools/cmd/cover - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- ./install_deps.sh - 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
before_script: before_script:
- gofmt -l -w . - gofmt -l -w .
- goimports -l -w . - goimports -l -w .
@ -19,7 +19,7 @@ before_script:
# - go vet ./... # - go vet ./...
# - go test -race ./... # - go test -race ./...
script: script:
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN - ./gocoverage.sh
env: env:
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64=" - secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="

View File

@ -25,14 +25,14 @@ RUN apt-get install -y qtbase5-private-dev qtdeclarative5-private-dev libqt5open
## Fetch and install serpent-go ## Fetch and install serpent-go
RUN go get -v -d github.com/ethereum/serpent-go RUN go get -v -d github.com/ethereum/serpent-go
WORKDIR $GOPATH/src/github.com/ethereum/serpent-go WORKDIR $GOPATH/src/github.com/ethereum/serpent-go
RUN git checkout master # RUN git checkout master
RUN git submodule update --init RUN git submodule update --init
RUN go install -v RUN go install -v
# Fetch and install go-ethereum # Fetch and install go-ethereum
RUN go get -v -d github.com/ethereum/go-ethereum/... RUN go get -v -d github.com/ethereum/go-ethereum/...
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
RUN git checkout poc8 # RUN git checkout develop
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 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 go install -v ./cmd/ethereum

View File

@ -9,6 +9,7 @@ Ethereum
[![Build [![Build
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [![Build Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [![Build
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop
[![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.png?branch=tests)](https://coveralls.io/r/ethereum/go-ethereum?branch=tests) tests
Ethereum Go Client © 2014 Jeffrey Wilcke. Ethereum Go Client © 2014 Jeffrey Wilcke.

BIN
_data/invalid1 Executable file

Binary file not shown.

BIN
_data/valid1 Executable file

Binary file not shown.

BIN
_data/valid2 Executable file

Binary file not shown.

BIN
_data/valid3 Executable file

Binary file not shown.

BIN
_data/valid4 Executable file

Binary file not shown.

View File

@ -1,351 +0,0 @@
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())
}
}
}
}
}

View File

@ -21,9 +21,9 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/ethereum/repl" "github.com/ethereum/go-ethereum/cmd/ethereum/repl"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
) )

View File

@ -38,7 +38,8 @@ var (
StartRpc bool StartRpc bool
StartWebSockets bool StartWebSockets bool
RpcPort int RpcPort int
UseUPnP bool NatType string
PMPGateway string
OutboundPort string OutboundPort string
ShowGenesis bool ShowGenesis bool
AddPeer string AddPeer string
@ -57,6 +58,7 @@ var (
DumpHash string DumpHash string
DumpNumber int DumpNumber int
VmType int VmType int
ImportChain string
) )
// flags specific to cli client // flags specific to cli client
@ -84,8 +86,9 @@ func Init() {
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use") flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)") flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support") flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server") flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
@ -102,6 +105,7 @@ func Init() {
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block") flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
flag.StringVar(&ImportChain, "chain", "", "Imports fiven chain")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]") flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex") flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
"time"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -30,7 +31,7 @@ import (
const ( const (
ClientIdentifier = "Ethereum(G)" ClientIdentifier = "Ethereum(G)"
Version = "0.7.9" Version = "0.7.11"
) )
var clilogger = logger.NewLogger("CLI") var clilogger = logger.NewLogger("CLI")
@ -38,6 +39,10 @@ var clilogger = logger.NewLogger("CLI")
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
defer func() {
logger.Flush()
}()
utils.HandleInterrupt() utils.HandleInterrupt()
// precedence: code-internal flag default < config file < environment variables < command line // precedence: code-internal flag default < config file < environment variables < command line
@ -69,15 +74,15 @@ func main() {
// create, import, export keys // create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive) utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier) clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer) ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
if Dump { if Dump {
var block *types.Block var block *types.Block
if len(DumpHash) == 0 && DumpNumber == -1 { if len(DumpHash) == 0 && DumpNumber == -1 {
block = ethereum.ChainManager().CurrentBlock block = ethereum.ChainManager().CurrentBlock()
} else if len(DumpHash) > 0 { } else if len(DumpHash) > 0 {
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash)) block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
} else { } else {
@ -93,9 +98,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
// block.GetRoot() does not exist
//fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
// Leave the Println. This needs clean output for piping // Leave the Println. This needs clean output for piping
fmt.Printf("%s\n", block.State().Dump()) fmt.Printf("%s\n", block.State().Dump())
@ -112,6 +114,16 @@ func main() {
utils.StartMining(ethereum) utils.StartMining(ethereum)
} }
if len(ImportChain) > 0 {
start := time.Now()
err := utils.ImportChain(ethereum, ImportChain)
if err != nil {
clilogger.Infoln(err)
}
clilogger.Infoln("import done in", time.Since(start))
return
}
// better reworked as cases // better reworked as cases
if StartJsConsole { if StartJsConsole {
InitJsConsole(ethereum) InitJsConsole(ethereum)
@ -131,5 +143,4 @@ func main() {
// this blocks the thread // this blocks the thread
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
logger.Flush()
} }

View File

@ -24,7 +24,7 @@ import (
"os" "os"
"path" "path"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -86,12 +86,6 @@ func (self *JSRepl) Stop() {
} }
func (self *JSRepl) parseInput(code string) { func (self *JSRepl) parseInput(code string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("[native] error", r)
}
}()
value, err := self.re.Run(code) value, err := self.re.Run(code)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

5
cmd/ethtest/.bowerrc Normal file
View File

@ -0,0 +1,5 @@
{
"directory": "example/js/",
"cwd": "./",
"analytics": false
}

12
cmd/ethtest/.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@ -4,9 +4,15 @@
# or operating system, you probably want to add a global ignore instead: # or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global # git config --global core.excludesfile ~/.gitignore_global
*.swp
/tmp /tmp
*/**/*un~ */**/*un~
*un~ *un~
.DS_Store .DS_Store
*/**/.DS_Store */**/.DS_Store
ethereum/ethereum
ethereal/ethereal
example/js
node_modules
bower_components
npm-debug.log

50
cmd/ethtest/.jshintrc Normal file
View File

@ -0,0 +1,50 @@
{
"predef": [
"console",
"require",
"equal",
"test",
"testBoth",
"testWithDefault",
"raises",
"deepEqual",
"start",
"stop",
"ok",
"strictEqual",
"module",
"expect",
"reject",
"impl"
],
"esnext": true,
"proto": true,
"node" : true,
"browser" : true,
"browserify" : true,
"boss" : true,
"curly": false,
"debug": true,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"shadow": true,
"eqnull": true
}

9
cmd/ethtest/.npmignore Normal file
View File

@ -0,0 +1,9 @@
example/js
node_modules
test
.gitignore
.editorconfig
.travis.yml
.npmignore
component.json
testling.html

11
cmd/ethtest/.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: node_js
node_js:
- "0.11"
- "0.10"
before_script:
- npm install
- npm install jshint
script:
- "jshint *.js lib"
after_script:
- npm run-script gulp

14
cmd/ethtest/LICENSE Normal file
View File

@ -0,0 +1,14 @@
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.

79
cmd/ethtest/README.md Normal file
View File

@ -0,0 +1,79 @@
# Ethereum JavaScript API
This is the Ethereum compatible JavaScript API using `Promise`s
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url]
<!-- [![browser support](https://ci.testling.com/ethereum/ethereum.js.png)](https://ci.testling.com/ethereum/ethereum.js) -->
## Installation
### Node.js
npm install ethereum.js
### For browser
Bower
bower install ethereum.js
Component
component install ethereum/ethereum.js
* Include `ethereum.min.js` in your html file.
* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6.
## Usage
Require the library:
var web3 = require('web3');
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider)
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth'));
There you go, now you can use it:
```
web3.eth.coinbase.then(function(result){
console.log(result);
return web3.eth.balanceAt(result);
}).then(function(balance){
console.log(web3.toDecimal(balance));
}).catch(function(err){
console.log(err);
});
```
For another example see `example/index.html`.
## Building
* `gulp build`
### Testing
**Please note this repo is in it's early stage.**
If you'd like to run a WebSocket ethereum node check out
[go-ethereum](https://github.com/ethereum/go-ethereum).
To install ethereum and spawn a node:
```
go get github.com/ethereum/go-ethereum/ethereum
ethereum -ws -loglevel=4
```
[npm-image]: https://badge.fury.io/js/ethereum.js.png
[npm-url]: https://npmjs.org/package/ethereum.js
[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg
[travis-url]: https://travis-ci.org/ethereum/ethereum.js
[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg
[dep-url]: https://david-dm.org/ethereum/ethereum.js
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies

51
cmd/ethtest/bower.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.3",
"description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": {
"es6-promise": "#master"
},
"repository": {
"type": "git",
"url": "https://github.com/ethereum/ethereum.js.git"
},
"homepage": "https://github.com/ethereum/ethereum.js",
"bugs": {
"url": "https://github.com/ethereum/ethereum.js/issues"
},
"keywords": [
"ethereum",
"javascript",
"API"
],
"authors": [
{
"name": "Marek Kotewicz",
"email": "marek@ethdev.com",
"homepage": "https://github.com/debris"
},
{
"name": "Marian Oancea",
"email": "marian@ethdev.com",
"homepage": "https://github.com/cubedro"
}
],
"license": "LGPL-3.0",
"ignore": [
"example",
"lib",
"node_modules",
"package.json",
".bowerrc",
".editorconfig",
".gitignore",
".jshintrc",
".npmignore",
".travis.yml",
"gulpfile.js",
"index.js",
"**/*.txt"
]
}

20
cmd/ethtest/dist/ethereum.js vendored Normal file

File diff suppressed because one or more lines are too long

29
cmd/ethtest/dist/ethereum.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1
cmd/ethtest/dist/ethereum.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,75 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.AutoProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" function multiply(uint a) returns(uint d) {\n" +
" return a * 7;\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
var contract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source;
// create contract
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
contract = web3.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
});
}
function callExampleContract() {
// this should be generated by ethereum
var param = document.getElementById('value').value;
// call the contract
contract.multiply(param).call().then(function(res) {
document.getElementById('result').innerText = res[0];
});
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="source"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<input type="number" id="value" onkeyup='callExampleContract()'></input>
</div>
<div id="result"></div>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.AutoProvider());
function watchBalance() {
var coinbase = web3.eth.coinbase;
var originalBalance = 0;
web3.eth.balanceAt(coinbase).then(function (balance) {
originalBalance = web3.toDecimal(balance);
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...';
});
web3.eth.watch({altered: coinbase}).changed(function() {
web3.eth.balanceAt(coinbase).then(function (balance) {
var currentBalance = web3.toDecimal(balance);
document.getElementById("current").innerText = 'current: ' + currentBalance;
document.getElementById("diff").innerText = 'diff: ' + (currentBalance - originalBalance);
});
});
}
</script>
</head>
<body>
<h1>coinbase balance</h1>
<button type="button" onClick="watchBalance();">watch balance</button>
<div></div>
<div id="original"></div>
<div id="current"></div>
<div id="diff"></div>
</body>
</html>

View File

@ -0,0 +1,16 @@
#!/usr/bin/env node
require('es6-promise').polyfill();
var web3 = require("../index.js");
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080'));
web3.eth.coinbase.then(function(result){
console.log(result);
return web3.eth.balanceAt(result);
}).then(function(balance){
console.log(web3.toDecimal(balance));
}).catch(function(err){
console.log(err);
});

123
cmd/ethtest/gulpfile.js Normal file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env node
'use strict';
var path = require('path');
var del = require('del');
var gulp = require('gulp');
var browserify = require('browserify');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var envify = require('envify/custom');
var unreach = require('unreachable-branch-transform');
var source = require('vinyl-source-stream');
var exorcist = require('exorcist');
var bower = require('bower');
var DEST = './dist/';
var build = function(src, dst) {
return browserify({
debug: true,
insert_global_vars: false,
detectGlobals: false,
bundleExternal: false
})
.require('./' + src + '.js', {expose: 'web3'})
.add('./' + src + '.js')
.transform('envify', {
NODE_ENV: 'build'
})
.transform('unreachable-branch-transform')
.transform('uglifyify', {
mangle: false,
compress: {
dead_code: false,
conditionals: true,
unused: false,
hoist_funs: true,
hoist_vars: true,
negate_iife: false
},
beautify: true,
warnings: true
})
.bundle()
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
.pipe(source(dst + '.js'))
.pipe(gulp.dest( DEST ));
};
var buildDev = function(src, dst) {
return browserify({
debug: true,
insert_global_vars: false,
detectGlobals: false,
bundleExternal: false
})
.require('./' + src + '.js', {expose: 'web3'})
.add('./' + src + '.js')
.transform('envify', {
NODE_ENV: 'build'
})
.transform('unreachable-branch-transform')
.bundle()
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
.pipe(source(dst + '.js'))
.pipe(gulp.dest( DEST ));
};
var uglifyFile = function(file) {
return gulp.src( DEST + file + '.js')
.pipe(uglify())
.pipe(rename(file + '.min.js'))
.pipe(gulp.dest( DEST ));
};
gulp.task('bower', function(cb){
bower.commands.install().on('end', function (installed){
console.log(installed);
cb();
});
});
gulp.task('lint', function(){
return gulp.src(['./*.js', './lib/*.js'])
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
gulp.task('clean', ['lint'], function(cb) {
del([ DEST ], cb);
});
gulp.task('build', ['clean'], function () {
return build('index', 'ethereum');
});
gulp.task('buildQt', ['clean'], function () {
return build('index_qt', 'ethereum');
});
gulp.task('buildDev', ['clean'], function () {
return buildDev('index', 'ethereum');
});
gulp.task('uglify', ['build'], function(){
return uglifyFile('ethereum');
});
gulp.task('uglifyQt', ['buildQt'], function () {
return uglifyFile('ethereum');
});
gulp.task('watch', function() {
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
});
gulp.task('default', ['bower', 'lint', 'build', 'uglify']);
gulp.task('qt', ['bower', 'lint', 'buildQt', 'uglifyQt']);
gulp.task('dev', ['bower', 'lint', 'buildDev']);

8
cmd/ethtest/index.js Normal file
View File

@ -0,0 +1,8 @@
var web3 = require('./lib/main');
web3.providers.WebSocketProvider = require('./lib/websocket');
web3.providers.HttpRpcProvider = require('./lib/httprpc');
web3.providers.QtProvider = require('./lib/qt');
web3.providers.AutoProvider = require('./lib/autoprovider');
web3.contract = require('./lib/contract');
module.exports = web3;

5
cmd/ethtest/index_qt.js Normal file
View File

@ -0,0 +1,5 @@
var web3 = require('./lib/main');
web3.providers.QtProvider = require('./lib/qt');
web3.contract = require('./lib/contract');
module.exports = web3;

218
cmd/ethtest/lib/abi.js Normal file
View File

@ -0,0 +1,218 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file abi.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) {
return method.name === methodName;
});
};
var padLeft = function (string, chars) {
return Array(chars - string.length + 1).join("0") + string;
};
var setupInputTypes = function () {
var prefixedType = function (prefix) {
return function (type, value) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return false;
}
var padding = parseInt(type.slice(expected.length)) / 8;
if (typeof value === "number")
value = value.toString(16);
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else
value = (+value).toString(16);
return padLeft(value, padding * 2);
};
};
var namedType = function (name, padding, formatter) {
return function (type, value) {
if (type !== name) {
return false;
}
return padLeft(formatter ? formatter(value) : value, padding * 2);
};
};
var formatBool = function (value) {
return value ? '0x1' : '0x0';
};
return [
prefixedType('uint'),
prefixedType('int'),
prefixedType('hash'),
namedType('address', 20),
namedType('bool', 1, formatBool),
];
};
var inputTypes = setupInputTypes();
var toAbiInput = function (json, methodName, params) {
var bytes = "";
var index = findMethodIndex(json, methodName);
if (index === -1) {
return;
}
bytes = "0x" + padLeft(index.toString(16), 2);
var method = json[index];
for (var i = 0; i < method.inputs.length; i++) {
var found = false;
for (var j = 0; j < inputTypes.length && !found; j++) {
found = inputTypes[j](method.inputs[i].type, params[i]);
}
if (!found) {
console.error('unsupported json type: ' + method.inputs[i].type);
}
bytes += found;
}
return bytes;
};
var setupOutputTypes = function () {
var prefixedType = function (prefix) {
return function (type) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return -1;
}
var padding = parseInt(type.slice(expected.length)) / 8;
return padding * 2;
};
};
var namedType = function (name, padding) {
return function (type) {
return name === type ? padding * 2 : -1;
};
};
var formatInt = function (value) {
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
};
var formatHash = function (value) {
return "0x" + value;
};
var formatBool = function (value) {
return value === '1' ? true : false;
};
return [
{ padding: prefixedType('uint'), format: formatInt },
{ padding: prefixedType('int'), format: formatInt },
{ padding: prefixedType('hash'), format: formatHash },
{ padding: namedType('address', 20) },
{ padding: namedType('bool', 1), format: formatBool }
];
};
var outputTypes = setupOutputTypes();
var fromAbiOutput = function (json, methodName, output) {
var index = findMethodIndex(json, methodName);
if (index === -1) {
return;
}
output = output.slice(2);
var result = [];
var method = json[index];
for (var i = 0; i < method.outputs.length; i++) {
var padding = -1;
for (var j = 0; j < outputTypes.length && padding === -1; j++) {
padding = outputTypes[j].padding(method.outputs[i].type);
}
if (padding === -1) {
// not found output parsing
continue;
}
var res = output.slice(0, padding);
var formatter = outputTypes[j - 1].format;
result.push(formatter ? formatter(res) : ("0x" + res));
output = output.slice(padding);
}
return result;
};
var inputParser = function (json) {
var parser = {};
json.forEach(function (method) {
parser[method.name] = function () {
var params = Array.prototype.slice.call(arguments);
return toAbiInput(json, method.name, params);
};
});
return parser;
};
var outputParser = function (json) {
var parser = {};
json.forEach(function (method) {
parser[method.name] = function (output) {
return fromAbiOutput(json, method.name, output);
};
});
return parser;
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser
};

View File

@ -0,0 +1,103 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file autoprovider.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* @date 2014
*/
/*
* @brief if qt object is available, uses QtProvider,
* if not tries to connect over websockets
* if it fails, it uses HttpRpcProvider
*/
// TODO: work out which of the following two lines it is supposed to be...
//if (process.env.NODE_ENV !== 'build') {
if ("build" !== 'build') {/*
var WebSocket = require('ws'); // jshint ignore:line
var web3 = require('./main.js'); // jshint ignore:line
*/}
var AutoProvider = function (userOptions) {
if (web3.haveProvider()) {
return;
}
// before we determine what provider we are, we have to cache request
this.sendQueue = [];
this.onmessageQueue = [];
if (navigator.qt) {
this.provider = new web3.providers.QtProvider();
return;
}
userOptions = userOptions || {};
var options = {
httprpc: userOptions.httprpc || 'http://localhost:8080',
websockets: userOptions.websockets || 'ws://localhost:40404/eth'
};
var self = this;
var closeWithSuccess = function (success) {
ws.close();
if (success) {
self.provider = new web3.providers.WebSocketProvider(options.websockets);
} else {
self.provider = new web3.providers.HttpRpcProvider(options.httprpc);
self.poll = self.provider.poll.bind(self.provider);
}
self.sendQueue.forEach(function (payload) {
self.provider(payload);
});
self.onmessageQueue.forEach(function (handler) {
self.provider.onmessage = handler;
});
};
var ws = new WebSocket(options.websockets);
ws.onopen = function() {
closeWithSuccess(true);
};
ws.onerror = function() {
closeWithSuccess(false);
};
};
AutoProvider.prototype.send = function (payload) {
if (this.provider) {
this.provider.send(payload);
return;
}
this.sendQueue.push(payload);
};
Object.defineProperty(AutoProvider.prototype, 'onmessage', {
set: function (handler) {
if (this.provider) {
this.provider.onmessage = handler;
return;
}
this.onmessageQueue.push(handler);
}
});
module.exports = AutoProvider;

View File

@ -0,0 +1,65 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file contract.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
// TODO: work out which of the following two lines it is supposed to be...
//if (process.env.NODE_ENV !== 'build') {
if ("build" !== 'build') {/*
var web3 = require('./web3'); // jshint ignore:line
*/}
var abi = require('./abi');
var contract = function (address, desc) {
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
var contract = {};
desc.forEach(function (method) {
contract[method.name] = function () {
var params = Array.prototype.slice.call(arguments);
var parsed = inputParser[method.name].apply(null, params);
var onSuccess = function (result) {
return outputParser[method.name](result);
};
return {
call: function (extra) {
extra = extra || {};
extra.to = address;
extra.data = parsed;
return web3.eth.call(extra).then(onSuccess);
},
transact: function (extra) {
extra = extra || {};
extra.to = address;
extra.data = parsed;
return web3.eth.transact(extra).then(onSuccess);
}
};
};
});
return contract;
};
module.exports = contract;

View File

@ -0,0 +1,95 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file httprpc.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* @date 2014
*/
// TODO: work out which of the following two lines it is supposed to be...
//if (process.env.NODE_ENV !== 'build') {
if ("build" !== "build") {/*
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
*/}
var HttpRpcProvider = function (host) {
this.handlers = [];
this.host = host;
};
function formatJsonRpcObject(object) {
return {
jsonrpc: '2.0',
method: object.call,
params: object.args,
id: object._id
};
}
function formatJsonRpcMessage(message) {
var object = JSON.parse(message);
return {
_id: object.id,
data: object.result,
error: object.error
};
}
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
var data = formatJsonRpcObject(payload);
var request = new XMLHttpRequest();
request.open("POST", this.host, true);
request.send(JSON.stringify(data));
request.onreadystatechange = function () {
if (request.readyState === 4 && cb) {
cb(request);
}
};
};
HttpRpcProvider.prototype.send = function (payload) {
var self = this;
this.sendRequest(payload, function (request) {
self.handlers.forEach(function (handler) {
handler.call(self, formatJsonRpcMessage(request.responseText));
});
});
};
HttpRpcProvider.prototype.poll = function (payload, id) {
var self = this;
this.sendRequest(payload, function (request) {
var parsed = JSON.parse(request.responseText);
if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {
return;
}
self.handlers.forEach(function (handler) {
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
});
});
};
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
set: function (handler) {
this.handlers.push(handler);
}
});
module.exports = HttpRpcProvider;

494
cmd/ethtest/lib/main.js Normal file
View File

@ -0,0 +1,494 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
function flattenPromise (obj) {
if (obj instanceof Promise) {
return Promise.resolve(obj);
}
if (obj instanceof Array) {
return new Promise(function (resolve) {
var promises = obj.map(function (o) {
return flattenPromise(o);
});
return Promise.all(promises).then(function (res) {
for (var i = 0; i < obj.length; i++) {
obj[i] = res[i];
}
resolve(obj);
});
});
}
if (obj instanceof Object) {
return new Promise(function (resolve) {
var keys = Object.keys(obj);
var promises = keys.map(function (key) {
return flattenPromise(obj[key]);
});
return Promise.all(promises).then(function (res) {
for (var i = 0; i < keys.length; i++) {
obj[keys[i]] = res[i];
}
resolve(obj);
});
});
}
return Promise.resolve(obj);
}
var web3Methods = function () {
return [
{ name: 'sha3', call: 'web3_sha3' }
];
};
var ethMethods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
};
var transactionCall = function (args) {
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
};
var uncleCall = function (args) {
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
};
var methods = [
{ name: 'balanceAt', call: 'eth_balanceAt' },
{ name: 'stateAt', call: 'eth_stateAt' },
{ name: 'storageAt', call: 'eth_storageAt' },
{ name: 'countAt', call: 'eth_countAt'},
{ name: 'codeAt', call: 'eth_codeAt' },
{ name: 'transact', call: 'eth_transact' },
{ name: 'call', call: 'eth_call' },
{ name: 'block', call: blockCall },
{ name: 'transaction', call: transactionCall },
{ name: 'uncle', call: uncleCall },
{ name: 'compilers', call: 'eth_compilers' },
{ name: 'lll', call: 'eth_lll' },
{ name: 'solidity', call: 'eth_solidity' },
{ name: 'serpent', call: 'eth_serpent' },
{ name: 'logs', call: 'eth_logs' }
];
return methods;
};
var ethProperties = function () {
return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
{ name: 'number', getter: 'eth_number'}
];
};
var dbMethods = function () {
return [
{ name: 'put', call: 'db_put' },
{ name: 'get', call: 'db_get' },
{ name: 'putString', call: 'db_putString' },
{ name: 'getString', call: 'db_getString' }
];
};
var shhMethods = function () {
return [
{ name: 'post', call: 'shh_post' },
{ name: 'newIdentity', call: 'shh_newIdentity' },
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
{ name: 'newGroup', call: 'shh_newGroup' },
{ name: 'addToGroup', call: 'shh_addToGroup' }
];
};
var ethWatchMethods = function () {
var newFilter = function (args) {
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
};
return [
{ name: 'newFilter', call: newFilter },
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' },
{ name: 'getMessages', call: 'eth_filterLogs' }
];
};
var shhWatchMethods = function () {
return [
{ name: 'newFilter', call: 'shh_newFilter' },
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
{ name: 'getMessage', call: 'shh_getMessages' }
];
};
var setupMethods = function (obj, methods) {
methods.forEach(function (method) {
obj[method.name] = function () {
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
var call = typeof method.call === "function" ? method.call(args) : method.call;
return {call: call, args: args};
}).then(function (request) {
return new Promise(function (resolve, reject) {
web3.provider.send(request, function (err, result) {
if (!err) {
resolve(result);
return;
}
reject(err);
});
});
}).catch(function(err) {
console.error(err);
});
};
});
};
var setupProperties = function (obj, properties) {
properties.forEach(function (property) {
var proto = {};
proto.get = function () {
return new Promise(function(resolve, reject) {
web3.provider.send({call: property.getter}, function(err, result) {
if (!err) {
resolve(result);
return;
}
reject(err);
});
});
};
if (property.setter) {
proto.set = function (val) {
return flattenPromise([val]).then(function (args) {
return new Promise(function (resolve) {
web3.provider.send({call: property.setter, args: args}, function (err, result) {
if (!err) {
resolve(result);
return;
}
reject(err);
});
});
}).catch(function (err) {
console.error(err);
});
};
}
Object.defineProperty(obj, property.name, proto);
});
};
// TODO: import from a dependency, don't duplicate.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
var web3 = {
_callbacks: {},
_events: {},
providers: {},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x')
i = 2;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i);
if(code === 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
},
fromAscii: function(str, pad) {
pad = pad === undefined ? 32 : pad;
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return "0x" + hex;
},
toDecimal: function (val) {
return hexToDec(val.substring(2));
},
fromDecimal: function (val) {
return "0x" + decToHex(val);
},
toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') == 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, function($0, $1, $2) { return $1 + ',' + $2; });
if (o == s)
break;
}
return s + ' ' + units[unit];
},
eth: {
prototype: Object(), // jshint ignore:line
watch: function (params) {
return new Filter(params, ethWatch);
}
},
db: {
prototype: Object() // jshint ignore:line
},
shh: {
prototype: Object(), // jshint ignore:line
watch: function (params) {
return new Filter(params, shhWatch);
}
},
on: function(event, id, cb) {
if(web3._events[event] === undefined) {
web3._events[event] = {};
}
web3._events[event][id] = cb;
return this;
},
off: function(event, id) {
if(web3._events[event] !== undefined) {
delete web3._events[event][id];
}
return this;
},
trigger: function(event, id, data) {
var callbacks = web3._events[event];
if (!callbacks || !callbacks[id]) {
return;
}
var cb = callbacks[id];
cb(data);
}
};
setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties());
setupMethods(web3.db, dbMethods());
setupMethods(web3.shh, shhMethods());
var ethWatch = {
changed: 'eth_changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shh_changed'
};
setupMethods(shhWatch, shhWatchMethods());
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
ProviderManager.prototype.sendQueued = function() {
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
web3.provider = new ProviderManager();
web3.setProvider = function(provider) {
provider.onmessage = messageHandler;
web3.provider.set(provider);
web3.provider.sendQueued();
};
web3.haveProvider = function() {
return !!web3.provider.provider;
};
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
Filter.prototype.logs = function () {
return this.messages();
};
function messageHandler(data) {
if(data._event !== undefined) {
web3.trigger(data._event, data._id, data.data);
return;
}
if(data._id) {
var cb = web3._callbacks[data._id];
if (cb) {
cb.call(this, data.error, data.data);
delete web3._callbacks[data._id];
}
}
}
module.exports = web3;

45
cmd/ethtest/lib/qt.js Normal file
View File

@ -0,0 +1,45 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file qt.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
var QtProvider = function() {
this.handlers = [];
var self = this;
navigator.qt.onmessage = function (message) {
self.handlers.forEach(function (handler) {
handler.call(self, JSON.parse(message.data));
});
};
};
QtProvider.prototype.send = function(payload) {
navigator.qt.postMessage(JSON.stringify(payload));
};
Object.defineProperty(QtProvider.prototype, "onmessage", {
set: function(handler) {
this.handlers.push(handler);
}
});
module.exports = QtProvider;

View File

@ -0,0 +1,78 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file websocket.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* @date 2014
*/
// TODO: work out which of the following two lines it is supposed to be...
//if (process.env.NODE_ENV !== 'build') {
if ("build" !== "build") {/*
var WebSocket = require('ws'); // jshint ignore:line
*/}
var WebSocketProvider = function(host) {
// onmessage handlers
this.handlers = [];
// queue will be filled with messages if send is invoked before the ws is ready
this.queued = [];
this.ready = false;
this.ws = new WebSocket(host);
var self = this;
this.ws.onmessage = function(event) {
for(var i = 0; i < self.handlers.length; i++) {
self.handlers[i].call(self, JSON.parse(event.data), event);
}
};
this.ws.onopen = function() {
self.ready = true;
for(var i = 0; i < self.queued.length; i++) {
// Resend
self.send(self.queued[i]);
}
};
};
WebSocketProvider.prototype.send = function(payload) {
if(this.ready) {
var data = JSON.stringify(payload);
this.ws.send(data);
} else {
this.queued.push(payload);
}
};
WebSocketProvider.prototype.onMessage = function(handler) {
this.handlers.push(handler);
};
WebSocketProvider.prototype.unload = function() {
this.ws.close();
};
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
set: function(provider) { this.onMessage(provider); }
});
module.exports = WebSocketProvider;

View File

@ -95,11 +95,16 @@ func RunVmTest(js string) (failed int) {
failed = 1 failed = 1
} }
if len(test.Gas) == 0 && err == nil {
log.Printf("0 gas indicates error but no error given by VM")
failed = 1
} else {
gexp := ethutil.Big(test.Gas) gexp := ethutil.Big(test.Gas)
if gexp.Cmp(gas) != 0 { if gexp.Cmp(gas) != 0 {
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas) log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
failed = 1 failed = 1
} }
}
for addr, account := range test.Post { for addr, account := range test.Post {
obj := state.GetStateObject(helper.FromHex(addr)) obj := state.GetStateObject(helper.FromHex(addr))

67
cmd/ethtest/package.json Normal file
View File

@ -0,0 +1,67 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.5",
"description": "Ethereum Compatible JavaScript API",
"main": "./index.js",
"directories": {
"lib": "./lib"
},
"dependencies": {
"es6-promise": "*",
"ws": "*",
"xmlhttprequest": "*"
},
"devDependencies": {
"bower": ">=1.3.0",
"browserify": ">=6.0",
"del": ">=0.1.1",
"envify": "^3.0.0",
"exorcist": "^0.1.6",
"gulp": ">=3.4.0",
"gulp-jshint": ">=1.5.0",
"gulp-rename": ">=1.2.0",
"gulp-uglify": ">=1.0.0",
"jshint": ">=2.5.0",
"uglifyify": "^2.6.0",
"unreachable-branch-transform": "^0.1.0",
"vinyl-source-stream": "^1.0.0"
},
"scripts": {
"build": "gulp",
"watch": "gulp watch",
"lint": "gulp lint"
},
"repository": {
"type": "git",
"url": "https://github.com/ethereum/ethereum.js.git"
},
"homepage": "https://github.com/ethereum/ethereum.js",
"bugs": {
"url": "https://github.com/ethereum/ethereum.js/issues"
},
"keywords": [
"ethereum",
"javascript",
"API"
],
"author": "ethdev.com",
"authors": [
{
"name": "Jeffery Wilcke",
"email": "jeff@ethdev.com",
"url": "https://github.com/obscuren"
},
{
"name": "Marek Kotewicz",
"email": "marek@ethdev.com",
"url": "https://github.com/debris"
},
{
"name": "Marian Oancea",
"email": "marian@ethdev.com",
"url": "https://github.com/cubedro"
}
],
"license": "LGPL-3.0"
}

View File

@ -37,8 +37,8 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/ptrie"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/vm" "github.com/ethereum/go-ethereum/vm"
) )
@ -65,7 +65,7 @@ func main() {
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "") ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := state.New(trie.New(db, "")) statedb := state.New(ptrie.New(nil, db))
sender := statedb.NewStateObject([]byte("sender")) sender := statedb.NewStateObject([]byte("sender"))
receiver := statedb.NewStateObject([]byte("receiver")) receiver := statedb.NewStateObject([]byte("receiver"))
//receiver.SetCode([]byte(*code)) //receiver.SetCode([]byte(*code))
@ -141,9 +141,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
} }
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution { func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
evm := vm.New(self, vm.DebugVmTy) return core.NewExecution(self, addr, data, gas, price, value)
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) { func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {

View File

@ -66,7 +66,11 @@ Rectangle {
onMessages: { onMessages: {
// Bit of a cheat to get proper JSON // Bit of a cheat to get proper JSON
var m = JSON.parse(JSON.parse(JSON.stringify(messages))) var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
webview.postEvent("messages", [m, id]); webview.postEvent("messages", id, m);
}
function onShhMessage(message, id) {
webview.postEvent("shhChanged", id, message)
} }
Item { Item {
@ -327,6 +331,33 @@ Rectangle {
require(1); require(1);
eth.uninstallFilter(data.args[0]) eth.uninstallFilter(data.args[0])
break; break;
case "shhNewFilter":
require(1);
var id = shh.watch(data.args[0], window);
postData(data._id, id);
break;
case "newIdentity":
postData(data._id, shh.newIdentity())
break
case "post":
require(1);
var params = data.args[0];
var fields = ["payload", "to", "from"];
for(var i = 0; i < fields.length; i++) {
params[fields[i]] = params[fields[i]] || "";
}
if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
params.topics = params.topics || [];
params.priority = params.priority || 1000;
params.ttl = params.ttl || 100;
console.log(JSON.stringify(params))
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
break;
} }
} catch(e) { } catch(e) {
console.log(data.call + ": " + e) console.log(data.call + ": " + e)
@ -348,8 +379,8 @@ Rectangle {
function postData(seed, data) { function postData(seed, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed})) webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
} }
function postEvent(event, data) { function postEvent(event, id, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
} }
function onWatchedCb(data, id) { function onWatchedCb(data, id) {

View File

@ -45,11 +45,12 @@ ApplicationWindow {
// Takes care of loading all default plugins // Takes care of loading all default plugins
Component.onCompleted: { Component.onCompleted: {
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); var browser = addPlugin("./browser.qml", {noAdd: true, close: false, section: "ethereum", active: true});
root.browser = browser; root.browser = browser;
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true}); addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
@ -786,8 +787,8 @@ ApplicationWindow {
title: "About" title: "About"
minimumWidth: 350 minimumWidth: 350
maximumWidth: 350 maximumWidth: 350
maximumHeight: 200 maximumHeight: 280
minimumHeight: 200 minimumHeight: 280
Image { Image {
id: aboutIcon id: aboutIcon
@ -797,7 +798,7 @@ ApplicationWindow {
smooth: true smooth: true
source: "../facet.png" source: "../facet.png"
x: 10 x: 10
y: 10 y: 30
} }
Text { Text {
@ -806,7 +807,7 @@ ApplicationWindow {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 30 anchors.topMargin: 30
font.pointSize: 12 font.pointSize: 12
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes" text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>"
} }
} }

View File

@ -0,0 +1,76 @@
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: "Whisper Traffic"
property var iconSource: "../facet.png"
property var menuItem
objectName: "whisperView"
anchors.fill: parent
property var identity: ""
Component.onCompleted: {
identity = shh.newIdentity()
console.log("New identity:", identity)
var t = shh.watch({}, root)
}
function onShhMessage(message, i) {
whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
}
RowLayout {
id: input
anchors {
left: parent.left
leftMargin: 20
top: parent.top
topMargin: 20
}
TextField {
id: to
placeholderText: "To"
}
TextField {
id: data
placeholderText: "Data"
}
TextField {
id: topics
placeholderText: "topic1, topic2, topic3, ..."
}
Button {
text: "Send"
onClicked: {
shh.post([eth.toHex(data.text)], "", identity, topics.text.split(","), 500, 50)
}
}
}
TableView {
id: txTableView
anchors {
top: input.bottom
topMargin: 10
bottom: parent.bottom
left: parent.left
right: parent.right
}
TableViewColumn{ id: fromRole; role: "from" ; title: "From"; width: 300 }
TableViewColumn{ role: "payload" ; title: "Payload" ; width: parent.width - fromRole.width - 2 }
model: ListModel {
id: whisperModel
}
}
}

View File

@ -149,7 +149,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
self.SetAsm(script) self.SetAsm(script)
block := self.lib.eth.ChainManager().CurrentBlock block := self.lib.eth.ChainManager().CurrentBlock()
env := utils.NewEnv(statedb, block, account.Address(), value) env := utils.NewEnv(statedb, block, account.Address(), value)

View File

@ -36,10 +36,12 @@ var (
Identifier string Identifier string
KeyRing string KeyRing string
KeyStore string KeyStore string
PMPGateway string
StartRpc bool StartRpc bool
StartWebSockets bool StartWebSockets bool
RpcPort int RpcPort int
UseUPnP bool UseUPnP bool
NatType string
OutboundPort string OutboundPort string
ShowGenesis bool ShowGenesis bool
AddPeer string AddPeer string
@ -104,17 +106,19 @@ func Init() {
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)") flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support") flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers") flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on") flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server") flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server") flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)") flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseSeed, "seed", true, "seed peers") flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)") flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use") flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)") flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)") flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")

View File

@ -30,50 +30,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/wire" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/ui/qt/qwhisper"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )
/*
func LoadExtension(path string) (uintptr, error) {
lib, err := ffi.NewLibrary(path)
if err != nil {
return 0, err
}
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
if err != nil {
return 0, err
}
ptr := so()
err = lib.Close()
if err != nil {
return 0, err
}
return ptr.Interface().(uintptr), nil
}
*/
/*
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
fmt.Printf("Fetched vec with addr: %#x\n", vec)
if errr != nil {
fmt.Println(errr)
} else {
context.SetVar("vec", (unsafe.Pointer)(vec))
}
*/
var guilogger = logger.NewLogger("GUI") var guilogger = logger.NewLogger("GUI")
type Gui struct { type Gui struct {
@ -88,6 +57,7 @@ type Gui struct {
// The public Ethereum library // The public Ethereum library
uiLib *UiLib uiLib *UiLib
whisper *qwhisper.Whisper
txDb *ethdb.LDBDatabase txDb *ethdb.LDBDatabase
@ -97,7 +67,7 @@ type Gui struct {
pipe *xeth.JSXEth pipe *xeth.JSXEth
Session string Session string
clientIdentity *wire.SimpleClientIdentity clientIdentity *p2p.SimpleClientIdentity
config *ethutil.ConfigManager config *ethutil.ConfigManager
plugins map[string]plugin plugins map[string]plugin
@ -107,7 +77,7 @@ type Gui struct {
} }
// Create GUI, but doesn't start it // Create GUI, but doesn't start it
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *wire.SimpleClientIdentity, session string, logLevel int) *Gui { func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *p2p.SimpleClientIdentity, session string, logLevel int) *Gui {
db, err := ethdb.NewLDBDatabase("tx_database") db, err := ethdb.NewLDBDatabase("tx_database")
if err != nil { if err != nil {
panic(err) panic(err)
@ -138,10 +108,12 @@ func (gui *Gui) Start(assetPath string) {
gui.engine = qml.NewEngine() gui.engine = qml.NewEngine()
context := gui.engine.Context() context := gui.engine.Context()
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath) gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
gui.whisper = qwhisper.New(gui.eth.Whisper())
// Expose the eth library and the ui library to QML // Expose the eth library and the ui library to QML
context.SetVar("gui", gui) context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib) context.SetVar("eth", gui.uiLib)
context.SetVar("shh", gui.whisper)
// Load the main QML interface // Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
@ -246,10 +218,10 @@ func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
} }
func (gui *Gui) setInitialChain(ancientBlocks bool) { func (gui *Gui) setInitialChain(ancientBlocks bool) {
sBlk := gui.eth.ChainManager().LastBlockHash sBlk := gui.eth.ChainManager().LastBlockHash()
blk := gui.eth.ChainManager().GetBlock(sBlk) blk := gui.eth.ChainManager().GetBlock(sBlk)
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) { for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
sBlk = blk.PrevHash sBlk = blk.ParentHash()
gui.processBlock(blk, true) gui.processBlock(blk, true)
} }
@ -297,7 +269,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
addr := gui.address() addr := gui.address()
var inout string var inout string
if bytes.Compare(tx.Sender(), addr) == 0 { if bytes.Compare(tx.From(), addr) == 0 {
inout = "send" inout = "send"
} else { } else {
inout = "recv" inout = "recv"
@ -305,27 +277,27 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
var ( var (
ptx = xeth.NewJSTx(tx, pipe.World().State()) ptx = xeth.NewJSTx(tx, pipe.World().State())
send = nameReg.Storage(tx.Sender()) send = nameReg.Storage(tx.From())
rec = nameReg.Storage(tx.Recipient) rec = nameReg.Storage(tx.To())
s, r string s, r string
) )
if tx.CreatesContract() { if core.MessageCreatesContract(tx) {
rec = nameReg.Storage(tx.CreationAddress(pipe.World().State())) rec = nameReg.Storage(core.AddressFromMessage(tx))
} }
if send.Len() != 0 { if send.Len() != 0 {
s = strings.Trim(send.Str(), "\x00") s = strings.Trim(send.Str(), "\x00")
} else { } else {
s = ethutil.Bytes2Hex(tx.Sender()) s = ethutil.Bytes2Hex(tx.From())
} }
if rec.Len() != 0 { if rec.Len() != 0 {
r = strings.Trim(rec.Str(), "\x00") r = strings.Trim(rec.Str(), "\x00")
} else { } else {
if tx.CreatesContract() { if core.MessageCreatesContract(tx) {
r = ethutil.Bytes2Hex(tx.CreationAddress(pipe.World().State())) r = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
} else { } else {
r = ethutil.Bytes2Hex(tx.Recipient) r = ethutil.Bytes2Hex(tx.To())
} }
} }
ptx.Sender = s ptx.Sender = s
@ -350,7 +322,7 @@ func (gui *Gui) readPreviousTransactions() {
} }
func (gui *Gui) processBlock(block *types.Block, initial bool) { func (gui *Gui) processBlock(block *types.Block, initial bool) {
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00") name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase()).Str(), "\x00")
b := xeth.NewJSBlock(block) b := xeth.NewJSBlock(block)
b.Name = name b.Name = name
@ -391,6 +363,8 @@ func (gui *Gui) update() {
gui.setPeerInfo() gui.setPeerInfo()
}() }()
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
for _, plugin := range gui.plugins { for _, plugin := range gui.plugins {
guilogger.Infoln("Loading plugin ", plugin.Name) guilogger.Infoln("Loading plugin ", plugin.Name)
@ -409,8 +383,7 @@ func (gui *Gui) update() {
miningLabel := gui.getObjectByName("miningLabel") miningLabel := gui.getObjectByName("miningLabel")
events := gui.eth.EventMux().Subscribe( events := gui.eth.EventMux().Subscribe(
eth.ChainSyncEvent{}, //eth.PeerListEvent{},
eth.PeerListEvent{},
core.NewBlockEvent{}, core.NewBlockEvent{},
core.TxPreEvent{}, core.TxPreEvent{},
core.TxPostEvent{}, core.TxPostEvent{},
@ -427,7 +400,7 @@ func (gui *Gui) update() {
switch ev := ev.(type) { switch ev := ev.(type) {
case core.NewBlockEvent: case core.NewBlockEvent:
gui.processBlock(ev.Block, false) gui.processBlock(ev.Block, false)
if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 { if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil) gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
} }
@ -448,30 +421,28 @@ func (gui *Gui) update() {
tx := ev.Tx tx := ev.Tx
object := state.GetAccount(gui.address()) object := state.GetAccount(gui.address())
if bytes.Compare(tx.Sender(), gui.address()) == 0 { if bytes.Compare(tx.From(), gui.address()) == 0 {
object.SubAmount(tx.Value) object.SubAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode()) gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 { } else if bytes.Compare(tx.To(), gui.address()) == 0 {
object.AddAmount(tx.Value) object.AddAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode()) gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} }
gui.setWalletValue(object.Balance(), nil) gui.setWalletValue(object.Balance(), nil)
state.UpdateStateObject(object) state.UpdateStateObject(object)
case eth.PeerListEvent:
gui.setPeerInfo()
} }
case <-peerUpdateTicker.C: case <-peerUpdateTicker.C:
gui.setPeerInfo() gui.setPeerInfo()
case <-generalUpdateTicker.C: case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String() statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
lastBlockLabel.Set("text", statusText) lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash") miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
/*
blockLength := gui.eth.BlockPool().BlocksProcessed blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength chainLength := gui.eth.BlockPool().ChainLength
@ -482,6 +453,7 @@ func (gui *Gui) update() {
) )
dlWidget.Set("value", pct) dlWidget.Set("value", pct)
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength)) dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
*/
case <-statsUpdateTicker.C: case <-statsUpdateTicker.C:
gui.setStatsPane() gui.setStatsPane()
@ -509,7 +481,7 @@ Heap Alloc: %d
CGNext: %x CGNext: %x
NumGC: %d NumGC: %d
`, Version, runtime.Version(), `, Version, runtime.Version(),
eth.ProtocolVersion, eth.P2PVersion, eth.ProtocolVersion, 2,
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(), runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
memStats.Alloc, memStats.HeapAlloc, memStats.Alloc, memStats.HeapAlloc,
memStats.NextGC, memStats.NumGC, memStats.NextGC, memStats.NumGC,
@ -531,3 +503,35 @@ func (gui *Gui) privateKey() string {
func (gui *Gui) address() []byte { func (gui *Gui) address() []byte {
return gui.eth.KeyManager().Address() return gui.eth.KeyManager().Address()
} }
/*
func LoadExtension(path string) (uintptr, error) {
lib, err := ffi.NewLibrary(path)
if err != nil {
return 0, err
}
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
if err != nil {
return 0, err
}
ptr := so()
err = lib.Close()
if err != nil {
return 0, err
}
return ptr.Interface().(uintptr), nil
}
*/
/*
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
fmt.Printf("Fetched vec with addr: %#x\n", vec)
if errr != nil {
fmt.Println(errr)
} else {
context.SetVar("vec", (unsafe.Pointer)(vec))
}
*/

View File

@ -139,7 +139,7 @@ func (app *HtmlApplication) Window() *qml.Window {
} }
func (app *HtmlApplication) NewBlock(block *types.Block) { func (app *HtmlApplication) NewBlock(block *types.Block) {
b := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} b := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
app.webView.Call("onNewBlockCb", b) app.webView.Call("onNewBlockCb", b)
} }

View File

@ -23,15 +23,15 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )
const ( const (
ClientIdentifier = "Mist" ClientIdentifier = "Mist"
Version = "0.7.9" Version = "0.7.11"
) )
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
@ -58,8 +58,8 @@ func run() error {
// create, import, export keys // create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive) utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier) clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer) ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
if ShowGenesis { if ShowGenesis {
utils.ShowGenesis(ethereum) utils.ShowGenesis(ethereum)
@ -69,6 +69,10 @@ func run() error {
utils.StartRpc(ethereum, RpcPort) utils.StartRpc(ethereum, RpcPort)
} }
if StartWebSockets {
utils.StartWebSockets(ethereum)
}
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel) gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
gui.stdLog = stdLog gui.stdLog = stdLog
@ -100,16 +104,10 @@ func main() {
utils.HandleInterrupt() utils.HandleInterrupt()
if StartWebSockets {
utils.StartWebSockets(ethereum)
}
// we need to run the interrupt callbacks in case gui is closed // we need to run the interrupt callbacks in case gui is closed
// this skips if we got here by actual interrupt stopping the GUI // this skips if we got here by actual interrupt stopping the GUI
if !interrupted { if !interrupted {
utils.RunInterruptCallbacks(os.Interrupt) utils.RunInterruptCallbacks(os.Interrupt)
} }
// this blocks the thread
ethereum.WaitForShutdown()
logger.Flush() logger.Flush()
} }

View File

@ -66,7 +66,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
// Events // Events
func (app *QmlApplication) NewBlock(block *types.Block) { func (app *QmlApplication) NewBlock(block *types.Block) {
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())} pblock := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
app.win.Call("onNewBlockCb", pblock) app.win.Call("onNewBlockCb", pblock)
} }

View File

@ -24,11 +24,12 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event/filter"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
@ -57,6 +58,7 @@ type UiLib struct {
jsEngine *javascript.JSRE jsEngine *javascript.JSRE
filterCallbacks map[int][]int filterCallbacks map[int][]int
filterManager *filter.FilterManager
miner *miner.Miner miner *miner.Miner
} }
@ -64,6 +66,7 @@ type UiLib struct {
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)} lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
lib.miner = miner.New(eth.KeyManager().Address(), eth) lib.miner = miner.New(eth.KeyManager().Address(), eth)
lib.filterManager = filter.NewFilterManager(eth.EventMux())
return lib return lib
} }
@ -123,7 +126,8 @@ func (self *UiLib) LookupAddress(name string) string {
} }
func (self *UiLib) PastPeers() *ethutil.List { func (self *UiLib) PastPeers() *ethutil.List {
return ethutil.NewList(eth.PastPeers()) return ethutil.NewList([]string{})
//return ethutil.NewList(eth.PastPeers())
} }
func (self *UiLib) ImportTx(rlpTx string) { func (self *UiLib) ImportTx(rlpTx string) {
@ -191,7 +195,7 @@ func (ui *UiLib) Connect(button qml.Object) {
} }
func (ui *UiLib) ConnectToPeer(addr string) { func (ui *UiLib) ConnectToPeer(addr string) {
ui.eth.ConnectToPeer(addr) ui.eth.SuggestPeer(addr)
} }
func (ui *UiLib) AssetPath(p string) string { func (ui *UiLib) AssetPath(p string) string {
@ -221,94 +225,6 @@ func (self *UiLib) StartDebugger() {
dbWindow.Show() dbWindow.Show()
} }
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
filter := qt.NewFilterFromMap(object, self.eth)
filter.MessageCallback = func(messages state.Messages) {
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
}
id = self.eth.InstallFilter(filter)
return id
}
func (self *UiLib) NewFilterString(typ string) (id int) {
filter := core.NewFilter(self.eth)
filter.BlockCallback = func(block *types.Block) {
if self.win != nil && self.win.Root() != nil {
self.win.Root().Call("invokeFilterCallback", "{}", id)
} else {
fmt.Println("QML is lagging")
}
}
id = self.eth.InstallFilter(filter)
return id
}
func (self *UiLib) Messages(id int) *ethutil.List {
filter := self.eth.GetFilter(id)
if filter != nil {
messages := xeth.ToJSMessages(filter.Find())
return messages
}
return ethutil.EmptyList()
}
func (self *UiLib) UninstallFilter(id int) {
self.eth.UninstallFilter(id)
}
func mapToTxParams(object map[string]interface{}) map[string]string {
// Default values
if object["from"] == nil {
object["from"] = ""
}
if object["to"] == nil {
object["to"] = ""
}
if object["value"] == nil {
object["value"] = ""
}
if object["gas"] == nil {
object["gas"] = ""
}
if object["gasPrice"] == nil {
object["gasPrice"] = ""
}
var dataStr string
var data []string
if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data)
} else if str, ok := object["data"].(string); ok {
data = []string{str}
}
for _, str := range data {
if ethutil.IsHex(str) {
str = str[2:]
if len(str) != 64 {
str = ethutil.LeftPadString(str, 64)
}
} else {
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
}
dataStr += str
}
object["data"] = dataStr
conv := make(map[string]string)
for key, value := range object {
if v, ok := value.(string); ok {
conv[key] = v
}
}
return conv
}
func (self *UiLib) Transact(params map[string]interface{}) (string, error) { func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
object := mapToTxParams(params) object := mapToTxParams(params)
@ -372,3 +288,104 @@ func (self *UiLib) ToggleMining() bool {
return false return false
} }
} }
func (self *UiLib) ToHex(data string) string {
return "0x" + ethutil.Bytes2Hex([]byte(data))
}
func (self *UiLib) ToAscii(data string) string {
start := 0
if len(data) > 1 && data[0:2] == "0x" {
start = 2
}
return string(ethutil.Hex2Bytes(data[start:]))
}
/// Ethereum filter methods
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
filter := qt.NewFilterFromMap(object, self.eth)
filter.MessageCallback = func(messages state.Messages) {
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
}
id = self.filterManager.InstallFilter(filter)
return id
}
func (self *UiLib) NewFilterString(typ string) (id int) {
filter := core.NewFilter(self.eth)
filter.BlockCallback = func(block *types.Block) {
if self.win != nil && self.win.Root() != nil {
self.win.Root().Call("invokeFilterCallback", "{}", id)
} else {
fmt.Println("QML is lagging")
}
}
id = self.filterManager.InstallFilter(filter)
return id
}
func (self *UiLib) Messages(id int) *ethutil.List {
filter := self.filterManager.GetFilter(id)
if filter != nil {
messages := xeth.ToJSMessages(filter.Find())
return messages
}
return ethutil.EmptyList()
}
func (self *UiLib) UninstallFilter(id int) {
self.filterManager.UninstallFilter(id)
}
func mapToTxParams(object map[string]interface{}) map[string]string {
// Default values
if object["from"] == nil {
object["from"] = ""
}
if object["to"] == nil {
object["to"] = ""
}
if object["value"] == nil {
object["value"] = ""
}
if object["gas"] == nil {
object["gas"] = ""
}
if object["gasPrice"] == nil {
object["gasPrice"] = ""
}
var dataStr string
var data []string
if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data)
} else if str, ok := object["data"].(string); ok {
data = []string{str}
}
for _, str := range data {
if ethutil.IsHex(str) {
str = str[2:]
if len(str) != 64 {
str = ethutil.LeftPadString(str, 64)
}
} else {
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
}
dataStr += str
}
object["data"] = dataStr
conv := make(map[string]string)
for key, value := range object {
if v, ok := value.(string); ok {
conv[key] = v
}
}
return conv
}

View File

@ -4,23 +4,25 @@ import (
"fmt" "fmt"
"io" "io"
"log" "log"
"net"
"os" "os"
"os/signal" "os/signal"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"time"
"bitbucket.org/kardianos/osext" "bitbucket.org/kardianos/osext"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/wire"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
) )
@ -144,17 +146,32 @@ func NewDatabase() ethutil.Database {
return db return db
} }
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity { func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier) return p2p.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier, pubkey)
} }
func NewEthereum(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum { func NatType(natType string, gateway string) (nat p2p.NAT) {
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp) switch natType {
case "UPNP":
nat = p2p.UPNP()
case "PMP":
ip := net.ParseIP(gateway)
if ip == nil {
clilogger.Fatalf("cannot resolve PMP gateway IP %s", gateway)
}
nat = p2p.PMP(ip)
case "":
default:
clilogger.Fatalf("unrecognised NAT type '%s'", natType)
}
return
}
func NewEthereum(db ethutil.Database, clientIdentity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, OutboundPort string, MaxPeer int) *eth.Ethereum {
ethereum, err := eth.New(db, clientIdentity, keyManager, nat, OutboundPort, MaxPeer)
if err != nil { if err != nil {
clilogger.Fatalln("eth start err:", err) clilogger.Fatalln("eth start err:", err)
} }
ethereum.Port = OutboundPort
ethereum.MaxPeers = MaxPeer
return ethereum return ethereum
} }
@ -268,11 +285,6 @@ func StartMining(ethereum *eth.Ethereum) bool {
if gminer == nil { if gminer == nil {
gminer = miner.New(addr, ethereum) gminer = miner.New(addr, ethereum)
} }
// Give it some time to connect with peers
time.Sleep(3 * time.Second)
for !ethereum.IsUpToDate() {
time.Sleep(5 * time.Second)
}
gminer.Start() gminer.Start()
}() }()
RegisterInterrupt(func(os.Signal) { RegisterInterrupt(func(os.Signal) {
@ -315,7 +327,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
return fmt.Errorf("unknown block %x", hash) return fmt.Errorf("unknown block %x", hash)
} }
parent := ethereum.ChainManager().GetBlock(block.PrevHash) parent := ethereum.ChainManager().GetBlock(block.ParentHash())
_, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block) _, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block)
if err != nil { if err != nil {
@ -325,3 +337,25 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
return nil return nil
} }
func ImportChain(ethereum *eth.Ethereum, fn string) error {
clilogger.Infof("importing chain '%s'\n", fn)
fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
var chain types.Blocks
if err := rlp.Decode(fh, &chain); err != nil {
return err
}
ethereum.ChainManager().Reset()
if err := ethereum.ChainManager().InsertChain(chain); err != nil {
return err
}
clilogger.Infof("imported %d blocks\n", len(chain))
return nil
}

View File

@ -30,15 +30,15 @@ func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *
} }
func (self *VMEnv) Origin() []byte { return self.transactor } func (self *VMEnv) Origin() []byte { return self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
func (self *VMEnv) Time() int64 { return self.block.Time } func (self *VMEnv) Time() int64 { return self.block.Time() }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) State() *state.StateDB { return self.state } 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) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) AddLog(log state.Log) { func (self *VMEnv) AddLog(log state.Log) {
@ -49,9 +49,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
} }
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution { func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
evm := vm.New(self, vm.DebugVmTy) return core.NewExecution(self, addr, data, gas, price, value)
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) { func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {

View File

@ -1,12 +1,15 @@
package utils package utils
import ( import (
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/websocket" "github.com/ethereum/go-ethereum/websocket"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
) )
var wslogger = logger.NewLogger("WS")
func args(v ...interface{}) []interface{} { func args(v ...interface{}) []interface{} {
return v return v
} }
@ -106,6 +109,8 @@ func (self *WebSocketServer) Serv() {
} }
func StartWebSockets(eth *eth.Ethereum) { func StartWebSockets(eth *eth.Ethereum) {
wslogger.Infoln("Starting WebSockets")
sock := NewWebSocketServer(eth) sock := NewWebSocketServer(eth)
go sock.Serv() go sock.Serv()
} }

View File

@ -2,7 +2,6 @@ package core
import ( import (
"bytes" "bytes"
"container/list"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -14,10 +13,11 @@ import (
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow/ezp" "github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/wire" "gopkg.in/fatih/set.v0"
) )
var statelogger = logger.NewLogger("BLOCK") var statelogger = logger.NewLogger("BLOCK")
@ -38,13 +38,12 @@ type EthManager interface {
BlockManager() *BlockManager BlockManager() *BlockManager
ChainManager() *ChainManager ChainManager() *ChainManager
TxPool() *TxPool TxPool() *TxPool
Broadcast(msgType wire.MsgType, data []interface{})
PeerCount() int PeerCount() int
IsMining() bool IsMining() bool
IsListening() bool IsListening() bool
Peers() *list.List Peers() []*p2p.Peer
KeyManager() *crypto.KeyManager KeyManager() *crypto.KeyManager
ClientIdentity() wire.ClientIdentity ClientIdentity() p2p.ClientIdentity
Db() ethutil.Database Db() ethutil.Database
EventMux() *event.TypeMux EventMux() *event.TypeMux
} }
@ -58,8 +57,8 @@ type BlockManager struct {
mem map[string]*big.Int mem map[string]*big.Int
// Proof of work used for validating // Proof of work used for validating
Pow pow.PoW Pow pow.PoW
// The ethereum manager interface
eth EthManager txpool *TxPool
// The last attempted block is mainly used for debugging purposes // The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during // This does not have to be a valid block and will be set during
@ -71,21 +70,21 @@ type BlockManager struct {
eventMux *event.TypeMux eventMux *event.TypeMux
} }
func NewBlockManager(ethereum EthManager) *BlockManager { func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager {
sm := &BlockManager{ sm := &BlockManager{
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: ezp.New(), Pow: ezp.New(),
eth: ethereum, bc: chainManager,
bc: ethereum.ChainManager(), eventMux: eventMux,
eventMux: ethereum.EventMux(), txpool: txpool,
} }
return sm return sm
} }
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) { func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
coinbase := statedb.GetOrNewStateObject(block.Coinbase) coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent)) coinbase.SetGasPool(CalcGasLimit(parent, block))
// Process the transactions on to current block // Process the transactions on to current block
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false) receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
@ -111,11 +110,11 @@ done:
// If we are mining this block and validating we want to set the logs back to 0 // If we are mining this block and validating we want to set the logs back to 0
state.EmptyLogs() state.EmptyLogs()
txGas := new(big.Int).Set(tx.Gas) txGas := new(big.Int).Set(tx.Gas())
cb := state.GetStateObject(coinbase.Address()) cb := state.GetStateObject(coinbase.Address())
st := NewStateTransition(cb, tx, state, block) st := NewStateTransition(cb, tx, state, block)
err = st.TransitionState() _, err = st.TransitionState()
if err != nil { if err != nil {
switch { switch {
case IsNonceErr(err): case IsNonceErr(err):
@ -129,12 +128,11 @@ done:
statelogger.Infoln(err) statelogger.Infoln(err)
erroneous = append(erroneous, tx) erroneous = append(erroneous, tx)
err = nil err = nil
continue
} }
} }
txGas.Sub(txGas, st.gas) txGas.Sub(txGas, st.gas)
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice)) cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
// Update the state with pending changes // Update the state with pending changes
state.Update(txGas) state.Update(txGas)
@ -143,6 +141,7 @@ done:
receipt := types.NewReceipt(state.Root(), cumulative) receipt := types.NewReceipt(state.Root(), cumulative)
receipt.SetLogs(state.Logs()) receipt.SetLogs(state.Logs())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
chainlogger.Debugln(receipt)
// Notify all subscribers // Notify all subscribers
if !transientProcess { if !transientProcess {
@ -158,7 +157,7 @@ done:
} }
block.Reward = cumulativeSum block.Reward = cumulativeSum
block.GasUsed = totalUsedGas block.Header().GasUsed = totalUsedGas
return receipts, handled, unhandled, erroneous, err return receipts, handled, unhandled, erroneous, err
} }
@ -168,14 +167,15 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
if sm.bc.HasBlock(block.Hash()) { header := block.Header()
return nil, nil, &KnownBlockError{block.Number, block.Hash()} if sm.bc.HasBlock(header.Hash()) {
return nil, nil, &KnownBlockError{header.Number, header.Hash()}
} }
if !sm.bc.HasBlock(block.PrevHash) { if !sm.bc.HasBlock(header.ParentHash) {
return nil, nil, ParentError(block.PrevHash) return nil, nil, ParentError(header.ParentHash)
} }
parent := sm.bc.GetBlock(block.PrevHash) parent := sm.bc.GetBlock(header.ParentHash)
return sm.ProcessWithParent(block, parent) return sm.ProcessWithParent(block, parent)
} }
@ -183,13 +183,7 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) { func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
sm.lastAttemptedBlock = block sm.lastAttemptedBlock = block
state := parent.State().Copy() state := state.New(parent.Trie().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 // Block validation
if err = sm.ValidateBlock(block, parent); err != nil { if err = sm.ValidateBlock(block, parent); err != nil {
@ -201,21 +195,24 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
return return
} }
header := block.Header()
rbloom := types.CreateBloom(receipts) rbloom := types.CreateBloom(receipts)
if bytes.Compare(rbloom, block.LogsBloom) != 0 { if bytes.Compare(rbloom, header.Bloom) != 0 {
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
return return
} }
txSha := types.DeriveSha(block.Transactions()) txSha := types.DeriveSha(block.Transactions())
if bytes.Compare(txSha, block.TxSha) != 0 { if bytes.Compare(txSha, header.TxHash) != 0 {
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha) err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
return return
} }
receiptSha := types.DeriveSha(receipts) receiptSha := types.DeriveSha(receipts)
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 { if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha) fmt.Println("receipts", receipts)
err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
return return
} }
@ -225,8 +222,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
state.Update(ethutil.Big0) state.Update(ethutil.Big0)
if !block.State().Cmp(state) { if !bytes.Equal(header.Root, state.Root()) {
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root()) err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
return return
} }
@ -238,9 +235,9 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
messages := state.Manifest().Messages messages := state.Manifest().Messages
state.Manifest().Reset() state.Manifest().Reset()
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4]) chainlogger.Infof("Processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])
sm.eth.TxPool().RemoveSet(block.Transactions()) sm.txpool.RemoveSet(block.Transactions())
return td, messages, nil return td, messages, nil
} else { } else {
@ -250,18 +247,18 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) { func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
uncleDiff := new(big.Int) uncleDiff := new(big.Int)
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles() {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) 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(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
td := new(big.Int) td := new(big.Int)
td = td.Add(sm.bc.TD, uncleDiff) td = td.Add(sm.bc.Td(), uncleDiff)
td = td.Add(td, block.Difficulty) td = td.Add(td, block.Header().Difficulty)
// The new TD will only be accepted if the new difficulty is // The new TD will only be accepted if the new difficulty is
// is greater than the previous. // is greater than the previous.
if td.Cmp(sm.bc.TD) > 0 { if td.Cmp(sm.bc.Td()) > 0 {
return td, true return td, true
} }
@ -273,13 +270,13 @@ func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
// Validation validates easy over difficult (dagger takes longer time = difficult) // Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error { func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
expd := CalcDifficulty(block, parent) expd := CalcDifficulty(block, parent)
if expd.Cmp(block.Difficulty) < 0 { if expd.Cmp(block.Header().Difficulty) < 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
} }
diff := block.Time - parent.Time diff := block.Header().Time - parent.Header().Time
if diff < 0 { if diff < 0 {
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time) return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Header().Time, sm.bc.CurrentBlock().Header().Time)
} }
/* XXX /* XXX
@ -291,7 +288,7 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
// Verify the nonce of the block. Return an error if it's not valid // 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*/) { if !sm.Pow.Verify(block /*block.HashNoNonce(), block.Difficulty, block.Nonce*/) {
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce)) return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce))
} }
return nil return nil
@ -300,24 +297,28 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error { func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
reward := new(big.Int).Set(BlockReward) reward := new(big.Int).Set(BlockReward)
knownUncles := ethutil.Set(parent.Uncles) knownUncles := set.New()
nonces := ethutil.NewSet(block.Nonce) for _, uncle := range parent.Uncles() {
for _, uncle := range block.Uncles { knownUncles.Add(string(uncle.Hash()))
}
nonces := ethutil.NewSet(block.Header().Nonce)
for _, uncle := range block.Uncles() {
if nonces.Include(uncle.Nonce) { if nonces.Include(uncle.Nonce) {
// Error not unique // Error not unique
return UncleError("Uncle not unique") return UncleError("Uncle not unique")
} }
uncleParent := sm.bc.GetBlock(uncle.PrevHash) uncleParent := sm.bc.GetBlock(uncle.ParentHash)
if uncleParent == nil { if uncleParent == nil {
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4])) return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
} }
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 {
return UncleError("Uncle too old") return UncleError("Uncle too old")
} }
if knownUncles.Include(uncle.Hash()) { if knownUncles.Has(string(uncle.Hash())) {
return UncleError("Uncle in chain") return UncleError("Uncle in chain")
} }
@ -333,15 +334,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
} }
// Get the account associated with the coinbase // Get the account associated with the coinbase
account := statedb.GetAccount(block.Coinbase) account := statedb.GetAccount(block.Header().Coinbase)
// Reward amount of ether to the coinbase address // Reward amount of ether to the coinbase address
account.AddAmount(reward) account.AddAmount(reward)
statedb.Manifest().AddMessage(&state.Message{ statedb.Manifest().AddMessage(&state.Message{
To: block.Coinbase, To: block.Header().Coinbase,
Input: nil, Input: nil,
Origin: nil, Origin: nil,
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number, Block: block.Hash(), Timestamp: int64(block.Header().Time), Coinbase: block.Header().Coinbase, Number: block.Header().Number,
Value: new(big.Int).Add(reward, block.Reward), Value: new(big.Int).Add(reward, block.Reward),
}) })
@ -349,15 +350,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
} }
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) { func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
if !sm.bc.HasBlock(block.PrevHash) { if !sm.bc.HasBlock(block.Header().ParentHash) {
return nil, ParentError(block.PrevHash) return nil, ParentError(block.Header().ParentHash)
} }
sm.lastAttemptedBlock = block sm.lastAttemptedBlock = block
var ( var (
parent = sm.bc.GetBlock(block.PrevHash) parent = sm.bc.GetBlock(block.Header().ParentHash)
state = parent.State().Copy() state = state.New(parent.Trie().Copy())
) )
defer state.Reset() defer state.Reset()

View File

@ -1,18 +1,22 @@
package core package core
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"sync"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
) )
var chainlogger = logger.NewLogger("CHAIN") var chainlogger = logger.NewLogger("CHAIN")
/*
func AddTestNetFunds(block *types.Block) { func AddTestNetFunds(block *types.Block) {
for _, addr := range []string{ for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99", "51ba59315b3a95761d0863b05ccc7a7f54703d99",
@ -30,39 +34,87 @@ func AddTestNetFunds(block *types.Block) {
block.State().UpdateStateObject(account) block.State().UpdateStateObject(account)
} }
} }
*/
func CalcDifficulty(block, parent *types.Block) *big.Int { func CalcDifficulty(block, parent *types.Block) *big.Int {
diff := new(big.Int) diff := new(big.Int)
adjust := new(big.Int).Rsh(parent.Difficulty, 10) bh, ph := block.Header(), parent.Header()
if block.Time >= parent.Time+5 { adjust := new(big.Int).Rsh(ph.Difficulty, 10)
diff.Sub(parent.Difficulty, adjust) if bh.Time >= ph.Time+5 {
diff.Sub(ph.Difficulty, adjust)
} else { } else {
diff.Add(parent.Difficulty, adjust) diff.Add(ph.Difficulty, adjust)
} }
return diff return diff
} }
func CalcGasLimit(parent, block *types.Block) *big.Int {
if block.Number().Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6)
}
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit())
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed()), big.NewRat(6, 5))
curInt := new(big.Int).Div(current.Num(), current.Denom())
result := new(big.Int).Add(previous, curInt)
result.Div(result, big.NewInt(1024))
min := big.NewInt(125000)
return ethutil.BigMax(min, result)
}
type ChainManager struct { type ChainManager struct {
//eth EthManager //eth EthManager
processor types.BlockProcessor processor types.BlockProcessor
eventMux *event.TypeMux eventMux *event.TypeMux
genesisBlock *types.Block genesisBlock *types.Block
// Last known total difficulty // Last known total difficulty
TD *big.Int mu sync.RWMutex
td *big.Int
LastBlockNumber uint64 lastBlockNumber uint64
currentBlock *types.Block
CurrentBlock *types.Block lastBlockHash []byte
LastBlockHash []byte
transState *state.StateDB 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 { func NewChainManager(mux *event.TypeMux) *ChainManager {
bc := &ChainManager{} bc := &ChainManager{}
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis)) bc.genesisBlock = GenesisBlock()
bc.eventMux = mux bc.eventMux = mux
bc.setLastBlock() bc.setLastBlock()
@ -72,12 +124,19 @@ func NewChainManager(mux *event.TypeMux) *ChainManager {
return bc return bc
} }
func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
self.mu.RLock()
defer self.mu.RUnlock()
return self.td, self.currentBlock.Hash(), self.Genesis().Hash()
}
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
self.processor = proc self.processor = proc
} }
func (self *ChainManager) State() *state.StateDB { func (self *ChainManager) State() *state.StateDB {
return self.CurrentBlock.State() return state.New(self.CurrentBlock().Trie())
} }
func (self *ChainManager) TransState() *state.StateDB { func (self *ChainManager) TransState() *state.StateDB {
@ -87,46 +146,48 @@ func (self *ChainManager) TransState() *state.StateDB {
func (bc *ChainManager) setLastBlock() { func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 { if len(data) != 0 {
// Prep genesis var block types.Block
AddTestNetFunds(bc.genesisBlock) rlp.Decode(bytes.NewReader(data), &block)
bc.currentBlock = &block
block := types.NewBlockFromBytes(data) bc.lastBlockHash = block.Hash()
bc.CurrentBlock = block bc.lastBlockNumber = block.Header().Number.Uint64()
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64()
// Set the last know difficulty (might be 0x0 as initial value, Genesis) // Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
} else { } else {
bc.Reset() bc.Reset()
} }
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash()) chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
} }
// Block creation & chain handling // Block creation & chain handling
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
var root interface{} bc.mu.RLock()
hash := ZeroHash256 defer bc.mu.RUnlock()
var root []byte
parentHash := ZeroHash256
if bc.CurrentBlock != nil { if bc.CurrentBlock != nil {
root = bc.CurrentBlock.Root() root = bc.currentBlock.Header().Root
hash = bc.LastBlockHash parentHash = bc.lastBlockHash
} }
block := types.CreateBlock( block := types.NewBlock(
root, parentHash,
hash,
coinbase, coinbase,
root,
ethutil.BigPow(2, 32), ethutil.BigPow(2, 32),
nil, nil,
"") "")
parent := bc.CurrentBlock parent := bc.currentBlock
if parent != nil { if parent != nil {
block.Difficulty = CalcDifficulty(block, parent) header := block.Header()
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1) header.Difficulty = CalcDifficulty(block, parent)
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock) header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
header.GasLimit = CalcGasLimit(parent, block)
} }
@ -134,41 +195,46 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
} }
func (bc *ChainManager) Reset() { func (bc *ChainManager) Reset() {
AddTestNetFunds(bc.genesisBlock) bc.mu.Lock()
defer bc.mu.Unlock()
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
ethutil.Config.Db.Delete(block.Hash())
}
bc.genesisBlock.Trie().Sync()
// Prepare the genesis block // Prepare the genesis block
bc.write(bc.genesisBlock) bc.write(bc.genesisBlock)
bc.insert(bc.genesisBlock) bc.insert(bc.genesisBlock)
bc.CurrentBlock = bc.genesisBlock bc.currentBlock = bc.genesisBlock
bc.SetTotalDifficulty(ethutil.Big("0")) 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 { func (self *ChainManager) Export() []byte {
chainlogger.Infoln("exporting", self.CurrentBlock.Number, "blocks") self.mu.RLock()
defer self.mu.RUnlock()
blocks := make(types.Blocks, int(self.CurrentBlock.Number.Int64())+1) chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
for block := self.CurrentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
blocks[block.Number.Int64()] = block blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
blocks[block.NumberU64()] = block
} }
return ethutil.Encode(blocks) return ethutil.Encode(blocks)
} }
func (bc *ChainManager) insert(block *types.Block) { func (bc *ChainManager) insert(block *types.Block) {
encodedBlock := block.RlpEncode() encodedBlock := ethutil.Encode(block)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
bc.CurrentBlock = block bc.currentBlock = block
bc.LastBlockHash = block.Hash() bc.lastBlockHash = block.Hash()
} }
func (bc *ChainManager) write(block *types.Block) { func (bc *ChainManager) write(block *types.Block) {
bc.writeBlockInfo(block) bc.writeBlockInfo(block)
encodedBlock := block.RlpEncode() encodedBlock := ethutil.Encode(block)
ethutil.Config.Db.Put(block.Hash(), encodedBlock) ethutil.Config.Db.Put(block.Hash(), encodedBlock)
} }
@ -183,7 +249,7 @@ func (bc *ChainManager) HasBlock(hash []byte) bool {
return len(data) != 0 return len(data) != 0
} }
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) { func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
block := self.GetBlock(hash) block := self.GetBlock(hash)
if block == nil { if block == nil {
return return
@ -193,11 +259,11 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
for i := uint64(0); i < max; i++ { for i := uint64(0); i < max; i++ {
chain = append(chain, block.Hash()) chain = append(chain, block.Hash())
if block.Number.Cmp(ethutil.Big0) <= 0 { if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
break break
} }
block = self.GetBlock(block.PrevHash) block = self.GetBlock(block.Header().ParentHash)
} }
return return
@ -208,65 +274,61 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
var block types.Block
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
fmt.Println(err)
return nil
}
return types.NewBlockFromBytes(data) return &block
} }
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
block := self.CurrentBlock self.mu.RLock()
for ; block != nil; block = self.GetBlock(block.PrevHash) { defer self.mu.RUnlock()
if block.Number.Uint64() == num {
block := self.currentBlock
for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
if block.Header().Number.Uint64() == num {
break break
} }
} }
if block != nil && block.Number.Uint64() == 0 && num != 0 { if block != nil && block.Header().Number.Uint64() == 0 && num != 0 {
return nil return nil
} }
return block return block
} }
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) { func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes()) ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
bc.TD = td bc.td = td
} }
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
parent := self.GetBlock(block.PrevHash) parent := self.GetBlock(block.Header().ParentHash)
if parent == nil { if parent == nil {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash) return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
} }
parentTd := parent.BlockInfo().TD parentTd := parent.Td
uncleDiff := new(big.Int) uncleDiff := new(big.Int)
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles() {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
} }
td := new(big.Int) td := new(big.Int)
td = td.Add(parentTd, uncleDiff) td = td.Add(parentTd, uncleDiff)
td = td.Add(td, block.Difficulty) td = td.Add(td, block.Header().Difficulty)
return td, nil 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 // Unexported method for writing extra non-essential block info to the db
func (bc *ChainManager) writeBlockInfo(block *types.Block) { func (bc *ChainManager) writeBlockInfo(block *types.Block) {
bc.LastBlockNumber++ 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() { func (bc *ChainManager) Stop() {
@ -283,24 +345,30 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
continue continue
} }
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4]) h := block.Header()
chainlogger.Infof("block #%v process failed (%x)\n", h.Number, h.Hash()[:4])
chainlogger.Infoln(block) chainlogger.Infoln(block)
chainlogger.Infoln(err) chainlogger.Infoln(err)
return err return err
} }
self.mu.Lock()
{
self.write(block) self.write(block)
if td.Cmp(self.TD) > 0 { cblock := self.currentBlock
if block.Number.Cmp(new(big.Int).Add(self.CurrentBlock.Number, ethutil.Big1)) < 0 { if td.Cmp(self.td) > 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]) if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
} }
self.SetTotalDifficulty(td) self.setTotalDifficulty(td)
self.insert(block) self.insert(block)
self.transState = self.State().Copy() self.transState = state.New(cblock.Trie().Copy())
//sm.eth.TxPool().RemoveSet(block.Transactions())
} }
}
self.mu.Unlock()
self.eventMux.Post(NewBlockEvent{block}) self.eventMux.Post(NewBlockEvent{block})
self.eventMux.Post(messages) self.eventMux.Post(messages)
} }

View File

@ -2,18 +2,138 @@ package core
import ( import (
"fmt" "fmt"
"os"
"path" "path"
"reflect"
"runtime"
"strconv"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rlp"
) )
func TestChainInsertions(t *testing.T) { //var Logger logpkg.LogSystem
c1, err := ethutil.ReadAllFile(path.Join("..", "_data", "chain1"))
//var Log = logpkg.NewLogger("TEST")
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
//Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.DebugLevel)
//logpkg.AddLogSystem(Logger)
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
db, err := ethdb.NewMemDatabase()
if err != nil {
panic("Could not create mem-db, failing")
}
ethutil.Config.Db = db
}
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
fh, err := os.OpenFile(path.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil {
return nil, err
}
defer fh.Close()
var chain types.Blocks
if err := rlp.Decode(fh, &chain); err != nil {
return nil, err
}
return chain, nil
}
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
err := chainMan.InsertChain(chain)
done <- true
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
t.FailNow() t.FailNow()
} }
data1, _ := ethutil.Decode([]byte(c1), 0) }
fmt.Println(data1)
func TestChainInsertions(t *testing.T) {
chain1, err := loadChain("valid1", t)
if err != nil {
fmt.Println(err)
t.FailNow()
}
fmt.Println(len(chain1))
chain2, err := loadChain("valid2", t)
if err != nil {
fmt.Println(err)
t.FailNow()
}
var eventMux event.TypeMux
chainMan := NewChainManager(&eventMux)
txPool := NewTxPool(chainMan, &eventMux)
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan)
const max = 2
done := make(chan bool, max)
go insertChain(done, chainMan, chain1, t)
go insertChain(done, chainMan, chain2, t)
for i := 0; i < max; i++ {
<-done
}
if reflect.DeepEqual(chain2[len(chain2)-1], chainMan.CurrentBlock()) {
t.Error("chain2 is canonical and shouldn't be")
}
if !reflect.DeepEqual(chain1[len(chain1)-1], chainMan.CurrentBlock()) {
t.Error("chain1 isn't canonical and should be")
}
}
func TestChainMultipleInsertions(t *testing.T) {
const max = 4
chains := make([]types.Blocks, max)
var longest int
for i := 0; i < max; i++ {
var err error
name := "valid" + strconv.Itoa(i+1)
chains[i], err = loadChain(name, t)
if len(chains[i]) >= len(chains[longest]) {
longest = i
}
fmt.Println("loaded", name, "with a length of", len(chains[i]))
if err != nil {
fmt.Println(err)
t.FailNow()
}
}
var eventMux event.TypeMux
chainMan := NewChainManager(&eventMux)
txPool := NewTxPool(chainMan, &eventMux)
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan)
done := make(chan bool, max)
for i, chain := range chains {
var i int = i
go func() {
insertChain(done, chainMan, chain, t)
fmt.Println(i, "done")
}()
}
for i := 0; i < max; i++ {
<-done
}
if !reflect.DeepEqual(chains[longest][len(chains[longest])-1], chainMan.CurrentBlock()) {
t.Error("Invalid canonical chain")
}
} }

View File

@ -10,3 +10,6 @@ type TxPostEvent struct{ Tx *types.Transaction }
// NewBlockEvent is posted when a block has been imported. // NewBlockEvent is posted when a block has been imported.
type NewBlockEvent struct{ Block *types.Block } type NewBlockEvent struct{ Block *types.Block }
// NewMinedBlockEvent is posted when a block has been imported.
type NewMinedBlockEvent struct{ Block *types.Block }

View File

@ -3,22 +3,21 @@ package core
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm" "github.com/ethereum/go-ethereum/vm"
) )
type Execution struct { type Execution struct {
vm vm.VirtualMachine env vm.Environment
address, input []byte address, input []byte
Gas, price, value *big.Int Gas, price, value *big.Int
object *state.StateObject
SkipTransfer bool SkipTransfer bool
} }
func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution { func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value} return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
} }
func (self *Execution) Addr() []byte { func (self *Execution) Addr() []byte {
@ -27,14 +26,19 @@ func (self *Execution) Addr() []byte {
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) { func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) {
// Retrieve the executing code // Retrieve the executing code
code := self.vm.Env().State().GetCode(codeAddr) code := self.env.State().GetCode(codeAddr)
return self.exec(code, codeAddr, caller) return self.exec(code, codeAddr, caller)
} }
func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) { func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
env := self.vm.Env() env := self.env
chainlogger.Debugf("pre state %x\n", env.State().Root()) evm := vm.New(env, vm.DebugVmTy)
if env.Depth() == vm.MaxCallDepth {
// Consume all gas (by not returning it) and return a depth error
return nil, vm.DepthError{}
}
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address) from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
// Skipping transfer is used on testing for the initial call // Skipping transfer is used on testing for the initial call
@ -49,32 +53,19 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret
} }
snapshot := env.State().Copy() snapshot := env.State().Copy()
defer func() { start := time.Now()
if vm.IsDepthErr(err) || vm.IsOOGErr(err) { ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
if err != nil {
env.State().Set(snapshot) env.State().Set(snapshot)
} }
chainlogger.Debugf("post state %x\n", env.State().Root()) chainlogger.Debugf("vm took %v\n", time.Since(start))
}()
self.object = to
// Pre-compiled contracts (address.go) 1, 2 & 3.
naddr := ethutil.BigD(contextAddr).Uint64()
if p := vm.Precompiled[naddr]; p != nil {
if self.Gas.Cmp(p.Gas(len(self.input))) >= 0 {
ret = p.Call(self.input)
self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret)
self.vm.Endl()
}
} else {
ret, err = self.vm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
}
return return
} }
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) { func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
ret, err = self.exec(self.input, nil, caller) ret, err = self.exec(self.input, nil, caller)
account = self.vm.Env().State().GetStateObject(self.address) account = self.env.State().GetStateObject(self.address)
return return
} }

View File

@ -76,13 +76,14 @@ func (self *Filter) SetSkip(skip int) {
// Run filters messages with the current parameters set // Run filters messages with the current parameters set
func (self *Filter) Find() []*state.Message { func (self *Filter) Find() []*state.Message {
earliestBlock := self.eth.ChainManager().CurrentBlock()
var earliestBlockNo uint64 = uint64(self.earliest) var earliestBlockNo uint64 = uint64(self.earliest)
if self.earliest == -1 { if self.earliest == -1 {
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64() earliestBlockNo = earliestBlock.NumberU64()
} }
var latestBlockNo uint64 = uint64(self.latest) var latestBlockNo uint64 = uint64(self.latest)
if self.latest == -1 { if self.latest == -1 {
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64() latestBlockNo = earliestBlock.NumberU64()
} }
var ( var (
@ -93,7 +94,7 @@ func (self *Filter) Find() []*state.Message {
for i := 0; !quit && block != nil; i++ { for i := 0; !quit && block != nil; i++ {
// Quit on latest // Quit on latest
switch { switch {
case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0: case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
quit = true quit = true
case self.max <= len(messages): case self.max <= len(messages):
break break
@ -113,7 +114,7 @@ func (self *Filter) Find() []*state.Message {
messages = append(messages, self.FilterMessages(msgs)...) messages = append(messages, self.FilterMessages(msgs)...)
} }
block = self.eth.ChainManager().GetBlock(block.PrevHash) block = self.eth.ChainManager().GetBlock(block.ParentHash())
} }
skip := int(math.Min(float64(len(messages)), float64(self.skip))) skip := int(math.Min(float64(len(messages)), float64(self.skip)))
@ -176,7 +177,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
var fromIncluded, toIncluded bool var fromIncluded, toIncluded bool
if len(self.from) > 0 { if len(self.from) > 0 {
for _, from := range self.from { for _, from := range self.from {
if types.BloomLookup(block.LogsBloom, from) || bytes.Equal(block.Coinbase, from) { if types.BloomLookup(block.Bloom(), from) || bytes.Equal(block.Coinbase(), from) {
fromIncluded = true fromIncluded = true
break break
} }
@ -187,7 +188,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
if len(self.to) > 0 { if len(self.to) > 0 {
for _, to := range self.to { for _, to := range self.to {
if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase, to) { if types.BloomLookup(block.Bloom(), ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase(), to) {
toIncluded = true toIncluded = true
break break
} }

View File

@ -1,7 +1 @@
package core package core
// import "testing"
// func TestFilter(t *testing.T) {
// NewFilter(NewTestManager())
// }

View File

@ -3,8 +3,10 @@ package core
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
) )
/* /*
@ -17,36 +19,35 @@ var ZeroHash512 = make([]byte, 64)
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{})) var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
var EmptyListRoot = crypto.Sha3(ethutil.Encode("")) var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
var GenesisHeader = []interface{}{ func GenesisBlock() *types.Block {
// Previous hash (none) genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
ZeroHash256, genesis.Header().Number = ethutil.Big0
// Empty uncles genesis.Header().GasLimit = big.NewInt(1000000)
EmptyShaList, genesis.Header().GasUsed = ethutil.Big0
// Coinbase genesis.Header().Time = 0
ZeroHash160,
// Root state
EmptyShaList,
// tx root
EmptyListRoot,
// receipt root
EmptyListRoot,
// bloom
ZeroHash512,
// Difficulty
//ethutil.BigPow(2, 22),
big.NewInt(131072),
// Number
ethutil.Big0,
// Block upper gas bound
big.NewInt(1000000),
// Block gas used
ethutil.Big0,
// Time
ethutil.Big0,
// Extra
nil,
// Nonce
crypto.Sha3(big.NewInt(42).Bytes()),
}
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}} genesis.SetUncles([]*types.Header{})
genesis.SetTransactions(types.Transactions{})
genesis.SetReceipts(types.Receipts{})
statedb := state.New(genesis.Trie())
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := statedb.GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
statedb.UpdateStateObject(account)
}
statedb.Sync()
genesis.Header().Root = statedb.Root()
return genesis
}

View File

@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/wire" "github.com/ethereum/go-ethereum/p2p"
) )
// Implement our EthTest Manager // Implement our EthTest Manager
@ -54,11 +54,11 @@ func (tm *TestManager) TxPool() *TxPool {
func (tm *TestManager) EventMux() *event.TypeMux { func (tm *TestManager) EventMux() *event.TypeMux {
return tm.eventMux return tm.eventMux
} }
func (tm *TestManager) Broadcast(msgType wire.MsgType, data []interface{}) { func (tm *TestManager) Broadcast(msgType p2p.Msg, data []interface{}) {
fmt.Println("Broadcast not implemented") fmt.Println("Broadcast not implemented")
} }
func (tm *TestManager) ClientIdentity() wire.ClientIdentity { func (tm *TestManager) ClientIdentity() p2p.ClientIdentity {
return nil return nil
} }
func (tm *TestManager) KeyManager() *crypto.KeyManager { func (tm *TestManager) KeyManager() *crypto.KeyManager {

View File

@ -5,6 +5,8 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm" "github.com/ethereum/go-ethereum/vm"
) )
@ -27,48 +29,69 @@ import (
*/ */
type StateTransition struct { type StateTransition struct {
coinbase, receiver []byte coinbase, receiver []byte
tx *types.Transaction msg Message
gas, gasPrice *big.Int gas, gasPrice *big.Int
initialGas *big.Int
value *big.Int value *big.Int
data []byte data []byte
state *state.StateDB state *state.StateDB
block *types.Block block *types.Block
cb, rec, sen *state.StateObject cb, rec, sen *state.StateObject
Env vm.Environment
} }
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.StateDB, block *types.Block) *StateTransition { type Message interface {
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil} Hash() []byte
From() []byte
To() []byte
GasPrice() *big.Int
Gas() *big.Int
Value() *big.Int
Nonce() uint64
Data() []byte
}
func AddressFromMessage(msg Message) []byte {
// Generate a new address
return crypto.Sha3(ethutil.NewValue([]interface{}{msg.From(), msg.Nonce()}).Encode())[12:]
}
func MessageCreatesContract(msg Message) bool {
return len(msg.To()) == 0
}
func MessageGasValue(msg Message) *big.Int {
return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
}
func NewStateTransition(coinbase *state.StateObject, msg Message, state *state.StateDB, block *types.Block) *StateTransition {
return &StateTransition{coinbase.Address(), msg.To(), msg, new(big.Int), new(big.Int).Set(msg.GasPrice()), new(big.Int), msg.Value(), msg.Data(), state, block, coinbase, nil, nil, nil}
}
func (self *StateTransition) VmEnv() vm.Environment {
if self.Env == nil {
self.Env = NewEnv(self.state, self.msg, self.block)
}
return self.Env
} }
func (self *StateTransition) Coinbase() *state.StateObject { func (self *StateTransition) Coinbase() *state.StateObject {
if self.cb != nil { return self.state.GetOrNewStateObject(self.coinbase)
return self.cb
}
self.cb = self.state.GetOrNewStateObject(self.coinbase)
return self.cb
} }
func (self *StateTransition) Sender() *state.StateObject { func (self *StateTransition) From() *state.StateObject {
if self.sen != nil { return self.state.GetOrNewStateObject(self.msg.From())
return self.sen
}
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
return self.sen
} }
func (self *StateTransition) Receiver() *state.StateObject { func (self *StateTransition) To() *state.StateObject {
if self.tx != nil && self.tx.CreatesContract() { if self.msg != nil && MessageCreatesContract(self.msg) {
return nil return nil
} }
return self.state.GetOrNewStateObject(self.msg.To())
if self.rec != nil {
return self.rec
}
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
return self.rec
} }
func (self *StateTransition) UseGas(amount *big.Int) error { func (self *StateTransition) UseGas(amount *big.Int) error {
@ -87,41 +110,33 @@ func (self *StateTransition) AddGas(amount *big.Int) {
func (self *StateTransition) BuyGas() error { func (self *StateTransition) BuyGas() error {
var err error var err error
sender := self.Sender() sender := self.From()
if sender.Balance().Cmp(self.tx.GasValue()) < 0 { if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance()) return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address()[:4], MessageGasValue(self.msg), sender.Balance())
} }
coinbase := self.Coinbase() coinbase := self.Coinbase()
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice) err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice())
if err != nil { if err != nil {
return err return err
} }
self.AddGas(self.tx.Gas) self.AddGas(self.msg.Gas())
sender.SubAmount(self.tx.GasValue()) self.initialGas.Set(self.msg.Gas())
sender.SubAmount(MessageGasValue(self.msg))
return nil return nil
} }
func (self *StateTransition) RefundGas() {
coinbase, sender := self.Coinbase(), self.Sender()
coinbase.RefundGas(self.gas, self.tx.GasPrice)
// Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
sender.AddAmount(remaining)
}
func (self *StateTransition) preCheck() (err error) { func (self *StateTransition) preCheck() (err error) {
var ( var (
tx = self.tx msg = self.msg
sender = self.Sender() sender = self.From()
) )
// Make sure this transaction's nonce is correct // Make sure this transaction's nonce is correct
if sender.Nonce != tx.Nonce { if sender.Nonce != msg.Nonce() {
return NonceError(tx.Nonce, sender.Nonce) return NonceError(msg.Nonce(), sender.Nonce)
} }
// Pre-pay gas / Buy gas of the coinbase account // Pre-pay gas / Buy gas of the coinbase account
@ -132,8 +147,8 @@ func (self *StateTransition) preCheck() (err error) {
return nil return nil
} }
func (self *StateTransition) TransitionState() (err error) { func (self *StateTransition) TransitionState() (ret []byte, err error) {
statelogger.Debugf("(~) %x\n", self.tx.Hash()) statelogger.Debugf("(~) %x\n", self.msg.Hash())
// XXX Transactions after this point are considered valid. // XXX Transactions after this point are considered valid.
if err = self.preCheck(); err != nil { if err = self.preCheck(); err != nil {
@ -141,8 +156,8 @@ func (self *StateTransition) TransitionState() (err error) {
} }
var ( var (
tx = self.tx msg = self.msg
sender = self.Sender() sender = self.From()
) )
defer self.RefundGas() defer self.RefundGas()
@ -168,30 +183,56 @@ func (self *StateTransition) TransitionState() (err error) {
return return
} }
var ret []byte vmenv := self.VmEnv()
vmenv := NewEnv(self.state, self.tx, self.block)
var ref vm.ClosureRef var ref vm.ClosureRef
if tx.CreatesContract() { if MessageCreatesContract(msg) {
self.rec = MakeContract(tx, self.state) contract := MakeContract(msg, self.state)
ret, err, ref = vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
ret, err, ref = vmenv.Create(sender, self.rec.Address(), self.tx.Data, self.gas, self.gasPrice, self.value) if err == nil {
dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, vm.GasCreateByte)
if err = self.UseGas(dataGas); err == nil {
//self.state.SetCode(ref.Address(), ret)
ref.SetCode(ret) ref.SetCode(ret)
} else {
ret, err = vmenv.Call(self.Sender(), self.Receiver().Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
} }
}
} else {
ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
}
if err != nil { if err != nil {
statelogger.Debugln(err) self.UseGas(self.gas)
} }
return return
} }
// Converts an transaction in to a state object // Converts an transaction in to a state object
func MakeContract(tx *types.Transaction, state *state.StateDB) *state.StateObject { func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
addr := tx.CreationAddress(state) addr := AddressFromMessage(msg)
contract := state.GetOrNewStateObject(addr) contract := state.GetOrNewStateObject(addr)
contract.InitCode = tx.Data contract.InitCode = msg.Data()
return contract return contract
} }
func (self *StateTransition) RefundGas() {
coinbase, sender := self.Coinbase(), self.From()
// Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
sender.AddAmount(remaining)
uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2)
for addr, ref := range self.state.Refunds() {
refund := ethutil.BigMin(uhalf, ref)
self.gas.Add(self.gas, refund)
self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice()))
}
coinbase.RefundGas(self.gas, self.msg.GasPrice())
}
func (self *StateTransition) GasUsed() *big.Int {
return new(big.Int).Sub(self.initialGas, self.gas)
}

View File

@ -8,9 +8,9 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/wire"
) )
var txplogger = logger.NewLogger("TXP") var txplogger = logger.NewLogger("TXP")
@ -18,7 +18,9 @@ var txplogger = logger.NewLogger("TXP")
const txPoolQueueSize = 50 const txPoolQueueSize = 50
type TxPoolHook chan *types.Transaction type TxPoolHook chan *types.Transaction
type TxMsgTy byte type TxMsg struct {
Tx *types.Transaction
}
const ( const (
minGasPrice = 1000000 minGasPrice = 1000000
@ -26,11 +28,6 @@ const (
var MinGasPrice = big.NewInt(10000000000000) var MinGasPrice = big.NewInt(10000000000000)
type TxMsg struct {
Tx *types.Transaction
Type TxMsgTy
}
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) { func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
for e := pool.Front(); e != nil; e = e.Next() { for e := pool.Front(); e != nil; e = e.Next() {
if it(e.Value.(*types.Transaction), e) { if it(e.Value.(*types.Transaction), e) {
@ -61,7 +58,6 @@ type TxProcessor interface {
// pool is being drained or synced for whatever reason the transactions // pool is being drained or synced for whatever reason the transactions
// will simple queue up and handled when the mutex is freed. // will simple queue up and handled when the mutex is freed.
type TxPool struct { type TxPool struct {
Ethereum EthManager
// The mutex for accessing the Tx pool. // The mutex for accessing the Tx pool.
mutex sync.Mutex mutex sync.Mutex
// Queueing channel for reading and writing incoming // Queueing channel for reading and writing incoming
@ -75,14 +71,18 @@ type TxPool struct {
SecondaryProcessor TxProcessor SecondaryProcessor TxProcessor
subscribers []chan TxMsg subscribers []chan TxMsg
chainManager *ChainManager
eventMux *event.TypeMux
} }
func NewTxPool(ethereum EthManager) *TxPool { func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool {
return &TxPool{ return &TxPool{
pool: list.New(), pool: list.New(),
queueChan: make(chan *types.Transaction, txPoolQueueSize), queueChan: make(chan *types.Transaction, txPoolQueueSize),
quit: make(chan bool), quit: make(chan bool),
Ethereum: ethereum, chainManager: chainManager,
eventMux: eventMux,
} }
} }
@ -94,20 +94,20 @@ func (pool *TxPool) addTransaction(tx *types.Transaction) {
pool.pool.PushBack(tx) pool.pool.PushBack(tx)
// Broadcast the transaction to the rest of the peers // Broadcast the transaction to the rest of the peers
pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()}) pool.eventMux.Post(TxPreEvent{tx})
} }
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
// Get the last block so we can retrieve the sender and receiver from // Get the last block so we can retrieve the sender and receiver from
// the merkle trie // the merkle trie
block := pool.Ethereum.ChainManager().CurrentBlock block := pool.chainManager.CurrentBlock
// Something has gone horribly wrong if this happens // Something has gone horribly wrong if this happens
if block == nil { if block == nil {
return fmt.Errorf("No last block on the block chain") return fmt.Errorf("No last block on the block chain")
} }
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 { if len(tx.To()) != 0 && len(tx.To()) != 20 {
return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient)) return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
} }
v, _, _ := tx.Curve() v, _, _ := tx.Curve()
@ -116,19 +116,17 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
} }
// Get the sender // Get the sender
sender := pool.Ethereum.ChainManager().State().GetAccount(tx.Sender()) senderAddr := tx.From()
if senderAddr == nil {
return fmt.Errorf("invalid sender")
}
sender := pool.chainManager.State().GetAccount(senderAddr)
totAmount := new(big.Int).Set(tx.Value) totAmount := new(big.Int).Set(tx.Value())
// Make sure there's enough in the sender's account. Having insufficient // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
if sender.Balance().Cmp(totAmount) < 0 { if sender.Balance().Cmp(totAmount) < 0 {
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender()) return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
}
if tx.IsContract() {
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
return fmt.Errorf("Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
}
} }
// Increment the nonce making each tx valid only once to prevent replay // Increment the nonce making each tx valid only once to prevent replay
@ -154,13 +152,10 @@ func (self *TxPool) Add(tx *types.Transaction) error {
self.addTransaction(tx) self.addTransaction(tx)
tmp := make([]byte, 4) txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.From()[:4], tx.To()[:4], tx.Value, tx.Hash())
copy(tmp, tx.Recipient)
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
// Notify the subscribers // Notify the subscribers
go self.Ethereum.EventMux().Post(TxPreEvent{tx}) go self.eventMux.Post(TxPreEvent{tx})
return nil return nil
} }
@ -169,7 +164,17 @@ func (self *TxPool) Size() int {
return self.pool.Len() return self.pool.Len()
} }
func (pool *TxPool) CurrentTransactions() []*types.Transaction { func (self *TxPool) AddTransactions(txs []*types.Transaction) {
for _, tx := range txs {
if err := self.Add(tx); err != nil {
txplogger.Infoln(err)
} else {
txplogger.Infof("tx %x\n", tx.Hash()[0:4])
}
}
}
func (pool *TxPool) GetTransactions() []*types.Transaction {
pool.mutex.Lock() pool.mutex.Lock()
defer pool.mutex.Unlock() defer pool.mutex.Unlock()
@ -192,9 +197,9 @@ func (pool *TxPool) RemoveInvalid(state *state.StateDB) {
for e := pool.pool.Front(); e != nil; e = e.Next() { for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*types.Transaction) tx := e.Value.(*types.Transaction)
sender := state.GetAccount(tx.Sender()) sender := state.GetAccount(tx.From())
err := pool.ValidateTransaction(tx) err := pool.ValidateTransaction(tx)
if err != nil || sender.Nonce >= tx.Nonce { if err != nil || sender.Nonce >= tx.Nonce() {
pool.pool.Remove(e) pool.pool.Remove(e)
} }
} }
@ -216,7 +221,7 @@ func (self *TxPool) RemoveSet(txs types.Transactions) {
} }
func (pool *TxPool) Flush() []*types.Transaction { func (pool *TxPool) Flush() []*types.Transaction {
txList := pool.CurrentTransactions() txList := pool.GetTransactions()
// Recreate a new list all together // Recreate a new list all together
// XXX Is this the fastest way? // XXX Is this the fastest way?

View File

@ -9,41 +9,240 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/ptrie"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/trie"
) )
type BlockInfo struct { type Header struct {
Number uint64 // Hash to the previous block
Hash []byte ParentHash ethutil.Bytes
Parent []byte // Uncles of this block
TD *big.Int UncleHash []byte
// The coin base address
Coinbase []byte
// Block Trie state
Root []byte
// Tx sha
TxHash []byte
// Receipt sha
ReceiptHash []byte
// Bloom
Bloom []byte
// Difficulty for the current block
Difficulty *big.Int
// The block number
Number *big.Int
// Gas limit
GasLimit *big.Int
// Gas used
GasUsed *big.Int
// Creation time
Time uint64
// Extra data
Extra string
// Block Nonce for verification
Nonce ethutil.Bytes
} }
func (bi *BlockInfo) RlpDecode(data []byte) { func (self *Header) rlpData(withNonce bool) []interface{} {
decoder := ethutil.NewValueFromBytes(data) fields := []interface{}{self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra}
if withNonce {
fields = append(fields, self.Nonce)
}
bi.Number = decoder.Get(0).Uint() return fields
bi.Hash = decoder.Get(1).Bytes()
bi.Parent = decoder.Get(2).Bytes()
bi.TD = decoder.Get(3).BigInt()
} }
func (bi *BlockInfo) RlpEncode() []byte { func (self *Header) RlpData() interface{} {
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD}) return self.rlpData(true)
}
func (self *Header) Hash() []byte {
return crypto.Sha3(ethutil.Encode(self.rlpData(true)))
}
func (self *Header) HashNoNonce() []byte {
return crypto.Sha3(ethutil.Encode(self.rlpData(false)))
}
type Block struct {
header *Header
uncles []*Header
transactions Transactions
Td *big.Int
receipts Receipts
Reward *big.Int
}
func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, extra string) *Block {
header := &Header{
Root: root,
ParentHash: parentHash,
Coinbase: coinbase,
Difficulty: difficulty,
Nonce: nonce,
Time: uint64(time.Now().Unix()),
Extra: extra,
GasUsed: new(big.Int),
GasLimit: new(big.Int),
}
block := &Block{header: header, Reward: new(big.Int)}
return block
}
func NewBlockWithHeader(header *Header) *Block {
return &Block{header: header}
}
func (self *Block) DecodeRLP(s *rlp.Stream) error {
if _, err := s.List(); err != nil {
return err
}
var header Header
if err := s.Decode(&header); err != nil {
return err
}
var transactions []*Transaction
if err := s.Decode(&transactions); err != nil {
return err
}
var uncleHeaders []*Header
if err := s.Decode(&uncleHeaders); err != nil {
return err
}
var tdBytes []byte
if err := s.Decode(&tdBytes); err != nil {
// If this block comes from the network that's fine. If loaded from disk it should be there
// Blocks don't store their Td when propagated over the network
} else {
self.Td = ethutil.BigD(tdBytes)
}
if err := s.ListEnd(); err != nil {
return err
}
self.header = &header
self.uncles = uncleHeaders
self.transactions = transactions
return nil
}
func (self *Block) Header() *Header {
return self.header
}
func (self *Block) Uncles() []*Header {
return self.uncles
}
func (self *Block) SetUncles(uncleHeaders []*Header) {
self.uncles = uncleHeaders
self.header.UncleHash = crypto.Sha3(ethutil.Encode(uncleHeaders))
}
func (self *Block) Transactions() Transactions {
return self.transactions
}
func (self *Block) Transaction(hash []byte) *Transaction {
for _, transaction := range self.transactions {
if bytes.Equal(hash, transaction.Hash()) {
return transaction
}
}
return nil
}
func (self *Block) SetTransactions(transactions Transactions) {
self.transactions = transactions
self.header.TxHash = DeriveSha(transactions)
}
func (self *Block) Receipts() Receipts {
return self.receipts
}
func (self *Block) SetReceipts(receipts Receipts) {
self.receipts = receipts
self.header.ReceiptHash = DeriveSha(receipts)
self.header.Bloom = CreateBloom(receipts)
}
func (self *Block) RlpData() interface{} {
return []interface{}{self.header, self.transactions, self.uncles}
}
func (self *Block) RlpDataForStorage() interface{} {
return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
}
// Header accessors (add as you need them)
func (self *Block) Number() *big.Int { return self.header.Number }
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
func (self *Block) ParentHash() []byte { return self.header.ParentHash }
func (self *Block) Bloom() []byte { return self.header.Bloom }
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
func (self *Block) Time() int64 { return int64(self.header.Time) }
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
func (self *Block) Hash() []byte { return self.header.Hash() }
func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
func (self *Block) SetRoot(root []byte) { self.header.Root = root }
// Implement block.Pow
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
func (self *Block) N() []byte { return self.header.Nonce }
func (self *Block) HashNoNonce() []byte {
return crypto.Sha3(ethutil.Encode(self.header.rlpData(false)))
}
func (self *Block) String() string {
return fmt.Sprintf(`BLOCK(%x): Size: %v {
Header:
[
%v
]
Transactions:
%v
Uncles:
%v
}
`, self.header.Hash(), self.Size(), self.header, self.transactions, self.uncles)
}
func (self *Header) String() string {
return fmt.Sprintf(`
ParentHash: %x
UncleHash: %x
Coinbase: %x
Root: %x
TxSha %x
ReceiptSha: %x
Bloom: %x
Difficulty: %v
Number: %v
GasLimit: %v
GasUsed: %v
Time: %v
Extra: %v
Nonce: %x
`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce)
} }
type Blocks []*Block type Blocks []*Block
func (self Blocks) AsSet() ethutil.UniqueSet {
set := make(ethutil.UniqueSet)
for _, block := range self {
set.Insert(block.Hash())
}
return set
}
type BlockBy func(b1, b2 *Block) bool type BlockBy func(b1, b2 *Block) bool
func (self BlockBy) Sort(blocks Blocks) { func (self BlockBy) Sort(blocks Blocks) {
@ -65,352 +264,4 @@ func (self blockSorter) Swap(i, j int) {
} }
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 } func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 }
type Block struct {
// Hash to the previous block
PrevHash ethutil.Bytes
// Uncles of this block
Uncles Blocks
UncleSha []byte
// The coin base address
Coinbase []byte
// Block Trie state
//state *ethutil.Trie
state *state.StateDB
// Difficulty for the current block
Difficulty *big.Int
// Creation time
Time int64
// The block number
Number *big.Int
// Gas limit
GasLimit *big.Int
// Gas used
GasUsed *big.Int
// Extra data
Extra string
// Block Nonce for verification
Nonce ethutil.Bytes
// List of transactions and/or contracts
transactions Transactions
receipts Receipts
TxSha, ReceiptSha []byte
LogsBloom []byte
Reward *big.Int
}
func NewBlockFromBytes(raw []byte) *Block {
block := &Block{}
block.RlpDecode(raw)
return block
}
// New block takes a raw encoded string
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
block := &Block{}
block.RlpValueDecode(rlpValue)
return block
}
func CreateBlock(root interface{},
prevHash []byte,
base []byte,
Difficulty *big.Int,
Nonce []byte,
extra string) *Block {
block := &Block{
PrevHash: prevHash,
Coinbase: base,
Difficulty: Difficulty,
Nonce: Nonce,
Time: time.Now().Unix(),
Extra: extra,
UncleSha: nil,
GasUsed: new(big.Int),
GasLimit: new(big.Int),
}
block.SetUncles([]*Block{})
block.state = state.New(trie.New(ethutil.Config.Db, root))
return block
}
// Returns a hash of the block
func (block *Block) Hash() ethutil.Bytes {
return crypto.Sha3(ethutil.NewValue(block.header()).Encode())
//return crypto.Sha3(block.Value().Encode())
}
func (block *Block) HashNoNonce() []byte {
return crypto.Sha3(ethutil.Encode(block.miningHeader()))
}
func (block *Block) State() *state.StateDB {
return block.state
}
func (block *Block) Transactions() Transactions {
return block.transactions
}
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
if block.Number.Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6)
}
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit)
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5))
curInt := new(big.Int).Div(current.Num(), current.Denom())
result := new(big.Int).Add(previous, curInt)
result.Div(result, big.NewInt(1024))
min := big.NewInt(125000)
return ethutil.BigMax(min, result)
}
func (block *Block) BlockInfo() BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (self *Block) GetTransaction(hash []byte) *Transaction {
for _, tx := range self.transactions {
if bytes.Compare(tx.Hash(), hash) == 0 {
return tx
}
}
return nil
}
// Sync the block's state and contract respectively
func (block *Block) Sync() {
block.state.Sync()
}
func (block *Block) Undo() {
// Sync the block state itself
block.state.Reset()
}
/////// Block Encoding
func (block *Block) rlpReceipts() interface{} {
// Marshal the transactions of this block
encR := make([]interface{}, len(block.receipts))
for i, r := range block.receipts {
// Cast it to a string (safe)
encR[i] = r.RlpData()
}
return encR
}
func (block *Block) rlpUncles() interface{} {
// Marshal the transactions of this block
uncles := make([]interface{}, len(block.Uncles))
for i, uncle := range block.Uncles {
// Cast it to a string (safe)
uncles[i] = uncle.header()
}
return uncles
}
func (block *Block) SetUncles(uncles []*Block) {
block.Uncles = uncles
block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles()))
}
func (self *Block) SetReceipts(receipts Receipts) {
self.receipts = receipts
self.ReceiptSha = DeriveSha(receipts)
self.LogsBloom = CreateBloom(receipts)
}
func (self *Block) SetTransactions(txs Transactions) {
self.transactions = txs
self.TxSha = DeriveSha(txs)
}
func (block *Block) Value() *ethutil.Value {
return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()})
}
func (block *Block) RlpEncode() []byte {
// Encode a slice interface which contains the header and the list of
// transactions.
return block.Value().Encode()
}
func (block *Block) RlpDecode(data []byte) {
rlpValue := ethutil.NewValueFromBytes(data)
block.RlpValueDecode(rlpValue)
}
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
block.setHeader(decoder.Get(0))
// Tx list might be empty if this is an uncle. Uncles only have their
// header set.
if decoder.Get(1).IsNil() == false { // Yes explicitness
//receipts := decoder.Get(1)
//block.receipts = make([]*Receipt, receipts.Len())
txs := decoder.Get(1)
block.transactions = make(Transactions, txs.Len())
for i := 0; i < txs.Len(); i++ {
block.transactions[i] = NewTransactionFromValue(txs.Get(i))
//receipt := NewRecieptFromValue(receipts.Get(i))
//block.transactions[i] = receipt.Tx
//block.receipts[i] = receipt
}
}
if decoder.Get(2).IsNil() == false { // Yes explicitness
uncles := decoder.Get(2)
block.Uncles = make([]*Block, uncles.Len())
for i := 0; i < uncles.Len(); i++ {
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
}
}
}
func (self *Block) setHeader(header *ethutil.Value) {
self.PrevHash = header.Get(0).Bytes()
self.UncleSha = header.Get(1).Bytes()
self.Coinbase = header.Get(2).Bytes()
self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val))
self.TxSha = header.Get(4).Bytes()
self.ReceiptSha = header.Get(5).Bytes()
self.LogsBloom = header.Get(6).Bytes()
self.Difficulty = header.Get(7).BigInt()
self.Number = header.Get(8).BigInt()
self.GasLimit = header.Get(9).BigInt()
self.GasUsed = header.Get(10).BigInt()
self.Time = int64(header.Get(11).BigInt().Uint64())
self.Extra = header.Get(12).Str()
self.Nonce = header.Get(13).Bytes()
}
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
block := &Block{}
block.setHeader(header)
return block
}
func (block *Block) Trie() *trie.Trie {
return block.state.Trie
}
func (block *Block) Root() interface{} {
return block.state.Root()
}
func (block *Block) Diff() *big.Int {
return block.Difficulty
}
func (self *Block) Receipts() []*Receipt {
return self.receipts
}
func (block *Block) miningHeader() []interface{} {
return []interface{}{
// Sha of the previous block
block.PrevHash,
// Sha of uncles
block.UncleSha,
// Coinbase address
block.Coinbase,
// root state
block.Root(),
// tx root
block.TxSha,
// Sha of tx
block.ReceiptSha,
// Bloom
block.LogsBloom,
// Current block Difficulty
block.Difficulty,
// The block number
block.Number,
// Block upper gas bound
block.GasLimit,
// Block gas used
block.GasUsed,
// Time the block was found?
block.Time,
// Extra data
block.Extra,
}
}
func (block *Block) header() []interface{} {
return append(block.miningHeader(), block.Nonce)
}
func (block *Block) String() string {
return fmt.Sprintf(`
BLOCK(%x): Size: %v
PrevHash: %x
UncleSha: %x
Coinbase: %x
Root: %x
TxSha %x
ReceiptSha: %x
Bloom: %x
Difficulty: %v
Number: %v
MaxLimit: %v
GasUsed: %v
Time: %v
Extra: %v
Nonce: %x
NumTx: %v
`,
block.Hash(),
block.Size(),
block.PrevHash,
block.UncleSha,
block.Coinbase,
block.Root(),
block.TxSha,
block.ReceiptSha,
block.LogsBloom,
block.Difficulty,
block.Number,
block.GasLimit,
block.GasUsed,
block.Time,
block.Extra,
block.Nonce,
len(block.transactions),
)
}
func (self *Block) Size() ethutil.StorageSize {
return ethutil.StorageSize(len(self.RlpEncode()))
}
// Implement RlpEncodable
func (self *Block) RlpData() interface{} {
return self.Value().Val
}
// Implement pow.Block
func (self *Block) N() []byte { return self.Nonce }

23
core/types/block_test.go Normal file
View File

@ -0,0 +1,23 @@
package types
import (
"bytes"
"testing"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/rlp"
)
func init() {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
}
func TestNewBlock(t *testing.T) {
block := GenesisBlock()
data := ethutil.Encode(block)
var genesis Block
err := rlp.Decode(bytes.NewReader(data), &genesis)
}

View File

@ -2,7 +2,7 @@ package types
import ( import (
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/ptrie"
) )
type DerivableList interface { type DerivableList interface {
@ -11,10 +11,10 @@ type DerivableList interface {
} }
func DeriveSha(list DerivableList) []byte { func DeriveSha(list DerivableList) []byte {
trie := trie.New(ethutil.Config.Db, "") trie := ptrie.New(nil, ethutil.Config.Db)
for i := 0; i < list.Len(); i++ { for i := 0; i < list.Len(); i++ {
trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i))) trie.Update(ethutil.Encode(i), list.GetRlp(i))
} }
return trie.GetRoot() return trie.Root()
} }

View File

@ -1,42 +1,37 @@
package types package types
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/rlp"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
) )
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func IsContractAddr(addr []byte) bool { func IsContractAddr(addr []byte) bool {
return len(addr) == 0 return len(addr) == 0
//return bytes.Compare(addr, ContractAddr) == 0
} }
type Transaction struct { type Transaction struct {
Nonce uint64 AccountNonce uint64
Price *big.Int
GasLimit *big.Int
Recipient []byte Recipient []byte
Value *big.Int Amount *big.Int
Gas *big.Int Payload []byte
GasPrice *big.Int V uint64
Data []byte R, S []byte
v byte
r, s []byte
// Indicates whether this tx is a contract creation transaction
contractCreation bool
} }
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction {
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} return NewTransactionMessage(nil, Amount, gasAmount, price, data)
} }
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction {
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)} return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
} }
func NewTransactionFromBytes(data []byte) *Transaction { func NewTransactionFromBytes(data []byte) *Transaction {
@ -46,46 +41,55 @@ func NewTransactionFromBytes(data []byte) *Transaction {
return tx return tx
} }
func NewTransactionFromValue(val *ethutil.Value) *Transaction { func NewTransactionFromAmount(val *ethutil.Value) *Transaction {
tx := &Transaction{} tx := &Transaction{}
tx.RlpValueDecode(val) tx.RlpValueDecode(val)
return tx return tx
} }
func (self *Transaction) GasValue() *big.Int {
return new(big.Int).Mul(self.Gas, self.GasPrice)
}
func (self *Transaction) TotalValue() *big.Int {
v := self.GasValue()
return v.Add(v, self.Value)
}
func (tx *Transaction) Hash() []byte { func (tx *Transaction) Hash() []byte {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
return crypto.Sha3(ethutil.NewValue(data).Encode()) return crypto.Sha3(ethutil.Encode(data))
} }
func (tx *Transaction) CreatesContract() bool { func (self *Transaction) Data() []byte {
return tx.contractCreation return self.Payload
} }
/* Deprecated */ func (self *Transaction) Gas() *big.Int {
func (tx *Transaction) IsContract() bool { return self.GasLimit
return tx.CreatesContract()
} }
func (tx *Transaction) CreationAddress(state *state.StateDB) []byte { func (self *Transaction) GasPrice() *big.Int {
// Generate a new address return self.Price
return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:] }
func (self *Transaction) Value() *big.Int {
return self.Amount
}
func (self *Transaction) Nonce() uint64 {
return self.AccountNonce
}
func (self *Transaction) SetNonce(AccountNonce uint64) {
self.AccountNonce = AccountNonce
}
func (self *Transaction) From() []byte {
return self.sender()
}
func (self *Transaction) To() []byte {
return self.Recipient
} }
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) { func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
v = tx.v v = byte(tx.V)
r = ethutil.LeftPadBytes(tx.r, 32) r = ethutil.LeftPadBytes(tx.R, 32)
s = ethutil.LeftPadBytes(tx.s, 32) s = ethutil.LeftPadBytes(tx.S, 32)
return return
} }
@ -106,18 +110,18 @@ func (tx *Transaction) PublicKey() []byte {
sig := append(r, s...) sig := append(r, s...)
sig = append(sig, v-27) sig = append(sig, v-27)
pubkey := crypto.Ecrecover(append(hash, sig...)) //pubkey := crypto.Ecrecover(append(hash, sig...))
//pubkey, _ := secp256k1.RecoverPubkey(hash, sig) pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
return pubkey return pubkey
} }
func (tx *Transaction) Sender() []byte { func (tx *Transaction) sender() []byte {
pubkey := tx.PublicKey() pubkey := tx.PublicKey()
// Validate the returned key. // Validate the returned key.
// Return nil if public key isn't in full format // Return nil if public key isn't in full format
if len(pubkey) != 0 && pubkey[0] != 4 { if len(pubkey) == 0 || pubkey[0] != 4 {
return nil return nil
} }
@ -128,48 +132,37 @@ func (tx *Transaction) Sign(privk []byte) error {
sig := tx.Signature(privk) sig := tx.Signature(privk)
tx.r = sig[:32] tx.R = sig[:32]
tx.s = sig[32:64] tx.S = sig[32:64]
tx.v = sig[64] + 27 tx.V = uint64(sig[64] + 27)
return nil return nil
} }
func (tx *Transaction) RlpData() interface{} { func (tx *Transaction) RlpData() interface{} {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
// TODO Remove prefixing zero's return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
}
func (tx *Transaction) RlpValue() *ethutil.Value {
return ethutil.NewValue(tx.RlpData())
} }
func (tx *Transaction) RlpEncode() []byte { func (tx *Transaction) RlpEncode() []byte {
return tx.RlpValue().Encode() return ethutil.Encode(tx)
} }
func (tx *Transaction) RlpDecode(data []byte) { func (tx *Transaction) RlpDecode(data []byte) {
tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) rlp.Decode(bytes.NewReader(data), tx)
} }
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint() tx.AccountNonce = decoder.Get(0).Uint()
tx.GasPrice = decoder.Get(1).BigInt() tx.Price = decoder.Get(1).BigInt()
tx.Gas = decoder.Get(2).BigInt() tx.GasLimit = decoder.Get(2).BigInt()
tx.Recipient = decoder.Get(3).Bytes() tx.Recipient = decoder.Get(3).Bytes()
tx.Value = decoder.Get(4).BigInt() tx.Amount = decoder.Get(4).BigInt()
tx.Data = decoder.Get(5).Bytes() tx.Payload = decoder.Get(5).Bytes()
tx.v = byte(decoder.Get(6).Uint()) tx.V = decoder.Get(6).Uint()
tx.R = decoder.Get(7).Bytes()
tx.r = decoder.Get(7).Bytes() tx.S = decoder.Get(8).Bytes()
tx.s = decoder.Get(8).Bytes()
if IsContractAddr(tx.Recipient) {
tx.contractCreation = true
}
} }
func (tx *Transaction) String() string { func (tx *Transaction) String() string {
@ -180,25 +173,28 @@ func (tx *Transaction) String() string {
To: %x To: %x
Nonce: %v Nonce: %v
GasPrice: %v GasPrice: %v
Gas: %v GasLimit %v
Value: %v Value: %v
Data: 0x%x Data: 0x%x
V: 0x%x V: 0x%x
R: 0x%x R: 0x%x
S: 0x%x S: 0x%x
`, Hex: %x
`,
tx.Hash(), tx.Hash(),
len(tx.Recipient) == 0, len(tx.Recipient) == 0,
tx.Sender(), tx.From(),
tx.Recipient, tx.To(),
tx.Nonce, tx.AccountNonce,
tx.GasPrice, tx.Price,
tx.Gas, tx.GasLimit,
tx.Value, tx.Amount,
tx.Data, tx.Payload,
tx.v, tx.V,
tx.r, tx.R,
tx.s) tx.S,
ethutil.Encode(tx),
)
} }
// Transaction slice type for basic sorting // Transaction slice type for basic sorting
@ -221,5 +217,5 @@ func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) }
type TxByNonce struct{ Transactions } type TxByNonce struct{ Transactions }
func (s TxByNonce) Less(i, j int) bool { func (s TxByNonce) Less(i, j int) bool {
return s.Transactions[i].Nonce < s.Transactions[j].Nonce return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce
} }

View File

@ -11,28 +11,28 @@ import (
type VMEnv struct { type VMEnv struct {
state *state.StateDB state *state.StateDB
block *types.Block block *types.Block
tx *types.Transaction msg Message
depth int depth int
} }
func NewEnv(state *state.StateDB, tx *types.Transaction, block *types.Block) *VMEnv { func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv {
return &VMEnv{ return &VMEnv{
state: state, state: state,
block: block, block: block,
tx: tx, msg: msg,
} }
} }
func (self *VMEnv) Origin() []byte { return self.tx.Sender() } func (self *VMEnv) Origin() []byte { return self.msg.From() }
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
func (self *VMEnv) Time() int64 { return self.block.Time } func (self *VMEnv) Time() int64 { return self.block.Time() }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.tx.Value } func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) State() *state.StateDB { return self.state } 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) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) AddLog(log state.Log) { func (self *VMEnv) AddLog(log state.Log) {
@ -43,9 +43,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
} }
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution { func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution {
evm := vm.New(self, vm.DebugVmTy) return NewExecution(self, addr, data, gas, price, value)
return NewExecution(evm, addr, data, gas, price, value)
} }
func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {

249
eth/backend.go Normal file
View File

@ -0,0 +1,249 @@
package eth
import (
"net"
"sync"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/whisper"
)
const (
seedNodeAddress = "poc-7.ethdev.com:30300"
)
var logger = ethlogger.NewLogger("SERV")
type Ethereum struct {
// Channel for shutting down the ethereum
shutdownChan chan bool
quit chan bool
// DB interface
db ethutil.Database
blacklist p2p.Blacklist
//*** SERVICES ***
// State manager for processing new blocks and managing the over all states
blockManager *core.BlockManager
txPool *core.TxPool
chainManager *core.ChainManager
blockPool *BlockPool
whisper *whisper.Whisper
server *p2p.Server
eventMux *event.TypeMux
txSub event.Subscription
blockSub event.Subscription
RpcServer *rpc.JsonRpcServer
keyManager *crypto.KeyManager
clientIdentity p2p.ClientIdentity
synclock sync.Mutex
syncGroup sync.WaitGroup
Mining bool
}
func New(db ethutil.Database, identity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, port string, maxPeers int) (*Ethereum, error) {
saveProtocolVersion(db)
ethutil.Config.Db = db
eth := &Ethereum{
shutdownChan: make(chan bool),
quit: make(chan bool),
db: db,
keyManager: keyManager,
clientIdentity: identity,
blacklist: p2p.NewBlacklist(),
eventMux: &event.TypeMux{},
}
eth.chainManager = core.NewChainManager(eth.EventMux())
eth.txPool = core.NewTxPool(eth.chainManager, eth.EventMux())
eth.blockManager = core.NewBlockManager(eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockManager)
eth.whisper = whisper.New()
hasBlock := eth.chainManager.HasBlock
insertChain := eth.chainManager.InsertChain
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
// Start services
eth.txPool.Start()
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
server := &p2p.Server{
Identity: identity,
MaxPeers: maxPeers,
Protocols: protocols,
ListenAddr: ":" + port,
Blacklist: eth.blacklist,
NAT: nat,
}
eth.server = server
return eth, nil
}
func (s *Ethereum) KeyManager() *crypto.KeyManager {
return s.keyManager
}
func (s *Ethereum) ClientIdentity() p2p.ClientIdentity {
return s.clientIdentity
}
func (s *Ethereum) ChainManager() *core.ChainManager {
return s.chainManager
}
func (s *Ethereum) BlockManager() *core.BlockManager {
return s.blockManager
}
func (s *Ethereum) TxPool() *core.TxPool {
return s.txPool
}
func (s *Ethereum) BlockPool() *BlockPool {
return s.blockPool
}
func (s *Ethereum) Whisper() *whisper.Whisper {
return s.whisper
}
func (s *Ethereum) EventMux() *event.TypeMux {
return s.eventMux
}
func (self *Ethereum) Db() ethutil.Database {
return self.db
}
func (s *Ethereum) IsMining() bool {
return s.Mining
}
func (s *Ethereum) IsListening() bool {
// XXX TODO
return false
}
func (s *Ethereum) PeerCount() int {
return s.server.PeerCount()
}
func (s *Ethereum) Peers() []*p2p.Peer {
return s.server.Peers()
}
func (s *Ethereum) MaxPeers() int {
return s.server.MaxPeers
}
// Start the ethereum
func (s *Ethereum) Start(seed bool) error {
err := s.server.Start()
if err != nil {
return err
}
s.blockPool.Start()
s.whisper.Start()
// broadcast transactions
s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
go s.txBroadcastLoop()
// broadcast mined blocks
s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{})
go s.blockBroadcastLoop()
// TODO: read peers here
if seed {
logger.Infof("Connect to seed node %v", seedNodeAddress)
if err := s.SuggestPeer(seedNodeAddress); err != nil {
return err
}
}
logger.Infoln("Server started")
return nil
}
func (self *Ethereum) SuggestPeer(addr string) error {
netaddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
logger.Errorf("couldn't resolve %s:", addr, err)
return err
}
self.server.SuggestPeer(netaddr.IP, netaddr.Port, nil)
return nil
}
func (s *Ethereum) Stop() {
// Close the database
defer s.db.Close()
close(s.quit)
s.txSub.Unsubscribe() // quits txBroadcastLoop
s.blockSub.Unsubscribe() // quits blockBroadcastLoop
if s.RpcServer != nil {
s.RpcServer.Stop()
}
s.txPool.Stop()
s.eventMux.Stop()
s.blockPool.Stop()
s.whisper.Stop()
logger.Infoln("Server stopped")
close(s.shutdownChan)
}
// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
<-s.shutdownChan
}
// now tx broadcasting is taken out of txPool
// handled here via subscription, efficiency?
func (self *Ethereum) txBroadcastLoop() {
// automatically stops if unsubscribe
for obj := range self.txSub.Chan() {
event := obj.(core.TxPreEvent)
self.server.Broadcast("eth", TxMsg, []interface{}{event.Tx.RlpData()})
}
}
func (self *Ethereum) blockBroadcastLoop() {
// automatically stops if unsubscribe
for obj := range self.txSub.Chan() {
event := obj.(core.NewMinedBlockEvent)
self.server.Broadcast("eth", NewBlockMsg, event.Block.RlpData())
}
}
func saveProtocolVersion(db ethutil.Database) {
d, _ := db.Get([]byte("ProtocolVersion"))
protocolVersion := ethutil.NewValue(d).Uint()
if protocolVersion == 0 {
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
}
}

1015
eth/block_pool.go Normal file

File diff suppressed because it is too large Load Diff

198
eth/block_pool_test.go Normal file
View File

@ -0,0 +1,198 @@
package eth
import (
"bytes"
"fmt"
"log"
"os"
"sync"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
ethlogger "github.com/ethereum/go-ethereum/logger"
)
var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
type testChainManager struct {
knownBlock func(hash []byte) bool
addBlock func(*types.Block) error
checkPoW func(*types.Block) bool
}
func (self *testChainManager) KnownBlock(hash []byte) bool {
if self.knownBlock != nil {
return self.knownBlock(hash)
}
return false
}
func (self *testChainManager) AddBlock(block *types.Block) error {
if self.addBlock != nil {
return self.addBlock(block)
}
return nil
}
func (self *testChainManager) CheckPoW(block *types.Block) bool {
if self.checkPoW != nil {
return self.checkPoW(block)
}
return false
}
func knownBlock(hashes ...[]byte) (f func([]byte) bool) {
f = func(block []byte) bool {
for _, hash := range hashes {
if bytes.Compare(block, hash) == 0 {
return true
}
}
return false
}
return
}
func addBlock(hashes ...[]byte) (f func(*types.Block) error) {
f = func(block *types.Block) error {
for _, hash := range hashes {
if bytes.Compare(block.Hash(), hash) == 0 {
return fmt.Errorf("invalid by test")
}
}
return nil
}
return
}
func checkPoW(hashes ...[]byte) (f func(*types.Block) bool) {
f = func(block *types.Block) bool {
for _, hash := range hashes {
if bytes.Compare(block.Hash(), hash) == 0 {
return false
}
}
return true
}
return
}
func newTestChainManager(knownBlocks [][]byte, invalidBlocks [][]byte, invalidPoW [][]byte) *testChainManager {
return &testChainManager{
knownBlock: knownBlock(knownBlocks...),
addBlock: addBlock(invalidBlocks...),
checkPoW: checkPoW(invalidPoW...),
}
}
type intToHash map[int][]byte
type hashToInt map[string]int
type testHashPool struct {
intToHash
hashToInt
}
func newHash(i int) []byte {
return crypto.Sha3([]byte(string(i)))
}
func newTestBlockPool(knownBlockIndexes []int, invalidBlockIndexes []int, invalidPoWIndexes []int) (hashPool *testHashPool, blockPool *BlockPool) {
hashPool = &testHashPool{make(intToHash), make(hashToInt)}
knownBlocks := hashPool.indexesToHashes(knownBlockIndexes)
invalidBlocks := hashPool.indexesToHashes(invalidBlockIndexes)
invalidPoW := hashPool.indexesToHashes(invalidPoWIndexes)
blockPool = NewBlockPool(newTestChainManager(knownBlocks, invalidBlocks, invalidPoW))
return
}
func (self *testHashPool) indexesToHashes(indexes []int) (hashes [][]byte) {
for _, i := range indexes {
hash, found := self.intToHash[i]
if !found {
hash = newHash(i)
self.intToHash[i] = hash
self.hashToInt[string(hash)] = i
}
hashes = append(hashes, hash)
}
return
}
func (self *testHashPool) hashesToIndexes(hashes [][]byte) (indexes []int) {
for _, hash := range hashes {
i, found := self.hashToInt[string(hash)]
if !found {
i = -1
}
indexes = append(indexes, i)
}
return
}
type protocolChecker struct {
blockHashesRequests []int
blocksRequests [][]int
invalidBlocks []error
hashPool *testHashPool
lock sync.Mutex
}
// -1 is special: not found (a hash never seen)
func (self *protocolChecker) requestBlockHashesCallBack() (requestBlockHashesCallBack func([]byte) error) {
requestBlockHashesCallBack = func(hash []byte) error {
indexes := self.hashPool.hashesToIndexes([][]byte{hash})
self.lock.Lock()
defer self.lock.Unlock()
self.blockHashesRequests = append(self.blockHashesRequests, indexes[0])
return nil
}
return
}
func (self *protocolChecker) requestBlocksCallBack() (requestBlocksCallBack func([][]byte) error) {
requestBlocksCallBack = func(hashes [][]byte) error {
indexes := self.hashPool.hashesToIndexes(hashes)
self.lock.Lock()
defer self.lock.Unlock()
self.blocksRequests = append(self.blocksRequests, indexes)
return nil
}
return
}
func (self *protocolChecker) invalidBlockCallBack() (invalidBlockCallBack func(error)) {
invalidBlockCallBack = func(err error) {
self.invalidBlocks = append(self.invalidBlocks, err)
}
return
}
func TestAddPeer(t *testing.T) {
ethlogger.AddLogSystem(sys)
knownBlockIndexes := []int{0, 1}
invalidBlockIndexes := []int{2, 3}
invalidPoWIndexes := []int{4, 5}
hashPool, blockPool := newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
// TODO:
// hashPool, blockPool, blockChainChecker = newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
peer0 := &protocolChecker{
// blockHashesRequests: make([]int),
// blocksRequests: make([][]int),
// invalidBlocks: make([]error),
hashPool: hashPool,
}
best := blockPool.AddPeer(ethutil.Big1, newHash(100), "0",
peer0.requestBlockHashesCallBack(),
peer0.requestBlocksCallBack(),
peer0.invalidBlockCallBack(),
)
if !best {
t.Errorf("peer not accepted as best")
}
blockPool.Stop()
}

71
eth/error.go Normal file
View File

@ -0,0 +1,71 @@
package eth
import (
"fmt"
)
const (
ErrMsgTooLarge = iota
ErrDecode
ErrInvalidMsgCode
ErrProtocolVersionMismatch
ErrNetworkIdMismatch
ErrGenesisBlockMismatch
ErrNoStatusMsg
ErrExtraStatusMsg
ErrInvalidBlock
ErrInvalidPoW
ErrUnrequestedBlock
)
var errorToString = map[int]string{
ErrMsgTooLarge: "Message too long",
ErrDecode: "Invalid message",
ErrInvalidMsgCode: "Invalid message code",
ErrProtocolVersionMismatch: "Protocol version mismatch",
ErrNetworkIdMismatch: "NetworkId mismatch",
ErrGenesisBlockMismatch: "Genesis block mismatch",
ErrNoStatusMsg: "No status message",
ErrExtraStatusMsg: "Extra status message",
ErrInvalidBlock: "Invalid block",
ErrInvalidPoW: "Invalid PoW",
ErrUnrequestedBlock: "Unrequested block",
}
type protocolError struct {
Code int
fatal bool
message string
format string
params []interface{}
// size int
}
func newProtocolError(code int, format string, params ...interface{}) *protocolError {
return &protocolError{Code: code, format: format, params: params}
}
func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
err = newProtocolError(code, format, params...)
// report(err)
return
}
func (self protocolError) Error() (message string) {
message = self.message
if message == "" {
message, ok := errorToString[self.Code]
if !ok {
panic("invalid error code")
}
if self.format != "" {
message += ": " + fmt.Sprintf(self.format, self.params...)
}
self.message = message
}
return
}
func (self *protocolError) Fatal() bool {
return self.fatal
}

23
eth/peer_util.go Normal file
View File

@ -0,0 +1,23 @@
package eth
import (
"encoding/json"
"github.com/ethereum/go-ethereum/ethutil"
)
func WritePeers(path string, addresses []string) {
if len(addresses) > 0 {
data, _ := json.MarshalIndent(addresses, "", " ")
ethutil.WriteFile(path, data)
}
}
func ReadPeers(path string) (ips []string, err error) {
var data string
data, err = ethutil.ReadAllFile(path)
if err != nil {
json.Unmarshal([]byte(data), &ips)
}
return
}

319
eth/protocol.go Normal file
View File

@ -0,0 +1,319 @@
package eth
import (
"bytes"
"fmt"
"math"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
)
const (
ProtocolVersion = 49
NetworkId = 0
ProtocolLength = uint64(8)
ProtocolMaxMsgSize = 10 * 1024 * 1024
)
// eth protocol message codes
const (
StatusMsg = iota
GetTxMsg // unused
TxMsg
GetBlockHashesMsg
BlockHashesMsg
GetBlocksMsg
BlocksMsg
NewBlockMsg
)
// ethProtocol represents the ethereum wire protocol
// instance is running on each peer
type ethProtocol struct {
txPool txPool
chainManager chainManager
blockPool blockPool
peer *p2p.Peer
id string
rw p2p.MsgReadWriter
}
// backend is the interface the ethereum protocol backend should implement
// used as an argument to EthProtocol
type txPool interface {
AddTransactions([]*types.Transaction)
}
type chainManager interface {
GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte)
GetBlock(hash []byte) (block *types.Block)
Status() (td *big.Int, currentBlock []byte, genesisBlock []byte)
}
type blockPool interface {
AddBlockHashes(next func() ([]byte, bool), peerId string)
AddBlock(block *types.Block, peerId string)
AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
RemovePeer(peerId string)
}
// message structs used for rlp decoding
type newBlockMsgData struct {
Block *types.Block
TD *big.Int
}
type getBlockHashesMsgData struct {
Hash []byte
Amount uint64
}
// main entrypoint, wrappers starting a server running the eth protocol
// use this constructor to attach the protocol ("class") to server caps
// the Dev p2p layer then runs the protocol instance on each peer
func EthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool) p2p.Protocol {
return p2p.Protocol{
Name: "eth",
Version: ProtocolVersion,
Length: ProtocolLength,
Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
return runEthProtocol(txPool, chainManager, blockPool, peer, rw)
},
}
}
// the main loop that handles incoming messages
// note RemovePeer in the post-disconnect hook
func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool, peer *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
self := &ethProtocol{
txPool: txPool,
chainManager: chainManager,
blockPool: blockPool,
rw: rw,
peer: peer,
id: (string)(peer.Identity().Pubkey()),
}
err = self.handleStatus()
if err == nil {
for {
err = self.handle()
if err != nil {
fmt.Println(err)
self.blockPool.RemovePeer(self.id)
break
}
}
}
return
}
func (self *ethProtocol) handle() error {
msg, err := self.rw.ReadMsg()
if err != nil {
return err
}
if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
// make sure that the payload has been fully consumed
defer msg.Discard()
switch msg.Code {
case StatusMsg:
return ProtocolError(ErrExtraStatusMsg, "")
case TxMsg:
// TODO: rework using lazy RLP stream
var txs []*types.Transaction
if err := msg.Decode(&txs); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
self.txPool.AddTransactions(txs)
case GetBlockHashesMsg:
var request getBlockHashesMsgData
if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
case BlockHashesMsg:
// TODO: redo using lazy decode , this way very inefficient on known chains
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
var err error
iter := func() (hash []byte, ok bool) {
hash, err = msgStream.Bytes()
if err == nil {
ok = true
}
return
}
self.blockPool.AddBlockHashes(iter, self.id)
if err != nil && err != rlp.EOL {
return ProtocolError(ErrDecode, "%v", err)
}
case GetBlocksMsg:
var blockHashes [][]byte
if err := msg.Decode(&blockHashes); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
max := int(math.Min(float64(len(blockHashes)), blockHashesBatchSize))
var blocks []interface{}
for i, hash := range blockHashes {
if i >= max {
break
}
block := self.chainManager.GetBlock(hash)
if block != nil {
blocks = append(blocks, block.RlpData())
}
}
return self.rw.EncodeMsg(BlocksMsg, blocks...)
case BlocksMsg:
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
for {
var block *types.Block
if err := msgStream.Decode(&block); err != nil {
if err == rlp.EOL {
break
} else {
return ProtocolError(ErrDecode, "%v", err)
}
}
self.blockPool.AddBlock(block, self.id)
}
case NewBlockMsg:
var request newBlockMsgData
if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
hash := request.Block.Hash()
// to simplify backend interface adding a new block
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
// (or selected as new best peer)
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
called := true
iter := func() (hash []byte, ok bool) {
if called {
called = false
return hash, true
} else {
return
}
}
self.blockPool.AddBlockHashes(iter, self.id)
self.blockPool.AddBlock(request.Block, self.id)
}
default:
return ProtocolError(ErrInvalidMsgCode, "%v", msg.Code)
}
return nil
}
type statusMsgData struct {
ProtocolVersion uint
NetworkId uint
TD *big.Int
CurrentBlock []byte
GenesisBlock []byte
}
func (self *ethProtocol) statusMsg() p2p.Msg {
td, currentBlock, genesisBlock := self.chainManager.Status()
return p2p.NewMsg(StatusMsg,
uint32(ProtocolVersion),
uint32(NetworkId),
td,
currentBlock,
genesisBlock,
)
}
func (self *ethProtocol) handleStatus() error {
// send precanned status message
if err := self.rw.WriteMsg(self.statusMsg()); err != nil {
return err
}
// read and handle remote status
msg, err := self.rw.ReadMsg()
if err != nil {
return err
}
if msg.Code != StatusMsg {
return ProtocolError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
}
if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
var status statusMsgData
if err := msg.Decode(&status); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
_, _, genesisBlock := self.chainManager.Status()
if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
return ProtocolError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
}
if status.NetworkId != NetworkId {
return ProtocolError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
}
if ProtocolVersion != status.ProtocolVersion {
return ProtocolError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
}
self.peer.Infof("Peer is [eth] capable (%d/%d). TD=%v H=%x\n", status.ProtocolVersion, status.NetworkId, status.TD, status.CurrentBlock[:4])
//self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
self.peer.Infoln("AddPeer(IGNORED)")
return nil
}
func (self *ethProtocol) requestBlockHashes(from []byte) error {
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
return self.rw.EncodeMsg(GetBlockHashesMsg, from, blockHashesBatchSize)
}
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
self.peer.Debugf("fetching %v blocks", len(hashes))
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes))
}
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
err = ProtocolError(code, format, params...)
if err.Fatal() {
self.peer.Errorln(err)
} else {
self.peer.Debugln(err)
}
return
}
func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
err := ProtocolError(code, format, params...)
if err.Fatal() {
self.peer.Errorln(err)
// disconnect
} else {
self.peer.Debugln(err)
}
}

232
eth/protocol_test.go Normal file
View File

@ -0,0 +1,232 @@
package eth
import (
"io"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
)
type testMsgReadWriter struct {
in chan p2p.Msg
out chan p2p.Msg
}
func (self *testMsgReadWriter) In(msg p2p.Msg) {
self.in <- msg
}
func (self *testMsgReadWriter) Out(msg p2p.Msg) {
self.in <- msg
}
func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
self.out <- msg
return nil
}
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
return self.WriteMsg(p2p.NewMsg(code, data))
}
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
msg, ok := <-self.in
if !ok {
return msg, io.EOF
}
return msg, nil
}
func errorCheck(t *testing.T, expCode int, err error) {
perr, ok := err.(*protocolError)
if ok && perr != nil {
if code := perr.Code; code != expCode {
ok = false
}
}
if !ok {
t.Errorf("expected error code %v, got %v", ErrNoStatusMsg, err)
}
}
type TestBackend struct {
getTransactions func() []*types.Transaction
addTransactions func(txs []*types.Transaction)
getBlockHashes func(hash []byte, amount uint32) (hashes [][]byte)
addBlockHashes func(next func() ([]byte, bool), peerId string)
getBlock func(hash []byte) *types.Block
addBlock func(block *types.Block, peerId string) (err error)
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool)
removePeer func(peerId string)
status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
}
func (self *TestBackend) GetTransactions() (txs []*types.Transaction) {
if self.getTransactions != nil {
txs = self.getTransactions()
}
return
}
func (self *TestBackend) AddTransactions(txs []*types.Transaction) {
if self.addTransactions != nil {
self.addTransactions(txs)
}
}
func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) {
if self.getBlockHashes != nil {
hashes = self.getBlockHashes(hash, amount)
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
}
}
=======
func (self *TestBackend) AddHash(hash []byte, peer *p2p.Peer) (more bool) {
if self.addHash != nil {
more = self.addHash(hash, peer)
=======
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
>>>>>>> eth protocol changes
}
}
<<<<<<< HEAD
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
func (self *TestBackend) GetBlock(hash []byte) (block *types.Block) {
if self.getBlock != nil {
block = self.getBlock(hash)
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
if self.addBlock != nil {
err = self.addBlock(block, peerId)
=======
func (self *TestBackend) AddBlock(td *big.Int, block *types.Block, peer *p2p.Peer) (fetchHashes bool, err error) {
if self.addBlock != nil {
fetchHashes, err = self.addBlock(td, block, peer)
>>>>>>> initial commit for eth-p2p integration
=======
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
if self.addBlock != nil {
err = self.addBlock(block, peerId)
>>>>>>> eth protocol changes
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
=======
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peer *p2p.Peer) (fetchHashes bool) {
if self.addPeer != nil {
fetchHashes = self.addPeer(td, currentBlock, peer)
>>>>>>> initial commit for eth-p2p integration
=======
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
>>>>>>> eth protocol changes
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> eth protocol changes
func (self *TestBackend) RemovePeer(peerId string) {
if self.removePeer != nil {
self.removePeer(peerId)
}
}
<<<<<<< HEAD
=======
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
func (self *TestBackend) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
if self.status != nil {
td, currentBlock, genesisBlock = self.status()
}
return
}
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> eth protocol changes
// TODO: refactor this into p2p/client_identity
type peerId struct {
pubkey []byte
}
func (self *peerId) String() string {
return "test peer"
}
func (self *peerId) Pubkey() (pubkey []byte) {
pubkey = self.pubkey
if len(pubkey) == 0 {
pubkey = crypto.GenerateNewKeyPair().PublicKey
self.pubkey = pubkey
}
return
}
func testPeer() *p2p.Peer {
return p2p.NewPeer(&peerId{}, []p2p.Cap{})
}
func TestErrNoStatusMsg(t *testing.T) {
<<<<<<< HEAD
=======
func TestEth(t *testing.T) {
>>>>>>> initial commit for eth-p2p integration
=======
>>>>>>> eth protocol changes
quit := make(chan bool)
rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
testBackend := &TestBackend{}
var err error
go func() {
<<<<<<< HEAD
<<<<<<< HEAD
err = runEthProtocol(testBackend, testPeer(), rw)
=======
err = runEthProtocol(testBackend, nil, rw)
>>>>>>> initial commit for eth-p2p integration
=======
err = runEthProtocol(testBackend, testPeer(), rw)
>>>>>>> eth protocol changes
close(quit)
}()
statusMsg := p2p.NewMsg(4)
rw.In(statusMsg)
<-quit
errorCheck(t, ErrNoStatusMsg, err)
// read(t, remote, []byte("hello, world"), nil)
}

View File

@ -71,6 +71,10 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
return self.db.NewIterator(nil, nil) return self.db.NewIterator(nil, nil)
} }
func (self *LDBDatabase) Write(batch *leveldb.Batch) error {
return self.db.Write(batch, nil)
}
func (self *LDBDatabase) Close() { func (self *LDBDatabase) Close() {
// Close the leveldb database // Close the leveldb database
self.db.Close() self.db.Close()

View File

@ -1,659 +0,0 @@
package eth
import (
"container/list"
"encoding/json"
"fmt"
"math/big"
"math/rand"
"net"
"path"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/core"
"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/rpc"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/wire"
)
const (
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
seedNodeAddress = "poc-7.ethdev.com:30303"
)
var loggerger = logger.NewLogger("SERV")
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
// Loop thru the peers and close them (if we had them)
for e := peers.Front(); e != nil; e = e.Next() {
callback(e.Value.(*Peer), e)
}
}
const (
processReapingTimeout = 60 // TODO increase
)
type Ethereum struct {
// Channel for shutting down the ethereum
shutdownChan chan bool
quit chan bool
// DB interface
db ethutil.Database
// State manager for processing new blocks and managing the over all states
blockManager *core.BlockManager
// The transaction pool. Transaction can be pushed on this pool
// for later including in the blocks
txPool *core.TxPool
// The canonical chain
blockChain *core.ChainManager
// The block pool
blockPool *BlockPool
// Eventer
eventMux event.TypeMux
// Peers
peers *list.List
// Nonce
Nonce uint64
Addr net.Addr
Port string
blacklist [][]byte
peerMut sync.Mutex
// Capabilities for outgoing peers
serverCaps Caps
nat NAT
// Specifies the desired amount of maximum peers
MaxPeers int
Mining bool
listening bool
RpcServer *rpc.JsonRpcServer
keyManager *crypto.KeyManager
clientIdentity wire.ClientIdentity
isUpToDate bool
filterMu sync.RWMutex
filterId int
filters map[int]*core.Filter
}
func New(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) {
var err error
var nat NAT
if usePnp {
nat, err = Discover()
if err != nil {
loggerger.Debugln("UPnP failed", err)
}
}
bootstrapDb(db)
ethutil.Config.Db = db
nonce, _ := ethutil.RandomUint64()
ethereum := &Ethereum{
shutdownChan: make(chan bool),
quit: make(chan bool),
db: db,
peers: list.New(),
Nonce: nonce,
serverCaps: caps,
nat: nat,
keyManager: keyManager,
clientIdentity: clientIdentity,
isUpToDate: true,
filters: make(map[int]*core.Filter),
}
ethereum.blockPool = NewBlockPool(ethereum)
ethereum.txPool = core.NewTxPool(ethereum)
ethereum.blockChain = core.NewChainManager(ethereum.EventMux())
ethereum.blockManager = core.NewBlockManager(ethereum)
ethereum.blockChain.SetProcessor(ethereum.blockManager)
// Start the tx pool
ethereum.txPool.Start()
return ethereum, nil
}
func (s *Ethereum) KeyManager() *crypto.KeyManager {
return s.keyManager
}
func (s *Ethereum) ClientIdentity() wire.ClientIdentity {
return s.clientIdentity
}
func (s *Ethereum) ChainManager() *core.ChainManager {
return s.blockChain
}
func (s *Ethereum) BlockManager() *core.BlockManager {
return s.blockManager
}
func (s *Ethereum) TxPool() *core.TxPool {
return s.txPool
}
func (s *Ethereum) BlockPool() *BlockPool {
return s.blockPool
}
func (s *Ethereum) EventMux() *event.TypeMux {
return &s.eventMux
}
func (self *Ethereum) Db() ethutil.Database {
return self.db
}
func (s *Ethereum) ServerCaps() Caps {
return s.serverCaps
}
func (s *Ethereum) IsMining() bool {
return s.Mining
}
func (s *Ethereum) PeerCount() int {
return s.peers.Len()
}
func (s *Ethereum) IsUpToDate() bool {
upToDate := true
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
if atomic.LoadInt32(&peer.connected) == 1 {
if peer.catchingUp == true && peer.versionKnown {
upToDate = false
}
}
})
return upToDate
}
func (s *Ethereum) PushPeer(peer *Peer) {
s.peers.PushBack(peer)
}
func (s *Ethereum) IsListening() bool {
return s.listening
}
func (s *Ethereum) HighestTDPeer() (td *big.Int) {
td = big.NewInt(0)
eachPeer(s.peers, func(p *Peer, v *list.Element) {
if p.td.Cmp(td) > 0 {
td = p.td
}
})
return
}
func (self *Ethereum) BlacklistPeer(peer *Peer) {
self.blacklist = append(self.blacklist, peer.pubkey)
}
func (s *Ethereum) AddPeer(conn net.Conn) {
peer := NewPeer(conn, s, true)
if peer != nil {
if s.peers.Len() < s.MaxPeers {
peer.Start()
} else {
loggerger.Debugf("Max connected peers reached. Not adding incoming peer.")
}
}
}
func (s *Ethereum) ProcessPeerList(addrs []string) {
for _, addr := range addrs {
// TODO Probably requires some sanity checks
s.ConnectToPeer(addr)
}
}
func (s *Ethereum) ConnectToPeer(addr string) error {
if s.peers.Len() < s.MaxPeers {
var alreadyConnected bool
ahost, aport, _ := net.SplitHostPort(addr)
var chost string
ips, err := net.LookupIP(ahost)
if err != nil {
return err
} else {
// If more then one ip is available try stripping away the ipv6 ones
if len(ips) > 1 {
var ipsv4 []net.IP
// For now remove the ipv6 addresses
for _, ip := range ips {
if strings.Contains(ip.String(), "::") {
continue
} else {
ipsv4 = append(ipsv4, ip)
}
}
if len(ipsv4) == 0 {
return fmt.Errorf("[SERV] No IPV4 addresses available for hostname")
}
// Pick a random ipv4 address, simulating round-robin DNS.
rand.Seed(time.Now().UTC().UnixNano())
i := rand.Intn(len(ipsv4))
chost = ipsv4[i].String()
} else {
if len(ips) == 0 {
return fmt.Errorf("[SERV] No IPs resolved for the given hostname")
return nil
}
chost = ips[0].String()
}
}
eachPeer(s.peers, func(p *Peer, v *list.Element) {
if p.conn == nil {
return
}
phost, pport, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
if phost == chost && pport == aport {
alreadyConnected = true
//loggerger.Debugf("Peer %s already added.\n", chost)
return
}
})
if alreadyConnected {
return nil
}
NewOutboundPeer(addr, s, s.serverCaps)
}
return nil
}
func (s *Ethereum) OutboundPeers() []*Peer {
// Create a new peer slice with at least the length of the total peers
outboundPeers := make([]*Peer, s.peers.Len())
length := 0
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if !p.inbound && p.conn != nil {
outboundPeers[length] = p
length++
}
})
return outboundPeers[:length]
}
func (s *Ethereum) InboundPeers() []*Peer {
// Create a new peer slice with at least the length of the total peers
inboundPeers := make([]*Peer, s.peers.Len())
length := 0
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if p.inbound {
inboundPeers[length] = p
length++
}
})
return inboundPeers[:length]
}
func (s *Ethereum) InOutPeers() []*Peer {
// Reap the dead peers first
s.reapPeers()
// Create a new peer slice with at least the length of the total peers
inboundPeers := make([]*Peer, s.peers.Len())
length := 0
eachPeer(s.peers, func(p *Peer, e *list.Element) {
// Only return peers with an actual ip
if len(p.host) > 0 {
inboundPeers[length] = p
length++
}
})
return inboundPeers[:length]
}
func (s *Ethereum) Broadcast(msgType wire.MsgType, data []interface{}) {
msg := wire.NewMessage(msgType, data)
s.BroadcastMsg(msg)
}
func (s *Ethereum) BroadcastMsg(msg *wire.Msg) {
eachPeer(s.peers, func(p *Peer, e *list.Element) {
p.QueueMessage(msg)
})
}
func (s *Ethereum) Peers() *list.List {
return s.peers
}
func (s *Ethereum) reapPeers() {
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
s.removePeerElement(e)
}
})
}
func (s *Ethereum) removePeerElement(e *list.Element) {
s.peerMut.Lock()
defer s.peerMut.Unlock()
s.peers.Remove(e)
s.eventMux.Post(PeerListEvent{s.peers})
}
func (s *Ethereum) RemovePeer(p *Peer) {
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
if peer == p {
s.removePeerElement(e)
}
})
}
func (s *Ethereum) reapDeadPeerHandler() {
reapTimer := time.NewTicker(processReapingTimeout * time.Second)
for {
select {
case <-reapTimer.C:
s.reapPeers()
}
}
}
// Start the ethereum
func (s *Ethereum) Start(seed bool) {
s.blockPool.Start()
// Bind to addr and port
ln, err := net.Listen("tcp", ":"+s.Port)
if err != nil {
loggerger.Warnf("Port %s in use. Connection listening disabled. Acting as client", s.Port)
s.listening = false
} else {
s.listening = true
// Starting accepting connections
loggerger.Infoln("Ready and accepting connections")
// Start the peer handler
go s.peerHandler(ln)
}
if s.nat != nil {
go s.upnpUpdateThread()
}
// Start the reaping processes
go s.reapDeadPeerHandler()
go s.update()
go s.filterLoop()
if seed {
s.Seed()
}
s.ConnectToPeer("localhost:40404")
loggerger.Infoln("Server started")
}
func (s *Ethereum) Seed() {
// Sorry Py person. I must blacklist. you perform badly
s.blacklist = append(s.blacklist, ethutil.Hex2Bytes("64656330303561383532336435376331616537643864663236623336313863373537353163636634333530626263396330346237336262623931383064393031"))
ips := PastPeers()
if len(ips) > 0 {
for _, ip := range ips {
loggerger.Infoln("Connecting to previous peer ", ip)
s.ConnectToPeer(ip)
}
} else {
loggerger.Debugln("Retrieving seed nodes")
// Eth-Go Bootstrapping
ips, er := net.LookupIP("seed.bysh.me")
if er == nil {
peers := []string{}
for _, ip := range ips {
node := fmt.Sprintf("%s:%d", ip.String(), 30303)
loggerger.Debugln("Found DNS Go Peer:", node)
peers = append(peers, node)
}
s.ProcessPeerList(peers)
}
// Official DNS Bootstrapping
_, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
if err == nil {
peers := []string{}
// Iterate SRV nodes
for _, n := range nodes {
target := n.Target
port := strconv.Itoa(int(n.Port))
// Resolve target to ip (Go returns list, so may resolve to multiple ips?)
addr, err := net.LookupHost(target)
if err == nil {
for _, a := range addr {
// Build string out of SRV port and Resolved IP
peer := net.JoinHostPort(a, port)
loggerger.Debugln("Found DNS Bootstrap Peer:", peer)
peers = append(peers, peer)
}
} else {
loggerger.Debugln("Couldn't resolve :", target)
}
}
// Connect to Peer list
s.ProcessPeerList(peers)
}
s.ConnectToPeer(seedNodeAddress)
}
}
func (s *Ethereum) peerHandler(listener net.Listener) {
for {
conn, err := listener.Accept()
if err != nil {
loggerger.Debugln(err)
continue
}
go s.AddPeer(conn)
}
}
func (s *Ethereum) Stop() {
// Stop eventMux first, it will close all subscriptions.
s.eventMux.Stop()
// Close the database
defer s.db.Close()
var ips []string
eachPeer(s.peers, func(p *Peer, e *list.Element) {
ips = append(ips, p.conn.RemoteAddr().String())
})
if len(ips) > 0 {
d, _ := json.MarshalIndent(ips, "", " ")
ethutil.WriteFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"), d)
}
eachPeer(s.peers, func(p *Peer, e *list.Element) {
p.Stop()
})
close(s.quit)
if s.RpcServer != nil {
s.RpcServer.Stop()
}
s.txPool.Stop()
s.blockPool.Stop()
loggerger.Infoln("Server stopped")
close(s.shutdownChan)
}
// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
<-s.shutdownChan
}
func (s *Ethereum) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes.
timer := time.NewTimer(5 * time.Minute)
lport, _ := strconv.ParseInt(s.Port, 10, 16)
first := true
out:
for {
select {
case <-timer.C:
var err error
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
if err != nil {
loggerger.Debugln("can't add UPnP port mapping:", err)
break out
}
if first && err == nil {
_, err = s.nat.GetExternalAddress()
if err != nil {
loggerger.Debugln("UPnP can't get external address:", err)
continue out
}
first = false
}
timer.Reset(time.Minute * 15)
case <-s.quit:
break out
}
}
timer.Stop()
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
loggerger.Debugln("unable to remove UPnP port mapping:", err)
} else {
loggerger.Debugln("succesfully disestablished UPnP port mapping")
}
}
func (self *Ethereum) update() {
upToDateTimer := time.NewTicker(1 * time.Second)
out:
for {
select {
case <-upToDateTimer.C:
if self.IsUpToDate() && !self.isUpToDate {
self.eventMux.Post(ChainSyncEvent{false})
self.isUpToDate = true
} else if !self.IsUpToDate() && self.isUpToDate {
self.eventMux.Post(ChainSyncEvent{true})
self.isUpToDate = false
}
case <-self.quit:
break out
}
}
}
// InstallFilter adds filter for blockchain events.
// The filter's callbacks will run for matching blocks and messages.
// The filter should not be modified after it has been installed.
func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
self.filterMu.Lock()
id = self.filterId
self.filters[id] = filter
self.filterId++
self.filterMu.Unlock()
return id
}
func (self *Ethereum) UninstallFilter(id int) {
self.filterMu.Lock()
delete(self.filters, id)
self.filterMu.Unlock()
}
// GetFilter retrieves a filter installed using InstallFilter.
// The filter may not be modified.
func (self *Ethereum) GetFilter(id int) *core.Filter {
self.filterMu.RLock()
defer self.filterMu.RUnlock()
return self.filters[id]
}
func (self *Ethereum) filterLoop() {
// Subscribe to events
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
for event := range events.Chan() {
switch event := event.(type) {
case core.NewBlockEvent:
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.BlockCallback != nil {
filter.BlockCallback(event.Block)
}
}
self.filterMu.RUnlock()
case state.Messages:
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.MessageCallback != nil {
msgs := filter.FilterMessages(event)
if len(msgs) > 0 {
filter.MessageCallback(msgs)
}
}
}
self.filterMu.RUnlock()
}
}
}
func bootstrapDb(db ethutil.Database) {
d, _ := db.Get([]byte("ProtocolVersion"))
protov := ethutil.NewValue(d).Uint()
if protov == 0 {
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
}
}
func PastPeers() []string {
var ips []string
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"))
json.Unmarshal([]byte(data), &ips)
return ips
}

View File

@ -2,7 +2,6 @@ package ethutil
import ( import (
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
@ -193,8 +192,13 @@ func Encode(object interface{}) []byte {
if blen < 56 { if blen < 56 {
buff.WriteByte(byte(blen) + 0xc0) buff.WriteByte(byte(blen) + 0xc0)
} else { } else {
buff.WriteByte(byte(intlen(int64(blen))) + 0xf7) ilen := byte(intlen(int64(blen)))
binary.Write(&buff, binary.BigEndian, int64(blen)) buff.WriteByte(ilen + 0xf7)
t := make([]byte, ilen)
for i := byte(0); i < ilen; i++ {
t[ilen-i-1] = byte(blen >> (i * 8))
}
buff.Write(t)
} }
buff.ReadFrom(&b) buff.ReadFrom(&b)
} }

View File

@ -397,5 +397,5 @@ func (it *ValueIterator) Value() *Value {
} }
func (it *ValueIterator) Idx() int { func (it *ValueIterator) Idx() int {
return it.idx return it.idx - 1
} }

View File

@ -2,19 +2,29 @@ package filter
type Generic struct { type Generic struct {
Str1, Str2, Str3 string Str1, Str2, Str3 string
Data map[string]struct{}
Fn func(data interface{}) Fn func(data interface{})
} }
// self = registered, f = incoming
func (self Generic) Compare(f Filter) bool { func (self Generic) Compare(f Filter) bool {
var strMatch, dataMatch = true, true
filter := f.(Generic) filter := f.(Generic)
if (len(self.Str1) == 0 || filter.Str1 == self.Str1) && if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
(len(self.Str2) == 0 || filter.Str2 == self.Str2) && (len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
(len(self.Str3) == 0 || filter.Str3 == self.Str3) { (len(self.Str3) > 0 && filter.Str3 != self.Str3) {
return true strMatch = false
} }
for k, _ := range self.Data {
if _, ok := filter.Data[k]; !ok {
return false return false
}
}
return strMatch && dataMatch
} }
func (self Generic) Trigger(data interface{}) { func (self Generic) Trigger(data interface{}) {

View File

@ -0,0 +1,94 @@
// XXX This is the old filter system specifically for messages. This is till in used and could use some refactoring
package filter
import (
"sync"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/state"
)
type FilterManager struct {
eventMux *event.TypeMux
filterMu sync.RWMutex
filterId int
filters map[int]*core.Filter
quit chan struct{}
}
func NewFilterManager(mux *event.TypeMux) *FilterManager {
return &FilterManager{
eventMux: mux,
filters: make(map[int]*core.Filter),
}
}
func (self *FilterManager) Start() {
go self.filterLoop()
}
func (self *FilterManager) Stop() {
close(self.quit)
}
func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
self.filterMu.Lock()
id = self.filterId
self.filters[id] = filter
self.filterId++
self.filterMu.Unlock()
return id
}
func (self *FilterManager) UninstallFilter(id int) {
self.filterMu.Lock()
delete(self.filters, id)
self.filterMu.Unlock()
}
// GetFilter retrieves a filter installed using InstallFilter.
// The filter may not be modified.
func (self *FilterManager) GetFilter(id int) *core.Filter {
self.filterMu.RLock()
defer self.filterMu.RUnlock()
return self.filters[id]
}
func (self *FilterManager) filterLoop() {
// Subscribe to events
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
out:
for {
select {
case <-self.quit:
break out
case event := <-events.Chan():
switch event := event.(type) {
case core.NewBlockEvent:
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.BlockCallback != nil {
filter.BlockCallback(event.Block)
}
}
self.filterMu.RUnlock()
case state.Messages:
self.filterMu.RLock()
for _, filter := range self.filters {
if filter.MessageCallback != nil {
msgs := filter.FilterMessages(event)
if len(msgs) > 0 {
filter.MessageCallback(msgs)
}
}
}
self.filterMu.RUnlock()
}
}
}
}

View File

@ -1,11 +0,0 @@
package eth
import "container/list"
type PeerListEvent struct {
Peers *list.List
}
type ChainSyncEvent struct {
InSync bool
}

View File

@ -13,7 +13,10 @@ for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d)
do do
if ls $dir/*.go &> /dev/null; then if ls $dir/*.go &> /dev/null; then
# echo $dir # echo $dir
if [[ $dir != "./tests/vm" ]]
then
go test -covermode=count -coverprofile=$dir/profile.tmp $dir go test -covermode=count -coverprofile=$dir/profile.tmp $dir
fi
if [ -f $dir/profile.tmp ] if [ -f $dir/profile.tmp ]
then then
cat $dir/profile.tmp | tail -n +2 >> profile.cov cat $dir/profile.tmp | tail -n +2 >> profile.cov
@ -26,4 +29,4 @@ go tool cover -func profile.cov
# To submit the test coverage result to coveralls.io, # To submit the test coverage result to coveralls.io,
# use goveralls (https://github.com/mattn/goveralls) # use goveralls (https://github.com/mattn/goveralls)
# goveralls -coverprofile=profile.cov -service=travis-ci goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN

View File

@ -1,8 +0,0 @@
#!/bin/bash
set -e
TEST_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 [ "$TEST_DEPS" ]; then
go get -race $TEST_DEPS
fi

View File

@ -7,10 +7,10 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -203,7 +203,7 @@ func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
if err != nil { if err != nil {
return otto.FalseValue() return otto.FalseValue()
} }
self.ethereum.ConnectToPeer(host) self.ethereum.SuggestPeer(host)
return otto.TrueValue() return otto.TrueValue()
} }

View File

@ -3,7 +3,7 @@ package javascript
import ( import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/ui" "github.com/ethereum/go-ethereum/ui"
@ -18,11 +18,11 @@ type JSStateObject struct {
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value { func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
cb := call.Argument(0) cb := call.Argument(0)
self.JSObject.EachStorage(func(key string, value *ethutil.Value) {
value.Decode()
cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes()))) it := self.JSObject.Trie().Iterator()
}) for it.Next() {
cb.Call(self.eth.toVal(self), self.eth.toVal(ethutil.Bytes2Hex(it.Key)), self.eth.toVal(ethutil.Bytes2Hex(it.Value)))
}
return otto.UndefinedValue() return otto.UndefinedValue()
} }

View File

@ -27,7 +27,7 @@ import (
"math/big" "math/big"
"sort" "sort"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow/ezp" "github.com/ethereum/go-ethereum/pow/ezp"
@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/wire"
) )
type LocalTx struct { type LocalTx struct {
@ -57,7 +56,7 @@ type Miner struct {
eth *eth.Ethereum eth *eth.Ethereum
events event.Subscription events event.Subscription
uncles types.Blocks uncles []*types.Header
localTxs map[int]*LocalTx localTxs map[int]*LocalTx
localTxId int localTxId int
@ -185,15 +184,17 @@ func (self *Miner) mine() {
block.SetUncles(self.uncles) block.SetUncles(self.uncles)
} }
parent := chainMan.GetBlock(block.PrevHash) parent := chainMan.GetBlock(block.ParentHash())
coinbase := block.State().GetOrNewStateObject(block.Coinbase) coinbase := block.State().GetOrNewStateObject(block.Coinbase())
coinbase.SetGasPool(block.CalcGasLimit(parent)) coinbase.SetGasPool(core.CalcGasLimit(parent, block))
transactions := self.finiliseTxs() transactions := self.finiliseTxs()
state := block.State()
// Accumulate all valid transactions and apply them to the new state // Accumulate all valid transactions and apply them to the new state
// Error may be ignored. It's not important during mining // Error may be ignored. It's not important during mining
receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true) receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, state, block, transactions, true)
if err != nil { if err != nil {
minerlogger.Debugln(err) minerlogger.Debugln(err)
} }
@ -203,21 +204,22 @@ func (self *Miner) mine() {
block.SetReceipts(receipts) block.SetReceipts(receipts)
// Accumulate the rewards included for this block // Accumulate the rewards included for this block
blockManager.AccumelateRewards(block.State(), block, parent) blockManager.AccumelateRewards(state, block, parent)
block.State().Update(ethutil.Big0) state.Update(ethutil.Big0)
block.SetRoot(state.Root())
minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions)) minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))
// Find a valid nonce // Find a valid nonce
nonce := self.pow.Search(block, self.powQuitCh) nonce := self.pow.Search(block, self.powQuitCh)
if nonce != nil { if nonce != nil {
block.Nonce = nonce block.Header().Nonce = nonce
err := chainMan.InsertChain(types.Blocks{block}) err := chainMan.InsertChain(types.Blocks{block})
if err != nil { if err != nil {
minerlogger.Infoln(err) minerlogger.Infoln(err)
} else { } else {
self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val}) self.eth.EventMux().Post(core.NewMinedBlockEvent{block})
minerlogger.Infof("🔨 Mined block %x\n", block.Hash()) minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
minerlogger.Infoln(block) minerlogger.Infoln(block)
@ -237,8 +239,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
key := self.eth.KeyManager() key := self.eth.KeyManager()
for i, ltx := range self.localTxs { for i, ltx := range self.localTxs {
tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data) tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
tx.Nonce = state.GetNonce(self.Coinbase) tx.SetNonce(state.GetNonce(self.Coinbase))
state.SetNonce(self.Coinbase, tx.Nonce+1) state.SetNonce(self.Coinbase, tx.Nonce()+1)
tx.Sign(key.PrivateKey()) tx.Sign(key.PrivateKey())
@ -246,8 +248,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
} }
// Faster than append // Faster than append
for _, tx := range self.eth.TxPool().CurrentTransactions() { for _, tx := range self.eth.TxPool().GetTransactions() {
if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 { if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 {
txs[actualSize] = tx txs[actualSize] = tx
actualSize++ actualSize++
} }

12
nat.go
View File

@ -1,12 +0,0 @@
package eth
import (
"net"
)
// protocol is either "udp" or "tcp"
type NAT interface {
GetExternalAddress() (addr net.IP, err error)
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
}

View File

@ -1,55 +0,0 @@
package eth
import (
"fmt"
"net"
natpmp "github.com/jackpal/go-nat-pmp"
)
// Adapt the NAT-PMP protocol to the NAT interface
// TODO:
// + Register for changes to the external address.
// + Re-register port mapping when router reboots.
// + A mechanism for keeping a port mapping registered.
type natPMPClient struct {
client *natpmp.Client
}
func NewNatPMP(gateway net.IP) (nat NAT) {
return &natPMPClient{natpmp.NewClient(gateway)}
}
func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
response, err := n.client.GetExternalAddress()
if err != nil {
return
}
ip := response.ExternalIPAddress
addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
return
}
func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
description string, timeout int) (mappedExternalPort int, err error) {
if timeout <= 0 {
err = fmt.Errorf("timeout must not be <= 0")
return
}
// Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
if err != nil {
return
}
mappedExternalPort = int(response.MappedExternalPort)
return
}
func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
// To destroy a mapping, send an add-port with
// an internalPort of the internal port to destroy, an external port of zero and a time of zero.
_, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
return
}

View File

@ -1,338 +0,0 @@
package eth
// Just enough UPnP to be able to forward ports
//
import (
"bytes"
"encoding/xml"
"errors"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"
)
type upnpNAT struct {
serviceURL string
ourIP string
}
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
if err != nil {
return
}
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {
return
}
socket := conn.(*net.UDPConn)
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(10 * time.Second))
if err != nil {
return
}
st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
buf := bytes.NewBufferString(
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
st +
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n\r\n")
message := buf.Bytes()
answerBytes := make([]byte, 1024)
for i := 0; i < 3; i++ {
_, err = socket.WriteToUDP(message, ssdp)
if err != nil {
return
}
var n int
n, _, err = socket.ReadFromUDP(answerBytes)
if err != nil {
continue
// socket.Close()
// return
}
answer := string(answerBytes[0:n])
if strings.Index(answer, "\r\n"+st) < 0 {
continue
}
// HTTP header field names are case-insensitive.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
locString := "\r\nlocation: "
answer = strings.ToLower(answer)
locIndex := strings.Index(answer, locString)
if locIndex < 0 {
continue
}
loc := answer[locIndex+len(locString):]
endIndex := strings.Index(loc, "\r\n")
if endIndex < 0 {
continue
}
locURL := loc[0:endIndex]
var serviceURL string
serviceURL, err = getServiceURL(locURL)
if err != nil {
return
}
var ourIP string
ourIP, err = getOurIP()
if err != nil {
return
}
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
return
}
err = errors.New("UPnP port discovery failed.")
return
}
// service represents the Service type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type service struct {
ServiceType string `xml:"serviceType"`
ControlURL string `xml:"controlURL"`
}
// deviceList represents the deviceList type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type deviceList struct {
XMLName xml.Name `xml:"deviceList"`
Device []device `xml:"device"`
}
// serviceList represents the serviceList type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type serviceList struct {
XMLName xml.Name `xml:"serviceList"`
Service []service `xml:"service"`
}
// device represents the device type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type device struct {
XMLName xml.Name `xml:"device"`
DeviceType string `xml:"deviceType"`
DeviceList deviceList `xml:"deviceList"`
ServiceList serviceList `xml:"serviceList"`
}
// specVersion represents the specVersion in a UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type specVersion struct {
XMLName xml.Name `xml:"specVersion"`
Major int `xml:"major"`
Minor int `xml:"minor"`
}
// root represents the Root document for a UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type root struct {
XMLName xml.Name `xml:"root"`
SpecVersion specVersion
Device device
}
func getChildDevice(d *device, deviceType string) *device {
dl := d.DeviceList.Device
for i := 0; i < len(dl); i++ {
if dl[i].DeviceType == deviceType {
return &dl[i]
}
}
return nil
}
func getChildService(d *device, serviceType string) *service {
sl := d.ServiceList.Service
for i := 0; i < len(sl); i++ {
if sl[i].ServiceType == serviceType {
return &sl[i]
}
}
return nil
}
func getOurIP() (ip string, err error) {
hostname, err := os.Hostname()
if err != nil {
return
}
p, err := net.LookupIP(hostname)
if err != nil && len(p) > 0 {
return
}
return p[0].String(), nil
}
func getServiceURL(rootURL string) (url string, err error) {
r, err := http.Get(rootURL)
if err != nil {
return
}
defer r.Body.Close()
if r.StatusCode >= 400 {
err = errors.New(string(r.StatusCode))
return
}
var root root
err = xml.NewDecoder(r.Body).Decode(&root)
if err != nil {
return
}
a := &root.Device
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
err = errors.New("No InternetGatewayDevice")
return
}
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
if b == nil {
err = errors.New("No WANDevice")
return
}
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
if c == nil {
err = errors.New("No WANConnectionDevice")
return
}
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
if d == nil {
err = errors.New("No WANIPConnection")
return
}
url = combineURL(rootURL, d.ControlURL)
return
}
func combineURL(rootURL, subURL string) string {
protocolEnd := "://"
protoEndIndex := strings.Index(rootURL, protocolEnd)
a := rootURL[protoEndIndex+len(protocolEnd):]
rootIndex := strings.Index(a, "/")
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
}
func soapRequest(url, function, message string) (r *http.Response, err error) {
fullMessage := "<?xml version=\"1.0\" ?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
"<s:Body>" + message + "</s:Body></s:Envelope>"
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
//req.Header.Set("Transfer-Encoding", "chunked")
req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
req.Header.Set("Connection", "Close")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("Pragma", "no-cache")
// log.Stderr("soapRequest ", req)
//fmt.Println(fullMessage)
r, err = http.DefaultClient.Do(req)
if err != nil {
return
}
if r.Body != nil {
defer r.Body.Close()
}
if r.StatusCode >= 400 {
// log.Stderr(function, r.StatusCode)
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
r = nil
return
}
return
}
type statusInfo struct {
externalIpAddress string
}
func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
"</u:GetStatusInfo>"
var response *http.Response
response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
if err != nil {
return
}
// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
response.Body.Close()
return
}
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
info, err := n.getStatusInfo()
if err != nil {
return
}
addr = net.ParseIP(info.externalIpAddress)
return
}
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation.
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
message += description +
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
"</NewLeaseDuration></u:AddPortMapping>"
var response *http.Response
response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
if err != nil {
return
}
// TODO: check response to see if the port was forwarded
// log.Println(message, response)
mappedExternalPort = externalPort
_ = response
return
}
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
"</u:DeletePortMapping>"
var response *http.Response
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
if err != nil {
return
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)
_ = response
return
}

View File

@ -246,12 +246,7 @@ func (srv *Server) Stop() {
func (srv *Server) discLoop() { func (srv *Server) discLoop() {
for peer := range srv.peerDisconnect { for peer := range srv.peerDisconnect {
// peer has just disconnected. free up its slot. srv.removePeer(peer)
srvlog.Infof("%v is gone", peer)
srv.peerSlots <- peer.slot
srv.lock.Lock()
srv.peers[peer.slot] = nil
srv.lock.Unlock()
} }
} }
@ -384,7 +379,7 @@ func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer {
func (srv *Server) removePeer(peer *Peer) { func (srv *Server) removePeer(peer *Peer) {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
srvlog.Debugf("Removing peer %v %v (slot %v)\n", peer, peer.slot) srvlog.Debugf("Removing %v (slot %v)\n", peer, peer.slot)
if srv.peers[peer.slot] != peer { if srv.peers[peer.slot] != peer {
srvlog.Warnln("Invalid peer to remove:", peer) srvlog.Warnln("Invalid peer to remove:", peer)
return return
@ -416,6 +411,7 @@ func (srv *Server) verifyPeer(addr *peerAddr) error {
return nil return nil
} }
// TODO replace with "Set"
type Blacklist interface { type Blacklist interface {
Get([]byte) (bool, error) Get([]byte) (bool, error)
Put([]byte) error Put([]byte) error

881
peer.go
View File

@ -1,881 +0,0 @@
package eth
import (
"bytes"
"container/list"
"fmt"
"math"
"math/big"
"net"
"strconv"
"strings"
"sync/atomic"
"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 peerlogger = logger.NewLogger("PEER")
const (
// The size of the output buffer for writing messages
outputBufferSize = 50
// Current protocol version
ProtocolVersion = 49
// Current P2P version
P2PVersion = 2
// Ethereum network version
NetVersion = 0
// Interval for ping/pong message
pingPongTimer = 2 * time.Second
)
type DiscReason byte
const (
// Values are given explicitly instead of by iota because these values are
// defined by the wire protocol spec; it is easier for humans to ensure
// correctness when values are explicit.
DiscRequested DiscReason = iota
DiscReTcpSysErr
DiscBadProto
DiscBadPeer
DiscTooManyPeers
DiscConnDup
DiscGenesisErr
DiscProtoErr
DiscQuitting
)
var discReasonToString = []string{
"requested",
"TCP sys error",
"bad protocol",
"useless peer",
"too many peers",
"already connected",
"wrong genesis block",
"incompatible network",
"quitting",
}
func (d DiscReason) String() string {
if len(discReasonToString) < int(d) {
return "Unknown"
}
return discReasonToString[d]
}
// Peer capabilities
type Caps byte
const (
CapPeerDiscTy Caps = 1 << iota
CapTxTy
CapChainTy
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
)
var capsToString = map[Caps]string{
CapPeerDiscTy: "Peer discovery",
CapTxTy: "Transaction relaying",
CapChainTy: "Block chain relaying",
}
func (c Caps) IsCap(cap Caps) bool {
return c&cap > 0
}
func (c Caps) String() string {
var caps []string
if c.IsCap(CapPeerDiscTy) {
caps = append(caps, capsToString[CapPeerDiscTy])
}
if c.IsCap(CapChainTy) {
caps = append(caps, capsToString[CapChainTy])
}
if c.IsCap(CapTxTy) {
caps = append(caps, capsToString[CapTxTy])
}
return strings.Join(caps, " | ")
}
type Peer struct {
// Ethereum interface
ethereum *Ethereum
// Net connection
conn net.Conn
// Output queue which is used to communicate and handle messages
outputQueue chan *wire.Msg
// Quit channel
quit chan bool
// Determines whether it's an inbound or outbound peer
inbound bool
// Flag for checking the peer's connectivity state
connected int32
disconnect int32
// Last known message send
lastSend time.Time
// Indicated whether a verack has been send or not
// This flag is used by writeMessage to check if messages are allowed
// to be send or not. If no version is known all messages are ignored.
versionKnown bool
statusKnown bool
// Last received pong message
lastPong int64
lastBlockReceived time.Time
doneFetchingHashes bool
lastHashAt time.Time
lastHashRequestedAt time.Time
host []byte
port uint16
caps Caps
td *big.Int
bestHash []byte
lastReceivedHash []byte
requestedHashes [][]byte
// This peer's public key
pubkey []byte
// Indicated whether the node is catching up or not
catchingUp bool
diverted bool
blocksRequested int
version string
// We use this to give some kind of pingtime to a node, not very accurate, could be improved.
pingTime time.Duration
pingStartTime time.Time
lastRequestedBlock *types.Block
protocolCaps *ethutil.Value
}
func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
pubkey := ethereum.KeyManager().PublicKey()[1:]
return &Peer{
outputQueue: make(chan *wire.Msg, outputBufferSize),
quit: make(chan bool),
ethereum: ethereum,
conn: conn,
inbound: inbound,
disconnect: 0,
connected: 1,
port: 30303,
pubkey: pubkey,
blocksRequested: 10,
caps: ethereum.ServerCaps(),
version: ethereum.ClientIdentity().String(),
protocolCaps: ethutil.NewValue(nil),
td: big.NewInt(0),
doneFetchingHashes: true,
}
}
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
p := &Peer{
outputQueue: make(chan *wire.Msg, outputBufferSize),
quit: make(chan bool),
ethereum: ethereum,
inbound: false,
connected: 0,
disconnect: 0,
port: 30303,
caps: caps,
version: ethereum.ClientIdentity().String(),
protocolCaps: ethutil.NewValue(nil),
td: big.NewInt(0),
doneFetchingHashes: true,
}
// Set up the connection in another goroutine so we don't block the main thread
go func() {
conn, err := p.Connect(addr)
if err != nil {
//peerlogger.Debugln("Connection to peer failed. Giving up.", err)
p.Stop()
return
}
p.conn = conn
// Atomically set the connection state
atomic.StoreInt32(&p.connected, 1)
atomic.StoreInt32(&p.disconnect, 0)
p.Start()
}()
return p
}
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
const maxTries = 3
for attempts := 0; attempts < maxTries; attempts++ {
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil {
time.Sleep(time.Duration(attempts*20) * time.Second)
continue
}
// Success
return
}
return
}
// Getters
func (p *Peer) PingTime() string {
return p.pingTime.String()
}
func (p *Peer) Inbound() bool {
return p.inbound
}
func (p *Peer) LastSend() time.Time {
return p.lastSend
}
func (p *Peer) LastPong() int64 {
return p.lastPong
}
func (p *Peer) Host() []byte {
return p.host
}
func (p *Peer) Port() uint16 {
return p.port
}
func (p *Peer) Version() string {
return p.version
}
func (p *Peer) Connected() *int32 {
return &p.connected
}
// Setters
func (p *Peer) SetVersion(version string) {
p.version = version
}
// Outputs any RLP encoded data to the peer
func (p *Peer) QueueMessage(msg *wire.Msg) {
if atomic.LoadInt32(&p.connected) != 1 {
return
}
p.outputQueue <- msg
}
func (p *Peer) writeMessage(msg *wire.Msg) {
// Ignore the write if we're not connected
if atomic.LoadInt32(&p.connected) != 1 {
return
}
if !p.versionKnown {
switch msg.Type {
case wire.MsgHandshakeTy: // Ok
default: // Anything but ack is allowed
return
}
} else {
/*
if !p.statusKnown {
switch msg.Type {
case wire.MsgStatusTy: // Ok
default: // Anything but ack is allowed
return
}
}
*/
}
peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
err := wire.WriteMessage(p.conn, msg)
if err != nil {
peerlogger.Debugln(" Can't send message:", err)
// Stop the client if there was an error writing to it
p.Stop()
return
}
}
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
pingTimer := time.NewTicker(pingPongTimer)
serviceTimer := time.NewTicker(10 * time.Second)
out:
for {
skip:
select {
// Main message queue. All outbound messages are processed through here
case msg := <-p.outputQueue:
if !p.statusKnown {
switch msg.Type {
case wire.MsgTxTy, wire.MsgGetBlockHashesTy, wire.MsgBlockHashesTy, wire.MsgGetBlocksTy, wire.MsgBlockTy:
break skip
}
}
switch msg.Type {
case wire.MsgGetBlockHashesTy:
p.lastHashRequestedAt = time.Now()
}
p.writeMessage(msg)
p.lastSend = time.Now()
// Ping timer
case <-pingTimer.C:
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
p.pingStartTime = time.Now()
// Service timer takes care of peer broadcasting, transaction
// posting or block posting
case <-serviceTimer.C:
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
case <-p.quit:
// Break out of the for loop if a quit message is posted
break out
}
}
clean:
// This loop is for draining the output queue and anybody waiting for us
for {
select {
case <-p.outputQueue:
// TODO
default:
break clean
}
}
}
func formatMessage(msg *wire.Msg) (ret string) {
ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
/*
XXX Commented out because I need the log level here to determine
if i should or shouldn't generate this message
*/
/*
switch msg.Type {
case wire.MsgPeersTy:
ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
case wire.MsgBlockTy:
b1, b2 := chain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
case wire.MsgBlockHashesTy:
h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
}
*/
return
}
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() {
for atomic.LoadInt32(&p.disconnect) == 0 {
// HMM?
time.Sleep(50 * time.Millisecond)
// Wait for a message from the peer
msgs, err := wire.ReadMessages(p.conn)
if err != nil {
peerlogger.Debugln(err)
}
for _, msg := range msgs {
peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
switch msg.Type {
case wire.MsgHandshakeTy:
// Version message
p.handleHandshake(msg)
//if p.caps.IsCap(CapPeerDiscTy) {
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
//}
case wire.MsgDiscTy:
p.Stop()
peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
case wire.MsgPingTy:
// Respond back with pong
p.QueueMessage(wire.NewMessage(wire.MsgPongTy, ""))
case wire.MsgPongTy:
// If we received a pong back from a peer we set the
// last pong so the peer handler knows this peer is still
// active.
p.lastPong = time.Now().Unix()
p.pingTime = time.Since(p.pingStartTime)
case wire.MsgTxTy:
// If the message was a transaction queue the transaction
// in the TxPool where it will undergo validation and
// processing when a new block is found
for i := 0; i < msg.Data.Len(); i++ {
tx := types.NewTransactionFromValue(msg.Data.Get(i))
err := p.ethereum.TxPool().Add(tx)
if err != nil {
peerlogger.Infoln(err)
} else {
peerlogger.Infof("tx OK (%x)\n", tx.Hash()[0:4])
}
}
case wire.MsgGetPeersTy:
// Peer asked for list of connected peers
//p.pushPeers()
case wire.MsgPeersTy:
// Received a list of peers (probably because MsgGetPeersTy was send)
data := msg.Data
// Create new list of possible peers for the ethereum to process
peers := make([]string, data.Len())
// Parse each possible peer
for i := 0; i < data.Len(); i++ {
value := data.Get(i)
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
}
// Connect to the list of peers
p.ethereum.ProcessPeerList(peers)
case wire.MsgStatusTy:
// Handle peer's status msg
p.handleStatus(msg)
}
// TMP
if p.statusKnown {
switch msg.Type {
case wire.MsgGetBlockHashesTy:
if msg.Data.Len() < 2 {
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
}
hash := msg.Data.Get(0).Bytes()
amount := msg.Data.Get(1).Uint()
hashes := p.ethereum.ChainManager().GetChainHashesFromHash(hash, amount)
p.QueueMessage(wire.NewMessage(wire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
case wire.MsgGetBlocksTy:
// Limit to max 300 blocks
max := int(math.Min(float64(msg.Data.Len()), 300.0))
var blocks []interface{}
for i := 0; i < max; i++ {
hash := msg.Data.Get(i).Bytes()
block := p.ethereum.ChainManager().GetBlock(hash)
if block != nil {
blocks = append(blocks, block.Value().Raw())
}
}
p.QueueMessage(wire.NewMessage(wire.MsgBlockTy, blocks))
case wire.MsgBlockHashesTy:
p.catchingUp = true
blockPool := p.ethereum.blockPool
foundCommonHash := false
p.lastHashAt = time.Now()
it := msg.Data.NewIterator()
for it.Next() {
hash := it.Value().Bytes()
p.lastReceivedHash = hash
if blockPool.HasCommonHash(hash) {
foundCommonHash = true
break
}
blockPool.AddHash(hash, p)
}
if !foundCommonHash {
p.FetchHashes()
} else {
peerlogger.Infof("Found common hash (%x...)\n", p.lastReceivedHash[0:4])
p.doneFetchingHashes = true
}
case wire.MsgBlockTy:
p.catchingUp = true
blockPool := p.ethereum.blockPool
it := msg.Data.NewIterator()
for it.Next() {
block := types.NewBlockFromRlpValue(it.Value())
blockPool.Add(block, p)
p.lastBlockReceived = time.Now()
}
case wire.MsgNewBlockTy:
var (
blockPool = p.ethereum.blockPool
block = types.NewBlockFromRlpValue(msg.Data.Get(0))
td = msg.Data.Get(1).BigInt()
)
if td.Cmp(blockPool.td) > 0 {
p.ethereum.blockPool.AddNew(block, p)
}
}
}
}
}
p.Stop()
}
func (self *Peer) FetchBlocks(hashes [][]byte) {
if len(hashes) > 0 {
peerlogger.Debugf("Fetching blocks (%d)\n", len(hashes))
self.QueueMessage(wire.NewMessage(wire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
}
}
func (self *Peer) FetchHashes() bool {
blockPool := self.ethereum.blockPool
return blockPool.FetchHashes(self)
}
func (self *Peer) FetchingHashes() bool {
return !self.doneFetchingHashes
}
// General update method
func (self *Peer) update() {
serviceTimer := time.NewTicker(100 * time.Millisecond)
out:
for {
select {
case <-serviceTimer.C:
if self.IsCap("eth") {
var (
sinceBlock = time.Since(self.lastBlockReceived)
)
if sinceBlock > 5*time.Second {
self.catchingUp = false
}
}
case <-self.quit:
break out
}
}
serviceTimer.Stop()
}
func (p *Peer) Start() {
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
if p.inbound {
p.host, p.port = packAddr(peerHost, peerPort)
} else {
p.host, p.port = packAddr(servHost, servPort)
}
err := p.pushHandshake()
if err != nil {
peerlogger.Debugln("Peer can't send outbound version ack", err)
p.Stop()
return
}
go p.HandleOutbound()
// Run the inbound handler in a new goroutine
go p.HandleInbound()
// Run the general update handler
go p.update()
// Wait a few seconds for startup and then ask for an initial ping
time.Sleep(2 * time.Second)
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
p.pingStartTime = time.Now()
}
func (p *Peer) Stop() {
p.StopWithReason(DiscRequested)
}
func (p *Peer) StopWithReason(reason DiscReason) {
if atomic.AddInt32(&p.disconnect, 1) != 1 {
return
}
// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
p.ethereum.RemovePeer(p)
close(p.quit)
if atomic.LoadInt32(&p.connected) != 0 {
p.writeMessage(wire.NewMessage(wire.MsgDiscTy, reason))
p.conn.Close()
}
}
func (p *Peer) peersMessage() *wire.Msg {
outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
// Serialise each peer
for i, peer := range p.ethereum.InOutPeers() {
// Don't return localhost as valid peer
if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
outPeers[i] = peer.RlpData()
}
}
// Return the message to the peer with the known list of connected clients
return wire.NewMessage(wire.MsgPeersTy, outPeers)
}
// Pushes the list of outbound peers to the client when requested
func (p *Peer) pushPeers() {
p.QueueMessage(p.peersMessage())
}
func (self *Peer) pushStatus() {
msg := wire.NewMessage(wire.MsgStatusTy, []interface{}{
uint32(ProtocolVersion),
uint32(NetVersion),
self.ethereum.ChainManager().TD,
self.ethereum.ChainManager().CurrentBlock.Hash(),
self.ethereum.ChainManager().Genesis().Hash(),
})
self.QueueMessage(msg)
}
func (self *Peer) handleStatus(msg *wire.Msg) {
c := msg.Data
var (
//protoVersion = c.Get(0).Uint()
netVersion = c.Get(1).Uint()
td = c.Get(2).BigInt()
bestHash = c.Get(3).Bytes()
genesis = c.Get(4).Bytes()
)
if bytes.Compare(self.ethereum.ChainManager().Genesis().Hash(), genesis) != 0 {
loggerger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
return
}
if netVersion != NetVersion {
loggerger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
return
}
/*
if protoVersion != ProtocolVersion {
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
return
}
*/
// Get the td and last hash
self.td = td
self.bestHash = bestHash
self.lastReceivedHash = bestHash
self.statusKnown = true
// Compare the total TD with the blockchain TD. If remote is higher
// fetch hashes from highest TD node.
self.FetchHashes()
loggerger.Infof("Peer is [eth] capable. (TD = %v ~ %x)", self.td, self.bestHash)
}
func (p *Peer) pushHandshake() error {
pubkey := p.ethereum.KeyManager().PublicKey()
msg := wire.NewMessage(wire.MsgHandshakeTy, []interface{}{
P2PVersion, []byte(p.version), []interface{}{[]interface{}{"eth", ProtocolVersion}}, p.port, pubkey[1:],
})
p.QueueMessage(msg)
return nil
}
func (p *Peer) handleHandshake(msg *wire.Msg) {
c := msg.Data
var (
p2pVersion = c.Get(0).Uint()
clientId = c.Get(1).Str()
caps = c.Get(2)
port = c.Get(3).Uint()
pub = c.Get(4).Bytes()
)
// Check correctness of p2p protocol version
if p2pVersion != P2PVersion {
peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
p.Stop()
return
}
// Handle the pub key (validation, uniqueness)
if len(pub) == 0 {
peerlogger.Warnln("Pubkey required, not supplied in handshake.")
p.Stop()
return
}
// Self connect detection
pubkey := p.ethereum.KeyManager().PublicKey()
if bytes.Compare(pubkey[1:], pub) == 0 {
p.Stop()
return
}
// Check for blacklisting
for _, pk := range p.ethereum.blacklist {
if bytes.Compare(pk, pub) == 0 {
peerlogger.Debugf("Blacklisted peer tried to connect (%x...)\n", pubkey[0:4])
p.StopWithReason(DiscBadPeer)
return
}
}
usedPub := 0
// This peer is already added to the peerlist so we expect to find a double pubkey at least once
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
if bytes.Compare(pub, peer.pubkey) == 0 {
usedPub++
}
})
if usedPub > 0 {
peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
p.Stop()
return
}
p.pubkey = pub
// If this is an inbound connection send an ack back
if p.inbound {
p.port = uint16(port)
}
p.SetVersion(clientId)
p.versionKnown = true
p.ethereum.PushPeer(p)
p.ethereum.eventMux.Post(PeerListEvent{p.ethereum.Peers()})
p.protocolCaps = caps
it := caps.NewIterator()
var capsStrs []string
for it.Next() {
cap := it.Value().Get(0).Str()
ver := it.Value().Get(1).Uint()
switch cap {
case "eth":
if ver != ProtocolVersion {
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", ver)
continue
}
p.pushStatus()
}
capsStrs = append(capsStrs, fmt.Sprintf("%s/%d", cap, ver))
}
peerlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
peerlogger.Debugln(p)
}
func (self *Peer) IsCap(cap string) bool {
capsIt := self.protocolCaps.NewIterator()
for capsIt.Next() {
if capsIt.Value().Str() == cap {
return true
}
}
return false
}
func (self *Peer) Caps() *ethutil.Value {
return self.protocolCaps
}
func (p *Peer) String() string {
var strBoundType string
if p.inbound {
strBoundType = "inbound"
} else {
strBoundType = "outbound"
}
var strConnectType string
if atomic.LoadInt32(&p.disconnect) == 0 {
strConnectType = "connected"
} else {
strConnectType = "disconnected"
}
return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version)
}
func (p *Peer) RlpData() []interface{} {
return []interface{}{p.host, p.port, p.pubkey}
}
func packAddr(address, _port string) (host []byte, port uint16) {
p, _ := strconv.Atoi(_port)
port = uint16(p)
h := net.ParseIP(address)
if ip := h.To4(); ip != nil {
host = []byte(ip)
} else {
host = []byte(h)
}
return
}
func unpackAddr(value *ethutil.Value, p uint64) string {
host, _ := net.IP(value.Bytes()).MarshalText()
prt := strconv.Itoa(int(p))
return net.JoinHostPort(string(host), prt)
}

View File

@ -3,7 +3,7 @@ package pow
import "math/big" import "math/big"
type Block interface { type Block interface {
Diff() *big.Int Difficulty() *big.Int
HashNoNonce() []byte HashNoNonce() []byte
N() []byte N() []byte
} }

View File

@ -21,7 +21,7 @@ type EasyPow struct {
} }
func New() *EasyPow { func New() *EasyPow {
return &EasyPow{} return &EasyPow{turbo: true}
} }
func (pow *EasyPow) GetHashrate() int64 { func (pow *EasyPow) GetHashrate() int64 {
@ -35,7 +35,7 @@ func (pow *EasyPow) Turbo(on bool) {
func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte { func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce() hash := block.HashNoNonce()
diff := block.Diff() diff := block.Difficulty()
i := int64(0) i := int64(0)
start := time.Now().UnixNano() start := time.Now().UnixNano()
t := time.Now() t := time.Now()
@ -59,7 +59,7 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
} }
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes()) sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
if pow.verify(hash, diff, sha) { if verify(hash, diff, sha) {
return sha return sha
} }
} }
@ -72,7 +72,11 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
return nil return nil
} }
func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool { func (pow *EasyPow) Verify(block pow.Block) bool {
return Verify(block)
}
func verify(hash []byte, diff *big.Int, nonce []byte) bool {
sha := sha3.NewKeccak256() sha := sha3.NewKeccak256()
d := append(hash, nonce...) d := append(hash, nonce...)
@ -84,6 +88,6 @@ func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
return res.Cmp(verification) <= 0 return res.Cmp(verification) <= 0
} }
func (pow *EasyPow) Verify(block pow.Block) bool { func Verify(block pow.Block) bool {
return pow.verify(block.HashNoNonce(), block.Diff(), block.N()) return verify(block.HashNoNonce(), block.Difficulty(), block.N())
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
package ptrie package ptrie
import "fmt"
type FullNode struct { type FullNode struct {
trie *Trie trie *Trie
nodes [17]Node nodes [17]Node
@ -21,8 +23,10 @@ func (self *FullNode) Branches() []Node {
func (self *FullNode) Copy() Node { func (self *FullNode) Copy() Node {
nnode := NewFullNode(self.trie) nnode := NewFullNode(self.trie)
for i, node := range self.nodes { for i, node := range self.nodes {
if node != nil {
nnode.nodes[i] = node nnode.nodes[i] = node
} }
}
return nnode return nnode
} }
@ -56,6 +60,10 @@ func (self *FullNode) RlpData() interface{} {
} }
func (self *FullNode) set(k byte, value Node) { func (self *FullNode) set(k byte, value Node) {
if _, ok := value.(*ValueNode); ok && k != 16 {
fmt.Println(value, k)
}
self.nodes[int(k)] = value self.nodes[int(k)] = value
} }

View File

@ -14,7 +14,7 @@ type Iterator struct {
} }
func NewIterator(trie *Trie) *Iterator { func NewIterator(trie *Trie) *Iterator {
return &Iterator{trie: trie, Key: []byte{0}} return &Iterator{trie: trie, Key: make([]byte, 32)}
} }
func (self *Iterator) Next() bool { func (self *Iterator) Next() bool {

View File

@ -17,7 +17,7 @@ type Node interface {
func (self *ValueNode) String() string { return self.fstring("") } func (self *ValueNode) String() string { return self.fstring("") }
func (self *FullNode) String() string { return self.fstring("") } func (self *FullNode) String() string { return self.fstring("") }
func (self *ShortNode) String() string { return self.fstring("") } func (self *ShortNode) String() string { return self.fstring("") }
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%s ", self.data) } func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.data) }
func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) } func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) }
// Full node // Full node
@ -36,5 +36,5 @@ func (self *FullNode) fstring(ind string) string {
// Short node // Short node
func (self *ShortNode) fstring(ind string) string { func (self *ShortNode) fstring(ind string) string {
return fmt.Sprintf("[ %s: %v ] ", self.key, self.value.fstring(ind+" ")) return fmt.Sprintf("[ %x: %v ] ", self.key, self.value.fstring(ind+" "))
} }

Some files were not shown because too many files have changed in this diff Show More