Merge remote-tracking branch 'upstream/develop' into bzz

This commit is contained in:
zelig 2015-01-09 17:53:53 +00:00
commit 1f7ec3539f
213 changed files with 14373 additions and 9936 deletions

12
.mailmap Normal file
View file

@ -0,0 +1,12 @@
Jeffrey Wilcke <jeffrey@ethereum.org>
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
Viktor Trón <viktor.tron@gmail.com>
Joseph Goulden <joegoulden@gmail.com>
Nick Savers <nicksavers@gmail.com>
Maran Hidskes <maran.hidskes@gmail.com>

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,10 +9,11 @@ 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.
Current state: Proof of Concept 0.7 Current state: Proof of Concept 0.8
Ethereum is currently in its testing phase. Ethereum is currently in its testing phase.

Binary file not shown.

Binary file not shown.

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,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -58,6 +61,10 @@ var (
DumpHash string DumpHash string
DumpNumber int DumpNumber int
VmType int VmType int
ImportChain string
SHH bool
Dial bool
PrintVersion bool
) )
// flags specific to cli client // flags specific to cli client
@ -87,12 +94,14 @@ func Init() {
flag.StringVar(&OutboundPort, "port", "30303", "listening port") flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)") flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP") flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
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(&SHH, "shh", true, "whisper protocol (on)")
flag.BoolVar(&Dial, "dial", true, "dial out connections (on)")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
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")
@ -104,6 +113,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 given 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")
@ -111,6 +121,7 @@ func Init() {
flag.BoolVar(&StartMining, "mine", false, "start dagger mining") flag.BoolVar(&StartMining, "mine", false, "start dagger mining")
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console") flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
flag.BoolVar(&PrintVersion, "version", false, "prints version number")
flag.Parse() flag.Parse()

View file

@ -1,36 +1,42 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( 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"
"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/logger"
"github.com/ethereum/go-ethereum/state"
) )
const ( const (
ClientIdentifier = "Ethereum(G)" ClientIdentifier = "Ethereum(G)"
Version = "0.7.9" Version = "0.8.1"
) )
var clilogger = logger.NewLogger("CLI") var clilogger = logger.NewLogger("CLI")
@ -38,40 +44,44 @@ 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
Init() // parsing command line Init() // parsing command line
// If the difftool option is selected ignore all other log output if PrintVersion {
if DiffTool || Dump { printVersion()
LogLevel = 0 return
} }
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
ethutil.Config.Diff = DiffTool
ethutil.Config.DiffType = DiffType
utils.InitDataDir(Datadir) ethereum, err := eth.New(&eth.Config{
Name: ClientIdentifier,
Version: Version,
KeyStore: KeyStore,
DataDir: Datadir,
LogFile: LogFile,
LogLevel: LogLevel,
Identifier: Identifier,
MaxPeers: MaxPeer,
Port: OutboundPort,
NATType: PMPGateway,
PMPGateway: PMPGateway,
KeyRing: KeyRing,
Shh: SHH,
Dial: Dial,
})
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
db := utils.NewDatabase()
err := utils.DBSanityCheck(db)
if err != nil { if err != nil {
fmt.Println(err) clilogger.Fatalln(err)
os.Exit(1)
} }
keyManager := utils.NewKeyManager(KeyStore, Datadir, db) utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
if Dump { if Dump {
var block *types.Block var block *types.Block
@ -93,30 +103,27 @@ 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()) statedb := state.New(block.Root(), ethereum.Db())
fmt.Printf("%s\n", statedb.Dump())
fmt.Println(block) fmt.Println(block)
os.Exit(0) return
}
if ShowGenesis {
utils.ShowGenesis(ethereum)
} }
if StartMining { if StartMining {
utils.StartMining(ethereum) utils.StartMining(ethereum)
} }
// better reworked as cases if len(ImportChain) > 0 {
if StartJsConsole { start := time.Now()
InitJsConsole(ethereum) err := utils.ImportChain(ethereum, ImportChain)
} else if len(InputFile) > 0 { if err != nil {
ExecJsFile(ethereum, InputFile) clilogger.Infoln(err)
}
clilogger.Infoln("import done in", time.Since(start))
return
} }
if StartRpc { if StartRpc {
@ -129,7 +136,21 @@ func main() {
utils.StartEthereum(ethereum, UseSeed) utils.StartEthereum(ethereum, UseSeed)
if StartJsConsole {
InitJsConsole(ethereum)
} else if len(InputFile) > 0 {
ExecJsFile(ethereum, InputFile)
}
// this blocks the thread // this blocks the thread
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
logger.Flush() }
func printVersion() {
fmt.Printf(`%v %v
PV=%d
GOOS=%s
GO=%s
GOPATH=%s
GOROOT=%s
`, ClientIdentifier, Version, eth.ProtocolVersion, runtime.GOOS, runtime.Version(), os.Getenv("GOPATH"), runtime.GOROOT())
} }

View file

@ -86,11 +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 {

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,6 +4,7 @@
# 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~
@ -11,4 +12,7 @@
*/**/.DS_Store */**/.DS_Store
ethereum/ethereum ethereum/ethereum
ethereal/ethereal 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

View file

@ -17,8 +17,6 @@
/** /**
* @authors: * @authors:
* Jeffrey Wilcke <i@jev.io> * Jeffrey Wilcke <i@jev.io>
* @date 2014
*
*/ */
package main package main
@ -26,11 +24,13 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strings" "strings"
"github.com/ethereum/go-ethereum/ethdb"
"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/tests/helper" "github.com/ethereum/go-ethereum/tests/helper"
@ -43,8 +43,8 @@ type Account struct {
Storage map[string]string Storage map[string]string
} }
func StateObjectFromAccount(addr string, account Account) *state.StateObject { func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject {
obj := state.NewStateObject(ethutil.Hex2Bytes(addr)) obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db)
obj.SetBalance(ethutil.Big(account.Balance)) obj.SetBalance(ethutil.Big(account.Balance))
if ethutil.IsHex(account.Code) { if ethutil.IsHex(account.Code) {
@ -66,19 +66,20 @@ type VmTest struct {
Pre map[string]Account Pre map[string]Account
} }
func RunVmTest(js string) (failed int) { func RunVmTest(r io.Reader) (failed int) {
tests := make(map[string]VmTest) tests := make(map[string]VmTest)
data, _ := ioutil.ReadAll(strings.NewReader(js)) data, _ := ioutil.ReadAll(r)
err := json.Unmarshal(data, &tests) err := json.Unmarshal(data, &tests)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
for name, test := range tests { for name, test := range tests {
state := state.New(helper.NewTrie()) db, _ := ethdb.NewMemDatabase()
state := state.New(nil, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(addr, account) obj := StateObjectFromAccount(db, addr, account)
state.SetStateObject(obj) state.SetStateObject(obj)
} }
@ -95,10 +96,15 @@ func RunVmTest(js string) (failed int) {
failed = 1 failed = 1
} }
gexp := ethutil.Big(test.Gas) if len(test.Gas) == 0 && err == nil {
if gexp.Cmp(gas) != 0 { log.Printf("0 gas indicates error but no error given by VM")
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
failed = 1 failed = 1
} else {
gexp := ethutil.Big(test.Gas)
if gexp.Cmp(gas) != 0 {
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
failed = 1
}
} }
for addr, account := range test.Post { for addr, account := range test.Post {
@ -120,9 +126,10 @@ func RunVmTest(js string) (failed int) {
func main() { func main() {
helper.Logger.SetLogLevel(5) helper.Logger.SetLogLevel(5)
if len(os.Args) == 1 {
log.Fatalln("no json supplied")
}
os.Exit(RunVmTest(os.Args[1])) if len(os.Args) > 1 {
os.Exit(RunVmTest(strings.NewReader(os.Args[1])))
} else {
os.Exit(RunVmTest(os.Stdin))
}
} }

View file

@ -17,8 +17,6 @@
/** /**
* @authors * @authors
* Jeffrey Wilcke <i@jev.io> * Jeffrey Wilcke <i@jev.io>
* @date 2014
*
*/ */
package main package main
@ -38,7 +36,6 @@ import (
"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/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 +62,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(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))
@ -133,6 +130,12 @@ func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) Depth() int { return 0 } func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) []byte {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash()
}
return nil
}
func (self *VMEnv) AddLog(log state.Log) { func (self *VMEnv) AddLog(log state.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
@ -141,24 +144,22 @@ 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.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
ret, err := exe.Call(addr, caller) ret, err := exe.Call(addr, caller)
self.Gas = exe.Gas self.Gas = exe.Gas
return ret, err return ret, err
} }
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) CallCode(caller vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(caller.Address(), data, gas, price, value) exe := self.vm(caller.Address(), data, gas, price, value)
return exe.Call(addr, caller) return exe.Call(addr, caller)
} }
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { func (self *VMEnv) Create(caller vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
return exe.Create(caller) return exe.Create(caller)
} }

View file

@ -1,18 +0,0 @@
# Ethereum JavaScript API
This is the Ethereum compatible JavaScript API using `Promise`s
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec.
For an example see `index.html`.
**Please note this repo is in it's early stage.**
If you'd like to run a WebSocket ethereum node check out
[go-ethereum](https://github.com/ethereum/go-ethereum).
To install ethereum and spawn a node:
```
go get github.com/ethereum/go-ethereum/ethereum
ethereum -ws -loglevel=4
```

View file

@ -1,70 +0,0 @@
(function () {
var HttpRpcProvider = function (host) {
this.handlers = [];
this.host = host;
};
function formatJsonRpcObject(object) {
return {
jsonrpc: '2.0',
method: object.call,
params: object.args,
id: object._id
}
};
function formatJsonRpcMessage(message) {
var object = JSON.parse(message);
return {
_id: object.id,
data: object.result
};
};
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
var data = formatJsonRpcObject(payload);
var request = new XMLHttpRequest();
request.open("POST", this.host, true);
request.send(JSON.stringify(data));
request.onreadystatechange = function () {
if (request.readyState === 4 && cb) {
cb(request);
}
}
};
HttpRpcProvider.prototype.send = function (payload) {
var self = this;
this.sendRequest(payload, function (request) {
self.handlers.forEach(function (handler) {
handler.call(self, formatJsonRpcMessage(request.responseText));
});
});
};
HttpRpcProvider.prototype.poll = function (payload, id) {
var self = this;
this.sendRequest(payload, function (request) {
var parsed = JSON.parse(request.responseText);
if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) {
return;
}
self.handlers.forEach(function (handler) {
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
});
});
};
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
set: function (handler) {
this.handlers.push(handler);
}
});
if (typeof(web3) !== "undefined" && web3.providers !== undefined) {
web3.providers.HttpRpcProvider = HttpRpcProvider;
}
})();

View file

@ -1,33 +0,0 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="websocket.js"></script>
<script type="text/javascript" src="qt.js"></script>
<script type="text/javascript" src="httprpc.js"></script>
<script type="text/javascript">
function registerName() {
var name = document.querySelector("#name").value;
name = web3.fromAscii(name);
var eth = web3.eth;
eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) {
document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through.";
}, function(err) {
console.log(err);
});
}
</script>
</head>
<body>
<h1>std::name_reg</h1>
<input type="text" id="name"></input>
<input type="submit" onClick="registerName();"></input>
<div id="result"></div>
</body>
</html>

View file

@ -1,432 +0,0 @@
(function(window) {
function isPromise(o) {
return o instanceof Promise
}
function flattenPromise (obj) {
if (obj instanceof Promise) {
return Promise.resolve(obj);
}
if (obj instanceof Array) {
return new Promise(function (resolve) {
var promises = obj.map(function (o) {
return flattenPromise(o);
});
return Promise.all(promises).then(function (res) {
for (var i = 0; i < obj.length; i++) {
obj[i] = res[i];
}
resolve(obj);
});
});
}
if (obj instanceof Object) {
return new Promise(function (resolve) {
var keys = Object.keys(obj);
var promises = keys.map(function (key) {
return flattenPromise(obj[key]);
});
return Promise.all(promises).then(function (res) {
for (var i = 0; i < keys.length; i++) {
obj[keys[i]] = res[i];
}
resolve(obj);
});
});
}
return Promise.resolve(obj);
};
var ethMethods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber";
};
var transactionCall = function (args) {
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
};
var uncleCall = function (args) {
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
};
var methods = [
{ name: 'balanceAt', call: 'balanceAt' },
{ name: 'stateAt', call: 'stateAt' },
{ name: 'countAt', call: 'countAt'},
{ name: 'codeAt', call: 'codeAt' },
{ name: 'transact', call: 'transact' },
{ name: 'call', call: 'call' },
{ name: 'block', call: blockCall },
{ name: 'transaction', call: transactionCall },
{ name: 'uncle', call: uncleCall },
{ name: 'compile', call: 'compile' }
];
return methods;
};
var ethProperties = function () {
return [
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' },
{ name: 'listening', getter: 'listening', setter: 'setListening' },
{ name: 'mining', getter: 'mining', setter: 'setMining' },
{ name: 'gasPrice', getter: 'gasPrice' },
{ name: 'account', getter: 'account' },
{ name: 'accounts', getter: 'accounts' },
{ name: 'peerCount', getter: 'peerCount' },
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' },
{ name: 'number', getter: 'number'}
];
};
var dbMethods = function () {
return [
{ name: 'put', call: 'put' },
{ name: 'get', call: 'get' },
{ name: 'putString', call: 'putString' },
{ name: 'getString', call: 'getString' }
];
};
var shhMethods = function () {
return [
{ name: 'post', call: 'post' },
{ name: 'newIdentity', call: 'newIdentity' },
{ name: 'haveIdentity', call: 'haveIdentity' },
{ name: 'newGroup', call: 'newGroup' },
{ name: 'addToGroup', call: 'addToGroup' }
];
};
var ethWatchMethods = function () {
var newFilter = function (args) {
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter';
};
return [
{ name: 'newFilter', call: newFilter },
{ name: 'uninstallFilter', call: 'uninstallFilter' },
{ name: 'getMessages', call: 'getMessages' }
];
};
var shhWatchMethods = function () {
return [
{ name: 'newFilter', call: 'shhNewFilter' },
{ name: 'uninstallFilter', call: 'shhUninstallFilter' },
{ name: 'getMessage', call: 'shhGetMessages' }
];
};
var setupMethods = function (obj, methods) {
methods.forEach(function (method) {
obj[method.name] = function () {
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
var call = typeof method.call === "function" ? method.call(args) : method.call;
return {call: call, args: args};
}).then(function (request) {
return new Promise(function (resolve, reject) {
web3.provider.send(request, function (result) {
//if (result || typeof result === "boolean") {
resolve(result);
return;
//}
//reject(result);
});
});
}).catch(function( err) {
console.error(err);
});
};
});
};
var setupProperties = function (obj, properties) {
properties.forEach(function (property) {
var proto = {};
proto.get = function () {
return new Promise(function(resolve, reject) {
web3.provider.send({call: property.getter}, function(result) {
resolve(result);
});
});
};
if (property.setter) {
proto.set = function (val) {
return flattenPromise([val]).then(function (args) {
return new Promise(function (resolve) {
web3.provider.send({call: property.setter, args: args}, function (result) {
resolve(result);
});
});
}).catch(function (err) {
console.error(err);
});
}
}
Object.defineProperty(obj, property.name, proto);
});
};
var web3 = {
_callbacks: {},
_events: {},
providers: {},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i)
if(code == 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
},
toDecimal: function (val) {
return parseInt(val, 16);
},
fromAscii: function(str, pad) {
pad = pad === undefined ? 32 : pad;
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return hex
},
eth: {
prototype: Object(),
watch: function (params) {
return new Filter(params, ethWatch);
},
},
db: {
prototype: Object()
},
shh: {
prototype: Object(),
watch: function (params) {
return new Filter(params, shhWatch);
}
},
on: function(event, id, cb) {
if(web3._events[event] === undefined) {
web3._events[event] = {};
}
web3._events[event][id] = cb;
return this
},
off: function(event, id) {
if(web3._events[event] !== undefined) {
delete web3._events[event][id];
}
return this
},
trigger: function(event, id, data) {
var callbacks = web3._events[event];
if (!callbacks || !callbacks[id]) {
return;
}
var cb = callbacks[id];
cb(data);
},
};
var eth = web3.eth;
setupMethods(eth, ethMethods());
setupProperties(eth, ethProperties());
setupMethods(web3.db, dbMethods());
setupMethods(web3.shh, shhMethods());
var ethWatch = {
changed: 'changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shhChanged'
};
setupMethods(shhWatch, shhWatchMethods());
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
ProviderManager.prototype.sendQueued = function() {
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
web3.provider = new ProviderManager();
web3.setProvider = function(provider) {
provider.onmessage = messageHandler;
web3.provider.set(provider);
web3.provider.sendQueued();
};
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
Filter.prototype.arrived = function(callback) {
this.changed(callback);
}
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
function messageHandler(data) {
if(data._event !== undefined) {
web3.trigger(data._event, data._id, data.data);
return;
}
if(data._id) {
var cb = web3._callbacks[data._id];
if (cb) {
cb.call(this, data.data)
delete web3._callbacks[data._id];
}
}
}
/*
// Install default provider
if(!web3.provider.installed()) {
var sock = new web3.WebSocket("ws://localhost:40404/eth");
web3.setProvider(sock);
}
*/
window.web3 = web3;
})(this);

View file

@ -1,27 +0,0 @@
(function() {
var QtProvider = function() {
this.handlers = [];
var self = this;
navigator.qt.onmessage = function (message) {
self.handlers.forEach(function (handler) {
handler.call(self, JSON.parse(message.data));
});
}
};
QtProvider.prototype.send = function(payload) {
navigator.qt.postMessage(JSON.stringify(payload));
};
Object.defineProperty(QtProvider.prototype, "onmessage", {
set: function(handler) {
this.handlers.push(handler);
},
});
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
web3.providers.QtProvider = QtProvider;
}
})();

View file

@ -1,51 +0,0 @@
(function() {
var WebSocketProvider = function(host) {
// onmessage handlers
this.handlers = [];
// queue will be filled with messages if send is invoked before the ws is ready
this.queued = [];
this.ready = false;
this.ws = new WebSocket(host);
var self = this;
this.ws.onmessage = function(event) {
for(var i = 0; i < self.handlers.length; i++) {
self.handlers[i].call(self, JSON.parse(event.data), event)
}
};
this.ws.onopen = function() {
self.ready = true;
for(var i = 0; i < self.queued.length; i++) {
// Resend
self.send(self.queued[i]);
}
};
};
WebSocketProvider.prototype.send = function(payload) {
if(this.ready) {
var data = JSON.stringify(payload);
this.ws.send(data);
} else {
this.queued.push(payload);
}
};
WebSocketProvider.prototype.onMessage = function(handler) {
this.handlers.push(handler);
};
WebSocketProvider.prototype.unload = function() {
this.ws.close();
};
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
set: function(provider) { this.onMessage(provider); }
});
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
web3.providers.WebSocketProvider = WebSocketProvider;
}
})();

View file

@ -1,312 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
// Main Ethereum library
window.eth = {
prototype: Object(),
_callbacks: {},
_onCallbacks: {},
test: function() {
var t = undefined;
postData({call: "test"})
navigator.qt.onmessage = function(d) {console.log("onmessage called"); t = d; }
for(;;) {
if(t !== undefined) {
return t
}
}
},
mutan: function(code, cb) {
postData({call: "mutan", args: [code]}, cb)
},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i)
if(code == 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str;
},
fromAscii: function(str, pad) {
if(pad === undefined) {
pad = 32
}
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return hex
},
// Retrieve block
//
// Either supply a number or a string. Type is determent for the lookup method
// string - Retrieves the block by looking up the hash
// number - Retrieves the block by looking up the block number
getBlock: function(numberOrHash, cb) {
var func;
if(typeof numberOrHash == "string") {
func = "getBlockByHash";
} else {
func = "getBlockByNumber";
}
postData({call: func, args: [numberOrHash]}, cb);
},
// Create transaction
//
// Transact between two state objects
transact: function(params, cb) {
if(params === undefined) {
params = {};
}
if(params.endowment !== undefined)
params.value = params.endowment;
if(params.code !== undefined)
params.data = params.code;
// Make sure everything is string
var fields = ["to", "from", "value", "gas", "gasPrice"];
for(var i = 0; i < fields.length; i++) {
if(params[fields[i]] === undefined) {
params[fields[i]] = "";
}
params[fields[i]] = params[fields[i]].toString();
}
var data;
if(typeof params.data === "object") {
data = "";
for(var i = 0; i < params.data.length; i++) {
data += params.data[i]
}
} else {
data = params.data;
}
postData({call: "transact", args: [params.from, params.to, params.value, params.gas, params.gasPrice, "0x"+data]}, cb);
},
getMessages: function(filter, cb) {
postData({call: "messages", args: [filter]}, cb);
},
getStorageAt: function(address, storageAddress, cb) {
postData({call: "getStorage", args: [address, storageAddress]}, cb);
},
getEachStorageAt: function(address, cb){
postData({call: "getEachStorage", args: [address]}, cb);
},
getKey: function(cb) {
postData({call: "getKey"}, cb);
},
getTxCountAt: function(address, cb) {
postData({call: "getTxCountAt", args: [address]}, cb);
},
getIsMining: function(cb){
postData({call: "getIsMining"}, cb)
},
getIsListening: function(cb){
postData({call: "getIsListening"}, cb)
},
getCoinBase: function(cb){
postData({call: "getCoinBase"}, cb);
},
getPeerCount: function(cb){
postData({call: "getPeerCount"}, cb);
},
getBalanceAt: function(address, cb) {
postData({call: "getBalance", args: [address]}, cb);
},
getTransactionsFor: function(address, cb) {
postData({call: "getTransactionsFor", args: [address]}, cb);
},
getSecretToAddress: function(sec, cb) {
postData({call: "getSecretToAddress", args: [sec]}, cb);
},
/*
watch: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.on(ev, cb)
postData({call: "watch", args: [address, storageAddrOrCb]});
},
disconnect: function(address, storageAddrOrCb, cb) {
var ev;
if(cb === undefined) {
cb = storageAddrOrCb;
storageAddrOrCb = "";
ev = "object:"+address;
} else {
ev = "storage:"+address+":"+storageAddrOrCb;
}
eth.off(ev, cb)
postData({call: "disconnect", args: [address, storageAddrOrCb]});
},
*/
watch: function(options) {
var filter = new Filter(options);
filter.number = newWatchNum().toString()
postData({call: "watch", args: [options, filter.number]})
return filter;
},
set: function(props) {
postData({call: "set", args: props});
},
on: function(event, cb) {
if(eth._onCallbacks[event] === undefined) {
eth._onCallbacks[event] = [];
}
eth._onCallbacks[event].push(cb);
return this
},
off: function(event, cb) {
if(eth._onCallbacks[event] !== undefined) {
var callbacks = eth._onCallbacks[event];
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] === cb) {
delete callbacks[i];
}
}
}
return this
},
trigger: function(event, data) {
var callbacks = eth._onCallbacks[event];
if(callbacks !== undefined) {
for(var i = 0; i < callbacks.length; i++) {
// Figure out whether the returned data was an array
// array means multiple return arguments (multiple params)
if(data instanceof Array) {
callbacks[i].apply(this, data);
} else {
callbacks[i].call(this, data);
}
}
}
},
}
var Filter = function(options) {
this.options = options;
};
Filter.prototype.changed = function(callback) {
// Register the watched:<number>. Qml will call the appropriate event if anything
// interesting happens in the land of Go.
eth.on("watched:"+this.number, callback)
}
Filter.prototype.getMessages = function(cb) {
return eth.getMessages(this.options, cb)
}
var watchNum = 0;
function newWatchNum() {
return watchNum++;
}
function postData(data, cb) {
data._seed = Math.floor(Math.random() * 1000000)
if(cb) {
eth._callbacks[data._seed] = cb;
}
if(data.args === undefined) {
data.args = [];
}
navigator.qt.postMessage(JSON.stringify(data));
}
navigator.qt.onmessage = function(ev) {
var data = JSON.parse(ev.data)
if(data._event !== undefined) {
eth.trigger(data._event, data.data);
} else {
if(data._seed) {
var cb = eth._callbacks[data._seed];
if(cb) {
cb.call(this, data.data)
// Remove the "trigger" callback
delete eth._callbacks[ev._seed];
}
}
}
}
eth.on("chain:changed", function() {
})
eth.on("messages", { /* filters */}, function(messages){
})
eth.on("pending:changed", function() {
})

View file

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

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

@ -0,0 +1,18 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
*.swp
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store
ethereum/ethereum
ethereal/ethereal
example/js
node_modules
bower_components
npm-debug.log

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
}

View file

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

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

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/>.

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

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"
]
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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,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 = parseInt(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,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);
});

View file

@ -0,0 +1,104 @@
#!/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, ugly) {
var result = 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');
if (ugly) {
result = result.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
});
}
return result.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('clean', ['lint'], function(cb) {
del([ DEST ], cb);
});
gulp.task('lint', function(){
return gulp.src(['./*.js', './lib/*.js'])
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
gulp.task('build', ['clean'], function () {
return build('index', 'ethereum', true);
});
gulp.task('buildDev', ['clean'], function () {
return build('index', 'ethereum', false);
});
gulp.task('uglify', ['build'], function(){
return uglifyFile('ethereum');
});
gulp.task('uglify', ['buildDev'], function(){
return uglifyFile('ethereum');
});
gulp.task('watch', function() {
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
});
gulp.task('release', ['bower', 'lint', 'build', 'uglify']);
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']);
gulp.task('default', ['dev']);

View file

@ -0,0 +1,8 @@
var web3 = require('./lib/web3');
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;

View file

@ -0,0 +1,267 @@
/*
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: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
}
// 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 new Array(chars - string.length + 1).join("0") + string;
};
var calcBitPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value) / 8;
};
var calcBytePadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value);
};
var calcRealPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
var sizes = value.split('x');
for (var padding = 0, i = 0; i < sizes; i++) {
padding += (sizes[i] / 8);
}
return padding;
};
var setupInputTypes = function () {
var prefixedType = function (prefix, calcPadding) {
return function (type, value) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return false;
}
var padding = calcPadding(type, expected);
if (typeof value === "number")
value = value.toString(16);
else if (typeof value === "string")
value = web3.toHex(value);
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', calcBitPadding),
prefixedType('int', calcBitPadding),
prefixedType('hash', calcBitPadding),
prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding),
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, calcPadding) {
return function (type) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return -1;
}
var padding = calcPadding(type, expected);
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;
};
var formatString = function (value) {
return web3.toAscii(value);
};
return [
{ padding: prefixedType('uint', calcBitPadding), format: formatInt },
{ padding: prefixedType('int', calcBitPadding), format: formatInt },
{ padding: prefixedType('hash', calcBitPadding), format: formatHash },
{ padding: prefixedType('string', calcBytePadding), format: formatString },
{ padding: prefixedType('real', calcRealPadding), format: formatInt },
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt },
{ 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;
};
if (typeof(module) !== "undefined") {
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: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line
var web3 = require('./web3'); // 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);
}
});
if (typeof(module) !== "undefined")
module.exports = AutoProvider;

View file

@ -0,0 +1,66 @@
/*
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: is these line is supposed to be here?
if (process.env.NODE_ENV !== '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;
};
if (typeof(module) !== "undefined")
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: is these line is supposed to be here?
if (process.env.NODE_ENV !== '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);
}
});
if (typeof(module) !== "undefined")
module.exports = HttpRpcProvider;

View file

@ -0,0 +1,46 @@
/*
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);
}
});
if (typeof(module) !== "undefined")
module.exports = QtProvider;

View file

@ -0,0 +1,509 @@
/*
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: {},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
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);
var replaceFunction = function($0, $1, $2) {
return $1 + ',' + $2;
};
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
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];
}
}
}
if (typeof(module) !== "undefined")
module.exports = web3;

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: is these line is supposed to be here?
if (process.env.NODE_ENV !== '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); }
});
if (typeof(module) !== "undefined")
module.exports = WebSocketProvider;

View file

@ -0,0 +1,67 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.6",
"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

@ -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("shh_changed", id, message)
} }
Item { Item {
@ -105,7 +109,8 @@ Rectangle {
leftMargin: 5 leftMargin: 5
rightMargin: 5 rightMargin: 5
} }
text: "http://etherian.io" //text: "http://etherian.io"
text: webview.url;
id: uriNav id: uriNav
y: parent.height / 2 - this.height / 2 y: parent.height / 2 - this.height / 2
@ -147,6 +152,12 @@ Rectangle {
window.open(request.url.toString()); window.open(request.url.toString());
} }
function injectJs(js) {
webview.experimental.navigatorQtObjectEnabled = true;
webview.experimental.evaluateJavaScript(js)
webview.experimental.javascriptEnabled = true;
}
function sendMessage(data) { function sendMessage(data) {
webview.experimental.postMessage(JSON.stringify(data)) webview.experimental.postMessage(JSON.stringify(data))
} }
@ -155,8 +166,7 @@ Rectangle {
experimental.preferences.javascriptEnabled: true experimental.preferences.javascriptEnabled: true
experimental.preferences.navigatorQtObjectEnabled: true experimental.preferences.navigatorQtObjectEnabled: true
experimental.preferences.developerExtrasEnabled: true experimental.preferences.developerExtrasEnabled: true
//experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"]
experimental.userScripts: ["../ext/q.js", "../ext/eth.js/main.js", "../ext/eth.js/qt.js", "../ext/setup.js"]
experimental.onMessageReceived: { experimental.onMessageReceived: {
console.log("[onMessageReceived]: ", message.data) console.log("[onMessageReceived]: ", message.data)
// TODO move to messaging.js // TODO move to messaging.js
@ -164,51 +174,51 @@ Rectangle {
try { try {
switch(data.call) { switch(data.call) {
case "compile": case "eth_compile":
postData(data._id, eth.compile(data.args[0])) postData(data._id, eth.compile(data.args[0]))
break break
case "coinbase": case "eth_coinbase":
postData(data._id, eth.coinBase()) postData(data._id, eth.coinBase())
case "account": case "eth_account":
postData(data._id, eth.key().address); postData(data._id, eth.key().address);
case "isListening": case "eth_istening":
postData(data._id, eth.isListening()) postData(data._id, eth.isListening())
break break
case "isMining": case "eth_mining":
postData(data._id, eth.isMining()) postData(data._id, eth.isMining())
break break
case "peerCount": case "eth_peerCount":
postData(data._id, eth.peerCount()) postData(data._id, eth.peerCount())
break break
case "countAt": case "eth_countAt":
require(1) require(1)
postData(data._id, eth.txCountAt(data.args[0])) postData(data._id, eth.txCountAt(data.args[0]))
break break
case "codeAt": case "eth_codeAt":
require(1) require(1)
var code = eth.codeAt(data.args[0]) var code = eth.codeAt(data.args[0])
postData(data._id, code); postData(data._id, code);
break break
case "blockByNumber": case "eth_blockByNumber":
require(1) require(1)
var block = eth.blockByNumber(data.args[0]) var block = eth.blockByNumber(data.args[0])
postData(data._id, block) postData(data._id, block)
break break
case "blockByHash": case "eth_blockByHash":
require(1) require(1)
var block = eth.blockByHash(data.args[0]) var block = eth.blockByHash(data.args[0])
postData(data._id, block) postData(data._id, block)
@ -219,8 +229,8 @@ Rectangle {
postData(data._id, block.transactions[data.args[1]]) postData(data._id, block.transactions[data.args[1]])
break break
case "transactionByHash": case "eth_transactionByHash":
case "transactionByNumber": case "eth_transactionByNumber":
require(2) require(2)
var block; var block;
@ -234,8 +244,8 @@ Rectangle {
postData(data._id, tx) postData(data._id, tx)
break break
case "uncleByHash": case "eth_uncleByHash":
case "uncleByNumber": case "eth_uncleByNumber":
require(2) require(2)
var block; var block;
@ -258,7 +268,7 @@ Rectangle {
break break
case "stateAt": case "eth_stateAt":
require(2); require(2);
var storage = eth.storageAt(data.args[0], data.args[1]); var storage = eth.storageAt(data.args[0], data.args[1]);
@ -266,55 +276,41 @@ Rectangle {
break break
case "call": case "eth_call":
require(1); require(1);
var ret = eth.call(data.args) var ret = eth.call(data.args)
postData(data._id, ret) postData(data._id, ret)
break break
case "balanceAt": case "eth_balanceAt":
require(1); require(1);
postData(data._id, eth.balanceAt(data.args[0])); postData(data._id, eth.balanceAt(data.args[0]));
break break
case "watch": case "eth_watch":
require(2) require(2)
eth.watch(data.args[0], data.args[1]) eth.watch(data.args[0], data.args[1])
case "disconnect": case "eth_disconnect":
require(1) require(1)
postData(data._id, null) postData(data._id, null)
break; break;
case "messages": case "eth_newFilterString":
require(1);
var messages = JSON.parse(eth.getMessages(data.args[0]))
postData(data._id, messages)
break
case "mutan":
require(1)
var code = eth.compileMutan(data.args[0])
postData(data._id, "0x"+code)
break;
case "newFilterString":
require(1) require(1)
var id = eth.newFilterString(data.args[0]) var id = eth.newFilterString(data.args[0])
postData(data._id, id); postData(data._id, id);
break; break;
case "newFilter": case "eth_newFilter":
require(1) require(1)
var id = eth.newFilter(data.args[0]) var id = eth.newFilter(data.args[0])
postData(data._id, id); postData(data._id, id);
break; break;
case "getMessages": case "eth_messages":
require(1); require(1);
var messages = eth.messages(data.args[0]); var messages = eth.messages(data.args[0]);
@ -323,10 +319,40 @@ Rectangle {
break; break;
case "deleteFilter": case "eth_deleteFilter":
require(1); require(1);
eth.uninstallFilter(data.args[0]) eth.uninstallFilter(data.args[0])
break; break;
case "shh_newFilter":
require(1);
var id = shh.watch(data.args[0], window);
postData(data._id, id);
break;
case "shh_newIdentity":
var id = shh.newIdentity()
postData(data._id, id)
break
case "shh_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;
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 +374,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,7 +45,7 @@ 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});
@ -59,8 +59,8 @@ ApplicationWindow {
mainSplit.setView(wallet.view, wallet.menuItem); mainSplit.setView(wallet.view, wallet.menuItem);
// Call the ready handler // Command setup
gui.done(); gui.sendCommand(0)
} }
function addViews(view, path, options) { function addViews(view, path, options) {
@ -512,18 +512,17 @@ ApplicationWindow {
var section; var section;
switch(options.section) { switch(options.section) {
case "ethereum": case "ethereum":
section = menuDefault; section = menuDefault;
break; break;
case "legacy": case "legacy":
section = menuLegacy; section = menuLegacy;
break; break;
default: default:
section = menuApps; section = menuApps;
break; break;
} }
var comp = menuItemTemplate.createObject(section) var comp = menuItemTemplate.createObject(section)
comp.view = view comp.view = view
comp.title = view.title comp.title = view.title
@ -771,12 +770,9 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
id: peerTable id: peerTable
model: peerModel model: peerModel
TableViewColumn{width: 100; role: "ip" ; title: "IP" } TableViewColumn{width: 200; role: "ip" ; title: "IP" }
TableViewColumn{width: 60; role: "port" ; title: "Port" }
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
TableViewColumn{width: 100; role: "latency"; title: "Latency" }
TableViewColumn{width: 260; role: "version" ; title: "Version" } TableViewColumn{width: 260; role: "version" ; title: "Version" }
TableViewColumn{width: 80; role: "caps" ; title: "Capabilities" } TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" }
} }
} }
} }
@ -787,8 +783,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
@ -798,7 +794,7 @@ ApplicationWindow {
smooth: true smooth: true
source: "../facet.png" source: "../facet.png"
x: 10 x: 10
y: 10 y: 30
} }
Text { Text {
@ -807,7 +803,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>"
} }
} }
@ -866,12 +862,15 @@ ApplicationWindow {
model: ListModel { id: pastPeers } model: ListModel { id: pastPeers }
Component.onCompleted: { Component.onCompleted: {
pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"})
/*
var ips = eth.pastPeers() var ips = eth.pastPeers()
for(var i = 0; i < ips.length; i++) { for(var i = 0; i < ips.length; i++) {
pastPeers.append({text: ips.get(i)}) pastPeers.append({text: ips.get(i)})
} }
pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"})
*/
} }
} }

View file

@ -46,6 +46,7 @@ Rectangle {
text: "Start" text: "Start"
onClicked: { onClicked: {
eth.setGasPrice(minGasPrice.text || "10000000000000"); eth.setGasPrice(minGasPrice.text || "10000000000000");
eth.setExtra(blockExtra.text)
if (eth.toggleMining()) { if (eth.toggleMining()) {
this.text = "Stop"; this.text = "Stop";
} else { } else {
@ -55,6 +56,7 @@ Rectangle {
} }
Rectangle { Rectangle {
id: minGasPriceRect
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 2 anchors.topMargin: 2
width: 200 width: 200
@ -65,6 +67,23 @@ Rectangle {
validator: RegExpValidator { regExp: /\d*/ } validator: RegExpValidator { regExp: /\d*/ }
} }
} }
Rectangle {
width: 300
anchors {
left: minGasPriceRect.right
leftMargin: 5
top: parent.top
topMargin: 2
}
TextField {
id: blockExtra
placeholderText: "Extra"
width: parent.width
maximumLength: 1024
}
}
} }
} }

View file

@ -20,7 +20,7 @@ Rectangle {
} }
function setBalance() { function setBalance() {
balance.text = "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address)) //balance.text = "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address))
if(menuItem) if(menuItem)
menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address)) menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address))
} }

View file

@ -9,8 +9,7 @@ import Ethereum 1.0
Rectangle { Rectangle {
id: root id: root
property var title: "Whisper" property var title: "Whisper Traffic"
property var iconSource: "../facet.png"
property var menuItem property var menuItem
objectName: "whisperView" objectName: "whisperView"
@ -21,10 +20,22 @@ Rectangle {
identity = shh.newIdentity() identity = shh.newIdentity()
console.log("New identity:", identity) console.log("New identity:", identity)
var t = shh.watch({topics: ["chat"]}) var t = shh.watch({}, root)
}
function onShhMessage(message, i) {
whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
} }
RowLayout { RowLayout {
id: input
anchors {
left: parent.left
leftMargin: 20
top: parent.top
topMargin: 20
}
TextField { TextField {
id: to id: to
placeholderText: "To" placeholderText: "To"
@ -40,8 +51,25 @@ Rectangle {
Button { Button {
text: "Send" text: "Send"
onClicked: { onClicked: {
shh.post(eth.toHex(data.text), "", identity, topics.text.split(","), 500, 50) 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

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -26,6 +29,7 @@ import (
"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/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
) )
type plugin struct { type plugin struct {
@ -57,7 +61,7 @@ func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, err
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d)) data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
} }
return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) return gui.xeth.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
} }
func (gui *Gui) SetCustomIdentifier(customIdentifier string) { func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
@ -72,7 +76,7 @@ func (gui *Gui) GetCustomIdentifier() string {
// functions that allow Gui to implement interface guilogger.LogSystem // functions that allow Gui to implement interface guilogger.LogSystem
func (gui *Gui) SetLogLevel(level logger.LogLevel) { func (gui *Gui) SetLogLevel(level logger.LogLevel) {
gui.logLevel = level gui.logLevel = level
gui.stdLog.SetLogLevel(level) gui.eth.Logger().SetLogLevel(level)
gui.config.Save("loglevel", level) gui.config.Save("loglevel", level)
} }
@ -118,7 +122,7 @@ func (self *Gui) DumpState(hash, path string) {
return return
} }
stateDump = block.State().Dump() stateDump = state.New(block.Root(), self.eth.Db()).Dump()
} }
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm) file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -151,7 +154,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
block := self.lib.eth.ChainManager().CurrentBlock() block := self.lib.eth.ChainManager().CurrentBlock()
env := utils.NewEnv(statedb, block, account.Address(), value) env := utils.NewEnv(self.lib.eth.ChainManager(), statedb, block, account.Address(), value)
self.Logf("callsize %d", len(script)) self.Logf("callsize %d", len(script))
go func() { go func() {
@ -309,9 +312,11 @@ func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack
d.win.Root().Call("setStack", val.String()) d.win.Root().Call("setStack", val.String())
} }
stateObject.EachStorage(func(key string, node *ethutil.Value) { it := stateObject.Trie().Iterator()
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())}) for it.Next() {
}) d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", it.Key), fmt.Sprintf("% x", it.Value)})
}
stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32)) stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64()) psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -106,7 +109,7 @@ 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")

View file

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import "C" import "C"
@ -23,7 +26,9 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"math/big" "math/big"
"os"
"path" "path"
"runtime" "runtime"
"strconv" "strconv"
@ -43,49 +48,24 @@ import (
"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 ServEv byte
const (
setup ServEv = iota
update
)
type Gui struct { type Gui struct {
// The main application window // The main application window
win *qml.Window win *qml.Window
// QML Engine // QML Engine
engine *qml.Engine engine *qml.Engine
component *qml.Common component *qml.Common
qmlDone bool
// The ethereum interface // The ethereum interface
eth *eth.Ethereum eth *eth.Ethereum
serviceEvents chan ServEv
// The public Ethereum library // The public Ethereum library
uiLib *UiLib uiLib *UiLib
@ -96,7 +76,7 @@ type Gui struct {
logLevel logger.LogLevel logLevel logger.LogLevel
open bool open bool
pipe *xeth.JSXEth xeth *xeth.JSXEth
Session string Session string
clientIdentity *p2p.SimpleClientIdentity clientIdentity *p2p.SimpleClientIdentity
@ -104,8 +84,7 @@ type Gui struct {
plugins map[string]plugin plugins map[string]plugin
miner *miner.Miner miner *miner.Miner
stdLog logger.LogSystem
} }
// Create GUI, but doesn't start it // Create GUI, but doesn't start it
@ -115,8 +94,18 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
panic(err) panic(err)
} }
pipe := xeth.NewJSXEth(ethereum) xeth := xeth.NewJSXEth(ethereum)
gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: logger.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)} gui := &Gui{eth: ethereum,
txDb: db,
xeth: xeth,
logLevel: logger.LogLevel(logLevel),
Session: session,
open: false,
clientIdentity: clientIdentity,
config: config,
plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1),
}
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json")) data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
json.Unmarshal([]byte(data), &gui.plugins) json.Unmarshal([]byte(data), &gui.plugins)
@ -128,6 +117,8 @@ func (gui *Gui) Start(assetPath string) {
guilogger.Infoln("Starting GUI") guilogger.Infoln("Starting GUI")
go gui.service()
// Register ethereum functions // Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{ qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" }, Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
@ -147,18 +138,7 @@ func (gui *Gui) Start(assetPath string) {
context.SetVar("eth", gui.uiLib) context.SetVar("eth", gui.uiLib)
context.SetVar("shh", gui.whisper) context.SetVar("shh", gui.whisper)
// Load the main QML interface win, err := gui.showWallet(context)
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
var win *qml.Window
var err error
var addlog = false
if len(data) == 0 {
win, err = gui.showKeyImport(context)
} else {
win, err = gui.showWallet(context)
addlog = true
}
if err != nil { if err != nil {
guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err) guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err)
@ -169,9 +149,7 @@ func (gui *Gui) Start(assetPath string) {
win.Show() win.Show()
// only add the gui guilogger after window is shown otherwise slider wont be shown // only add the gui guilogger after window is shown otherwise slider wont be shown
if addlog { logger.AddLogSystem(gui)
logger.AddLogSystem(gui)
}
win.Wait() win.Wait()
// need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel) // need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
@ -197,18 +175,11 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
return nil, err return nil, err
} }
gui.win = gui.createWindow(component) gui.createWindow(component)
gui.update()
return gui.win, nil return gui.win, nil
} }
// The done handler will be called by QML when all views have been loaded
func (gui *Gui) Done() {
gui.qmlDone = true
}
func (gui *Gui) ImportKey(filePath string) { func (gui *Gui) ImportKey(filePath string) {
} }
@ -222,10 +193,8 @@ func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
} }
func (gui *Gui) createWindow(comp qml.Object) *qml.Window { func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
win := comp.CreateWindow(nil) gui.win = comp.CreateWindow(nil)
gui.uiLib.win = gui.win
gui.win = win
gui.uiLib.win = win
return gui.win return gui.win
} }
@ -253,7 +222,7 @@ 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)
} }
@ -261,43 +230,40 @@ func (gui *Gui) setInitialChain(ancientBlocks bool) {
func (gui *Gui) loadAddressBook() { func (gui *Gui) loadAddressBook() {
view := gui.getObjectByName("infoView") view := gui.getObjectByName("infoView")
nameReg := gui.pipe.World().Config().Get("NameReg") nameReg := gui.xeth.World().Config().Get("NameReg")
if nameReg != nil { if nameReg != nil {
nameReg.EachStorage(func(name string, value *ethutil.Value) { it := nameReg.Trie().Iterator()
if name[0] != 0 { for it.Next() {
value.Decode() if it.Key[0] != 0 {
view.Call("addAddress", struct{ Name, Address string }{string(it.Key), ethutil.Bytes2Hex(it.Value)})
view.Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())})
} }
})
}
} }
} }
func (self *Gui) loadMergedMiningOptions() { func (self *Gui) loadMergedMiningOptions() {
view := self.getObjectByName("mergedMiningModel") view := self.getObjectByName("mergedMiningModel")
nameReg := self.pipe.World().Config().Get("MergeMining") mergeMining := self.xeth.World().Config().Get("MergeMining")
if nameReg != nil { if mergeMining != nil {
i := 0 i := 0
nameReg.EachStorage(func(name string, value *ethutil.Value) { it := mergeMining.Trie().Iterator()
if name[0] != 0 { for it.Next() {
value.Decode() view.Call("addMergedMiningOption", struct {
Checked bool
Name, Address string
Id, ItemId int
}{false, string(it.Key), ethutil.Bytes2Hex(it.Value), 0, i})
view.Call("addMergedMiningOption", struct { i++
Checked bool
Name, Address string
Id, ItemId int
}{false, name, ethutil.Bytes2Hex(value.Bytes()), 0, i})
i++ }
}
})
} }
} }
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) { func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
pipe := xeth.New(gui.eth) nameReg := gui.xeth.World().Config().Get("NameReg")
nameReg := pipe.World().Config().Get("NameReg")
addr := gui.address() addr := gui.address()
var inout string var inout string
@ -308,7 +274,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
} }
var ( var (
ptx = xeth.NewJSTx(tx, pipe.World().State()) ptx = xeth.NewJSTx(tx)
send = nameReg.Storage(tx.From()) send = nameReg.Storage(tx.From())
rec = nameReg.Storage(tx.To()) rec = nameReg.Storage(tx.To())
s, r string s, r string
@ -354,7 +320,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.xeth.World().Config().Get("NameReg").Storage(block.Coinbase()).Str(), "\x00")
b := xeth.NewJSBlock(block) b := xeth.NewJSBlock(block)
b.Name = name b.Name = name
@ -381,11 +347,48 @@ func (self *Gui) getObjectByName(objectName string) qml.Object {
return self.win.Root().ObjectByName(objectName) return self.win.Root().ObjectByName(objectName)
} }
// Simple go routine function that updates the list of peers in the GUI func loadJavascriptAssets(gui *Gui) (jsfiles string) {
func (gui *Gui) update() { for _, fn := range []string{"ext/q.js", "ext/eth.js/main.js", "ext/eth.js/qt.js", "ext/setup.js"} {
// We have to wait for qml to be done loading all the windows. f, err := os.Open(gui.uiLib.AssetPath(fn))
for !gui.qmlDone { if err != nil {
time.Sleep(300 * time.Millisecond) fmt.Println(err)
continue
}
content, err := ioutil.ReadAll(f)
if err != nil {
fmt.Println(err)
continue
}
jsfiles += string(content)
}
return
}
func (gui *Gui) SendCommand(cmd ServEv) {
gui.serviceEvents <- cmd
}
func (gui *Gui) service() {
for ev := range gui.serviceEvents {
switch ev {
case setup:
go gui.setup()
case update:
go gui.update()
}
}
}
func (gui *Gui) setup() {
for gui.win == nil {
time.Sleep(time.Millisecond * 200)
}
for _, plugin := range gui.plugins {
guilogger.Infoln("Loading plugin ", plugin.Name)
gui.win.Root().Call("addPlugin", plugin.Path, "")
} }
go func() { go func() {
@ -395,14 +398,21 @@ func (gui *Gui) update() {
gui.setPeerInfo() gui.setPeerInfo()
}() }()
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView")) // Inject javascript files each time navigation is requested.
// Unfortunately webview.experimental.userScripts injects _after_
// the page has loaded which kind of renders it useless...
//jsfiles := loadJavascriptAssets(gui)
gui.getObjectByName("webView").On("navigationRequested", func() {
//gui.getObjectByName("webView").Call("injectJs", jsfiles)
})
for _, plugin := range gui.plugins { gui.whisper.SetView(gui.getObjectByName("whisperView"))
guilogger.Infoln("Loading plugin ", plugin.Name)
gui.win.Root().Call("addPlugin", plugin.Path, "") gui.SendCommand(update)
} }
// Simple go routine function that updates the list of peers in the GUI
func (gui *Gui) update() {
peerUpdateTicker := time.NewTicker(5 * time.Second) peerUpdateTicker := time.NewTicker(5 * time.Second)
generalUpdateTicker := time.NewTicker(500 * time.Millisecond) generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
statsUpdateTicker := time.NewTicker(5 * time.Second) statsUpdateTicker := time.NewTicker(5 * time.Second)
@ -421,77 +431,75 @@ func (gui *Gui) update() {
core.TxPostEvent{}, core.TxPostEvent{},
) )
go func() { defer events.Unsubscribe()
defer events.Unsubscribe() for {
for { select {
select { case ev, isopen := <-events.Chan():
case ev, isopen := <-events.Chan(): if !isopen {
if !isopen { return
return
}
switch ev := ev.(type) {
case core.NewBlockEvent:
gui.processBlock(ev.Block, false)
if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 {
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
}
case core.TxPreEvent:
tx := ev.Tx
tstate := gui.eth.ChainManager().TransState()
cstate := gui.eth.ChainManager().State()
taccount := tstate.GetAccount(gui.address())
caccount := cstate.GetAccount(gui.address())
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
gui.insertTransaction("pre", tx)
case core.TxPostEvent:
tx := ev.Tx
object := state.GetAccount(gui.address())
if bytes.Compare(tx.From(), gui.address()) == 0 {
object.SubAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
object.AddAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
gui.setWalletValue(object.Balance(), nil)
state.UpdateStateObject(object)
}
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number.String()
lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
/*
blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength
var (
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
)
dlWidget.Set("value", pct)
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
*/
case <-statsUpdateTicker.C:
gui.setStatsPane()
} }
switch ev := ev.(type) {
case core.NewBlockEvent:
gui.processBlock(ev.Block, false)
if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
}
case core.TxPreEvent:
tx := ev.Tx
tstate := gui.eth.ChainManager().TransState()
cstate := gui.eth.ChainManager().State()
taccount := tstate.GetAccount(gui.address())
caccount := cstate.GetAccount(gui.address())
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
gui.insertTransaction("pre", tx)
case core.TxPostEvent:
tx := ev.Tx
object := state.GetAccount(gui.address())
if bytes.Compare(tx.From(), gui.address()) == 0 {
object.SubAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
object.AddAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
gui.setWalletValue(object.Balance(), nil)
state.UpdateStateObject(object)
}
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
/*
blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength
var (
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
)
dlWidget.Set("value", pct)
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
*/
case <-statsUpdateTicker.C:
gui.setStatsPane()
} }
}() }
} }
func (gui *Gui) setStatsPane() { func (gui *Gui) setStatsPane() {
@ -523,7 +531,7 @@ NumGC: %d
func (gui *Gui) setPeerInfo() { func (gui *Gui) setPeerInfo() {
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers)) gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
gui.win.Root().Call("resetPeers") gui.win.Root().Call("resetPeers")
for _, peer := range gui.pipe.Peers() { for _, peer := range gui.xeth.Peers() {
gui.win.Root().Call("addPeer", peer) gui.win.Root().Call("addPeer", peer)
} }
} }
@ -535,3 +543,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

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -139,7 +142,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

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -26,15 +29,17 @@ import (
"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/eth"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )
const ( const (
ClientIdentifier = "Mist" ClientIdentifier = "Mist"
Version = "0.7.9" Version = "0.8.1"
) )
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
var mainlogger = logger.NewLogger("MAIN")
func run() error { func run() error {
// precedence: code-internal flag default < config file < environment variables < command line // precedence: code-internal flag default < config file < environment variables < command line
@ -43,27 +48,25 @@ func run() error {
tstart := time.Now() tstart := time.Now()
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH") config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
utils.InitDataDir(Datadir) ethereum, err := eth.New(&eth.Config{
Name: ClientIdentifier,
stdLog := utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile) Version: Version,
KeyStore: KeyStore,
db := utils.NewDatabase() DataDir: Datadir,
err := utils.DBSanityCheck(db) LogFile: LogFile,
LogLevel: LogLevel,
Identifier: Identifier,
MaxPeers: MaxPeer,
Port: OutboundPort,
NATType: PMPGateway,
PMPGateway: PMPGateway,
KeyRing: KeyRing,
Dial: true,
})
if err != nil { if err != nil {
ErrorWindow(err) mainlogger.Fatalln(err)
os.Exit(1)
}
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
if ShowGenesis {
utils.ShowGenesis(ethereum)
} }
utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
if StartRpc { if StartRpc {
utils.StartRpc(ethereum, RpcPort) utils.StartRpc(ethereum, RpcPort)
@ -73,8 +76,7 @@ func run() error {
utils.StartWebSockets(ethereum) utils.StartWebSockets(ethereum)
} }
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel) gui := NewWindow(ethereum, config, ethereum.ClientIdentity().(*p2p.SimpleClientIdentity), KeyRing, LogLevel)
gui.stdLog = stdLog
utils.RegisterInterrupt(func(os.Signal) { utils.RegisterInterrupt(func(os.Signal) {
gui.Stop() gui.Stop()

View file

@ -1,19 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public go-ethereum is free software: you can redistribute it and/or modify
// License as published by the Free Software Foundation; either it under the terms of the GNU General Public License as published by
// version 2.1 of the License, or (at your option) any later version. the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of go-ethereum is distributed in the hope that it will be useful,
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU but WITHOUT ANY WARRANTY; without even the implied warranty of
// General Public License for more details. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software You should have received a copy of the GNU General Public License
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// MA 02110-1301 USA */
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
@ -66,7 +70,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

@ -1,20 +1,23 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. /*
// This file is part of go-ethereum
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package main package main
import ( import (
@ -195,7 +198,9 @@ func (ui *UiLib) Connect(button qml.Object) {
} }
func (ui *UiLib) ConnectToPeer(addr string) { func (ui *UiLib) ConnectToPeer(addr string) {
ui.eth.SuggestPeer(addr) if err := ui.eth.SuggestPeer(addr); err != nil {
guilogger.Infoln(err)
}
} }
func (ui *UiLib) AssetPath(p string) string { func (ui *UiLib) AssetPath(p string) string {
@ -225,6 +230,87 @@ func (self *UiLib) StartDebugger() {
dbWindow.Show() dbWindow.Show()
} }
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.JSXEth.Transact(
object["from"],
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}
func (self *UiLib) Compile(code string) (string, error) {
bcode, err := ethutil.Compile(code, false)
if err != nil {
return err.Error(), err
}
return ethutil.Bytes2Hex(bcode), err
}
func (self *UiLib) Call(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.JSXEth.Execute(
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}
func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int {
return self.miner.AddLocalTx(&miner.LocalTx{
To: ethutil.Hex2Bytes(to),
Data: ethutil.Hex2Bytes(data),
Gas: gas,
GasPrice: gasPrice,
Value: value,
}) - 1
}
func (self *UiLib) RemoveLocalTransaction(id int) {
self.miner.RemoveLocalTx(id)
}
func (self *UiLib) SetGasPrice(price string) {
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
}
func (self *UiLib) SetExtra(extra string) {
self.miner.Extra = extra
}
func (self *UiLib) ToggleMining() bool {
if !self.miner.Mining() {
self.miner.Start()
return true
} else {
self.miner.Stop()
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) { func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
filter := qt.NewFilterFromMap(object, self.eth) filter := qt.NewFilterFromMap(object, self.eth)
filter.MessageCallback = func(messages state.Messages) { filter.MessageCallback = func(messages state.Messages) {
@ -312,80 +398,3 @@ func mapToTxParams(object map[string]interface{}) map[string]string {
return conv return conv
} }
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.JSXEth.Transact(
object["from"],
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}
func (self *UiLib) Compile(code string) (string, error) {
bcode, err := ethutil.Compile(code, false)
if err != nil {
return err.Error(), err
}
return ethutil.Bytes2Hex(bcode), err
}
func (self *UiLib) Call(params map[string]interface{}) (string, error) {
object := mapToTxParams(params)
return self.JSXEth.Execute(
object["to"],
object["value"],
object["gas"],
object["gasPrice"],
object["data"],
)
}
func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int {
return self.miner.AddLocalTx(&miner.LocalTx{
To: ethutil.Hex2Bytes(to),
Data: ethutil.Hex2Bytes(data),
Gas: gas,
GasPrice: gasPrice,
Value: value,
}) - 1
}
func (self *UiLib) RemoveLocalTransaction(id int) {
self.miner.RemoveLocalTx(id)
}
func (self *UiLib) SetGasPrice(price string) {
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
}
func (self *UiLib) ToggleMining() bool {
if !self.miner.Mining() {
self.miner.Start()
return true
} else {
self.miner.Stop()
return false
}
}
func (self *UiLib) ToHex(data string) string {
return "0x" + ethutil.Bytes2Hex([]byte(data))
}
/*
// XXX Refactor me & MOVE
func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
return self.filterManager.InstallFilter(filter)
}
func (self *Ethereum) UninstallFilter(id int) { self.filterManager.UninstallFilter(id) }
func (self *Ethereum) GetFilter(id int) *core.Filter { return self.filterManager.GetFilter(id) }
*/

View file

@ -1,10 +1,25 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
package main package main
import ( import (
"crypto/elliptic" "crypto/elliptic"
"fmt" "flag"
"log" "log"
"net"
"os" "os"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -12,29 +27,32 @@ import (
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
) )
var (
natType = flag.String("nat", "", "NAT traversal implementation")
pmpGateway = flag.String("gateway", "", "gateway address for NAT-PMP")
listenAddr = flag.String("addr", ":30301", "listen address")
)
func main() { func main() {
flag.Parse()
nat, err := p2p.ParseNAT(*natType, *pmpGateway)
if err != nil {
log.Fatal("invalid nat:", err)
}
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel)) logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel))
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y)
srv := p2p.Server{ srv := p2p.Server{
MaxPeers: 10, MaxPeers: 100,
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)), Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", marshaled),
ListenAddr: ":30301", ListenAddr: *listenAddr,
NAT: p2p.UPNP(), NAT: nat,
NoDial: true,
} }
if err := srv.Start(); err != nil { if err := srv.Start(); err != nil {
fmt.Println("could not start server:", err) log.Fatal("could not start server:", err)
os.Exit(1)
} }
// add seed peers
seed, err := net.ResolveTCPAddr("tcp", "poc-7.ethdev.com:30300")
if err != nil {
fmt.Println("couldn't resolve:", err)
os.Exit(1)
}
srv.SuggestPeer(seed.IP, seed.Port, nil)
select {} select {}
} }

148
cmd/rlpdump/main.go Normal file
View file

@ -0,0 +1,148 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Felix Lange <felix@ethdev.com>
*/
// rlpdump is a pretty-printer for RLP data.
package main
import (
"bytes"
"encoding/hex"
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/ethereum/go-ethereum/rlp"
)
var (
hexMode = flag.String("hex", "", "dump given hex data")
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
)
func init() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, `
Dumps RLP data from the given file in readable form.
If the filename is omitted, data is read from stdin.`)
}
}
func main() {
flag.Parse()
var r io.Reader
switch {
case *hexMode != "":
data, err := hex.DecodeString(*hexMode)
if err != nil {
die(err)
}
r = bytes.NewReader(data)
case flag.NArg() == 0:
r = os.Stdin
case flag.NArg() == 1:
fd, err := os.Open(flag.Arg(0))
if err != nil {
die(err)
}
defer fd.Close()
r = fd
default:
fmt.Fprintln(os.Stderr, "Error: too many arguments")
flag.Usage()
os.Exit(2)
}
s := rlp.NewStream(r)
for {
if err := dump(s, 0); err != nil {
if err != io.EOF {
die(err)
}
break
}
fmt.Println()
}
}
func dump(s *rlp.Stream, depth int) error {
kind, size, err := s.Kind()
if err != nil {
return err
}
switch kind {
case rlp.Byte, rlp.String:
str, err := s.Bytes()
if err != nil {
return err
}
if len(str) == 0 || !*noASCII && isASCII(str) {
fmt.Printf("%s%q", ws(depth), str)
} else {
fmt.Printf("%s%x", ws(depth), str)
}
case rlp.List:
s.List()
defer s.ListEnd()
if size == 0 {
fmt.Print(ws(depth) + "[]")
} else {
fmt.Println(ws(depth) + "[")
for i := 0; ; i++ {
if i > 0 {
fmt.Print(",\n")
}
if err := dump(s, depth+1); err == rlp.EOL {
break
} else if err != nil {
return err
}
}
fmt.Print(ws(depth) + "]")
}
}
return nil
}
func isASCII(b []byte) bool {
for _, c := range b {
if c < 32 || c > 126 {
return false
}
}
return true
}
func ws(n int) string {
return strings.Repeat(" ", n)
}
func die(args ...interface{}) {
fmt.Fprintln(os.Stderr, args...)
os.Exit(1)
}

View file

@ -1,10 +1,28 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
* Viktor Tron <viktor@ethdev.com>
*/
package utils package utils
import ( import (
"fmt" "fmt"
"io"
"log"
"net"
"os" "os"
"os/signal" "os/signal"
"path" "path"
@ -13,14 +31,15 @@ import (
"runtime" "runtime"
"bitbucket.org/kardianos/osext" "bitbucket.org/kardianos/osext"
"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/eth"
"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/state"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
) )
@ -50,15 +69,8 @@ func RunInterruptCallbacks(sig os.Signal) {
} }
} }
func AbsolutePath(Datadir string, filename string) string {
if path.IsAbs(filename) {
return filename
}
return path.Join(Datadir, filename)
}
func openLogFile(Datadir string, filename string) *os.File { func openLogFile(Datadir string, filename string) *os.File {
path := AbsolutePath(Datadir, filename) path := ethutil.AbsolutePath(Datadir, filename)
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil { if err != nil {
panic(fmt.Sprintf("error opening log file '%s': %v", filename, err)) panic(fmt.Sprintf("error opening log file '%s': %v", filename, err))
@ -74,23 +86,13 @@ func confirm(message string) bool {
if r == "n" || r == "y" { if r == "n" || r == "y" {
break break
} else { } else {
fmt.Printf("Yes or no?", r) fmt.Printf("Yes or no? (%s)", r)
} }
} }
return r == "y" return r == "y"
} }
func DBSanityCheck(db ethutil.Database) error { func initDataDir(Datadir string) {
d, _ := db.Get([]byte("ProtocolVersion"))
protov := ethutil.NewValue(d).Uint()
if protov != eth.ProtocolVersion && protov != 0 {
return fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, eth.ProtocolVersion, ethutil.Config.ExecPath+"/database")
}
return nil
}
func InitDataDir(Datadir string) {
_, err := os.Stat(Datadir) _, err := os.Stat(Datadir)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -100,26 +102,8 @@ func InitDataDir(Datadir string) {
} }
} }
func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) logger.LogSystem {
var writer io.Writer
if LogFile == "" {
writer = os.Stdout
} else {
writer = openLogFile(Datadir, LogFile)
}
sys := logger.NewStdLogSystem(writer, log.LstdFlags, logger.LogLevel(LogLevel))
logger.AddLogSystem(sys)
if DebugFile != "" {
writer = openLogFile(Datadir, DebugFile)
logger.AddLogSystem(logger.NewStdLogSystem(writer, log.LstdFlags, logger.DebugLevel))
}
return sys
}
func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager { func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
InitDataDir(Datadir) initDataDir(Datadir)
cfg := ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix) cfg := ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix)
cfg.VmType = vmType cfg.VmType = vmType
@ -136,70 +120,19 @@ func exit(err error) {
os.Exit(status) os.Exit(status)
} }
func NewDatabase() ethutil.Database { func StartEthereum(ethereum *eth.Ethereum, UseSeed bool) {
db, err := ethdb.NewLDBDatabase("database") clilogger.Infof("Starting %s", ethereum.ClientIdentity())
err := ethereum.Start(UseSeed)
if err != nil { if err != nil {
exit(err) exit(err)
} }
return db
}
func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
return p2p.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier, pubkey)
}
func NatType(natType string, gateway string) (nat p2p.NAT) {
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 {
clilogger.Fatalln("eth start err:", err)
}
return ethereum
}
func StartEthereum(ethereum *eth.Ethereum, UseSeed bool) {
clilogger.Infof("Starting %s", ethereum.ClientIdentity())
ethereum.Start(UseSeed)
RegisterInterrupt(func(sig os.Signal) { RegisterInterrupt(func(sig os.Signal) {
ethereum.Stop() ethereum.Stop()
logger.Flush() logger.Flush()
}) })
} }
func ShowGenesis(ethereum *eth.Ethereum) {
clilogger.Infoln(ethereum.ChainManager().Genesis())
exit(nil)
}
func NewKeyManager(KeyStore string, Datadir string, db ethutil.Database) *crypto.KeyManager {
var keyManager *crypto.KeyManager
switch {
case KeyStore == "db":
keyManager = crypto.NewDBKeyManager(db)
case KeyStore == "file":
keyManager = crypto.NewFileKeyManager(Datadir)
default:
exit(fmt.Errorf("unknown keystore type: %s", KeyStore))
}
return keyManager
}
func DefaultAssetPath() string { func DefaultAssetPath() string {
var assetPath string var assetPath string
// If the current working directory is the go-ethereum dir // If the current working directory is the go-ethereum dir
@ -325,9 +258,10 @@ 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) statedb := state.New(parent.Root(), ethereum.Db())
_, err := ethereum.BlockProcessor().TransitionState(statedb, parent, block)
if err != nil { if err != nil {
return err return err
} }
@ -335,3 +269,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

@ -1,3 +1,23 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package utils package utils
import ( import (
@ -10,6 +30,7 @@ import (
) )
type VMEnv struct { type VMEnv struct {
chain *core.ChainManager
state *state.StateDB state *state.StateDB
block *types.Block block *types.Block
@ -20,8 +41,9 @@ type VMEnv struct {
Gas *big.Int Gas *big.Int
} }
func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *big.Int) *VMEnv { func NewEnv(chain *core.ChainManager, state *state.StateDB, block *types.Block, transactor []byte, value *big.Int) *VMEnv {
return &VMEnv{ return &VMEnv{
chain: chain,
state: state, state: state,
block: block, block: block,
transactor: transactor, transactor: transactor,
@ -30,17 +52,23 @@ 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) 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) GetHash(n uint64) []byte {
if block := self.chain.GetBlockByNumber(n); block != nil {
return block.Hash()
}
return nil
}
func (self *VMEnv) AddLog(log state.Log) { func (self *VMEnv) AddLog(log state.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
@ -52,19 +80,19 @@ func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execu
return core.NewExecution(self, addr, data, gas, price, value) return core.NewExecution(self, addr, data, gas, price, value)
} }
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Call(caller vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
ret, err := exe.Call(addr, caller) ret, err := exe.Call(addr, caller)
self.Gas = exe.Gas self.Gas = exe.Gas
return ret, err return ret, err
} }
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) CallCode(caller vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(caller.Address(), data, gas, price, value) exe := self.vm(caller.Address(), data, gas, price, value)
return exe.Call(addr, caller) return exe.Call(addr, caller)
} }
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { func (self *VMEnv) Create(caller vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
return exe.Create(caller) return exe.Create(caller)
} }

View file

@ -1,12 +1,35 @@
/*
This file is part of go-ethereum
go-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
go-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* Jeffrey Wilcke <i@jev.io>
*/
package utils package utils
import ( import (
"github.com/ethereum/go-ethereum/eth" "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
} }
@ -30,73 +53,73 @@ func (self *WebSocketServer) Serv() {
data := ethutil.NewValue(msg.Args) data := ethutil.NewValue(msg.Args)
bcode, err := ethutil.Compile(data.Get(0).Str(), false) bcode, err := ethutil.Compile(data.Get(0).Str(), false)
if err != nil { if err != nil {
c.Write(args(nil, err.Error()), msg.Seed) c.Write(args(nil, err.Error()), msg.Id)
} }
code := ethutil.Bytes2Hex(bcode) code := ethutil.Bytes2Hex(bcode)
c.Write(args(code, nil), msg.Seed) c.Write(args(code, nil), msg.Id)
case "getBlockByNumber": case "eth_blockByNumber":
args := msg.Arguments() args := msg.Arguments()
block := pipe.BlockByNumber(int32(args.Get(0).Uint())) block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
c.Write(block, msg.Seed) c.Write(block, msg.Id)
case "getKey": case "eth_blockByHash":
c.Write(pipe.Key().PrivateKey, msg.Seed) args := msg.Arguments()
case "transact":
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Id)
case "eth_transact":
if mp, ok := msg.Args[0].(map[string]interface{}); ok { if mp, ok := msg.Args[0].(map[string]interface{}); ok {
object := mapToTxParams(mp) object := mapToTxParams(mp)
c.Write( c.Write(
args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])), args(pipe.Transact(pipe.Key().PrivateKey, object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
msg.Seed, msg.Id,
) )
} }
case "getCoinBase": case "eth_gasPrice":
c.Write(pipe.CoinBase(), msg.Seed) c.Write("10000000000000", msg.Id)
case "eth_coinbase":
c.Write(pipe.CoinBase(), msg.Id)
case "getIsListening": case "eth_listening":
c.Write(pipe.IsListening(), msg.Seed) c.Write(pipe.IsListening(), msg.Id)
case "getIsMining": case "eth_mining":
c.Write(pipe.IsMining(), msg.Seed) c.Write(pipe.IsMining(), msg.Id)
case "getPeerCoint": case "eth_peerCount":
c.Write(pipe.PeerCount(), msg.Seed) c.Write(pipe.PeerCount(), msg.Id)
case "getCountAt": case "eth_countAt":
args := msg.Arguments() args := msg.Arguments()
c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Seed) c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Id)
case "getCodeAt": case "eth_codeAt":
args := msg.Arguments() args := msg.Arguments()
c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Seed) c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Id)
case "getBlockByHash": case "eth_storageAt":
args := msg.Arguments() args := msg.Arguments()
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Seed) c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Id)
case "getStorageAt": case "eth_balanceAt":
args := msg.Arguments() args := msg.Arguments()
c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Seed) c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Id)
case "getBalanceAt": case "eth_secretToAddress":
args := msg.Arguments() args := msg.Arguments()
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed) c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Id)
case "getSecretToAddress": case "eth_newFilter":
args := msg.Arguments() case "eth_newFilterString":
case "eth_messages":
c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed)
case "newFilter":
case "newFilterString":
case "messages":
// TODO // TODO
} }
@ -106,6 +129,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

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"sync" "sync"
"time"
"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"
@ -17,24 +16,13 @@ import (
"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"
"gopkg.in/fatih/set.v0"
) )
var statelogger = logger.NewLogger("BLOCK") var statelogger = logger.NewLogger("BLOCK")
type Peer interface {
Inbound() bool
LastSend() time.Time
LastPong() int64
Host() []byte
Port() uint16
Version() string
PingTime() string
Connected() *int32
Caps() *ethutil.Value
}
type EthManager interface { type EthManager interface {
BlockManager() *BlockManager BlockProcessor() *BlockProcessor
ChainManager() *ChainManager ChainManager() *ChainManager
TxPool() *TxPool TxPool() *TxPool
PeerCount() int PeerCount() int
@ -47,7 +35,8 @@ type EthManager interface {
EventMux() *event.TypeMux EventMux() *event.TypeMux
} }
type BlockManager struct { type BlockProcessor struct {
db ethutil.Database
// Mutex for locking the block processor. Blocks can only be handled one at a time // Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex mutex sync.Mutex
// Canonical block chain // Canonical block chain
@ -69,8 +58,9 @@ type BlockManager struct {
eventMux *event.TypeMux eventMux *event.TypeMux
} }
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager { func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
sm := &BlockManager{ sm := &BlockProcessor{
db: db,
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: ezp.New(), Pow: ezp.New(),
bc: chainManager, bc: chainManager,
@ -81,11 +71,11 @@ func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event
return sm return sm
} }
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) { func (sm *BlockProcessor) 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 parent state
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false) receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -94,7 +84,7 @@ func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *t
return receipts, nil return receipts, nil
} }
func (self *BlockManager) ApplyTransactions(coinbase *state.StateObject, state *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) { func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
var ( var (
receipts types.Receipts receipts types.Receipts
handled, unhandled types.Transactions handled, unhandled types.Transactions
@ -112,7 +102,7 @@ done:
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(NewEnv(state, self.bc, tx, block), tx, cb)
_, err = st.TransitionState() _, err = st.TransitionState()
if err != nil { if err != nil {
switch { switch {
@ -127,7 +117,6 @@ done:
statelogger.Infoln(err) statelogger.Infoln(err)
erroneous = append(erroneous, tx) erroneous = append(erroneous, tx)
err = nil err = nil
continue
} }
} }
@ -141,6 +130,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 {
@ -156,38 +146,34 @@ 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
} }
func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) { func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) {
// Processing a blocks may never happen simultaneously // Processing a blocks may never happen simultaneously
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)
} }
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) { func (sm *BlockProcessor) 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.Root(), sm.db)
//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 {
@ -199,21 +185,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
} }
@ -223,8 +212,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
} }
@ -233,10 +222,12 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
// Sync the current block's state to the database and cancelling out the deferred Undo // Sync the current block's state to the database and cancelling out the deferred Undo
state.Sync() state.Sync()
state.Manifest().SetHash(block.Hash())
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.txpool.RemoveSet(block.Transactions()) sm.txpool.RemoveSet(block.Transactions())
@ -246,16 +237,16 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
} }
} }
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) { func (sm *BlockProcessor) 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.
@ -269,15 +260,20 @@ func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
// Validates the current block. Returns an error if the block was invalid, // Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain. // an uncle or anything that isn't on the current block chain.
// 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 *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
expd := CalcDifficulty(block, parent) if len(block.Header().Extra) > 1024 {
if expd.Cmp(block.Difficulty) < 0 { return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra))
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
} }
diff := block.Time - parent.Time expd := CalcDifficulty(block, parent)
if expd.Cmp(block.Header().Difficulty) < 0 {
fmt.Println("parent\n", parent)
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
}
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
@ -289,37 +285,47 @@ 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
} }
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error { func (sm *BlockProcessor) 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) ancestors := set.New()
nonces := ethutil.NewSet(block.Nonce) for _, ancestor := range sm.bc.GetAncestors(block, 6) {
for _, uncle := range block.Uncles { ancestors.Add(string(ancestor.Hash()))
if nonces.Include(uncle.Nonce) { }
uncles := set.New()
uncles.Add(string(block.Hash()))
for _, uncle := range block.Uncles() {
if uncles.Has(string(uncle.Hash())) {
// Error not unique // Error not unique
return UncleError("Uncle not unique") return UncleError("Uncle not unique")
} }
uncles.Add(string(uncle.Hash()))
uncleParent := sm.bc.GetBlock(uncle.PrevHash) if !ancestors.Has(uncle.ParentHash) {
if uncleParent == nil { return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
} }
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { /*
return UncleError("Uncle too old") uncleParent := sm.bc.GetBlock(uncle.ParentHash)
} if uncleParent == nil {
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
}
if knownUncles.Include(uncle.Hash()) { if uncleParent.Number().Cmp(new(big.Int).Sub(parent.Number(), big.NewInt(6))) < 0 {
return UncleError("Uncle in chain") return UncleError("Uncle too old")
} }
nonces.Insert(uncle.Nonce) if knownUncles.Has(string(uncle.Hash())) {
return UncleError("Uncle in chain")
}
*/
r := new(big.Int) r := new(big.Int)
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
@ -331,31 +337,32 @@ 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, 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),
}) })
return nil return nil
} }
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) { func (sm *BlockProcessor) 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())
state = state.New(parent.Root(), sm.db)
) )
defer state.Reset() defer state.Reset()

View file

@ -1,6 +1,7 @@
package core package core
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"sync" "sync"
@ -9,44 +10,52 @@ 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/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) { type StateQuery interface {
for _, addr := range []string{ GetAccount(addr []byte) *state.StateObject
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.State().GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
block.State().UpdateStateObject(account)
}
} }
func CalcDifficulty(block, parent *types.Block) *big.Int { 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+13 {
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
db ethutil.Database
processor types.BlockProcessor processor types.BlockProcessor
eventMux *event.TypeMux eventMux *event.TypeMux
genesisBlock *types.Block genesisBlock *types.Block
@ -88,13 +97,9 @@ func (self *ChainManager) CurrentBlock() *types.Block {
return self.currentBlock return self.currentBlock
} }
func NewChainManager(mux *event.TypeMux) *ChainManager { func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
bc := &ChainManager{} bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
bc.eventMux = mux
bc.setLastBlock() bc.setLastBlock()
bc.transState = bc.State().Copy() bc.transState = bc.State().Copy()
return bc return bc
@ -112,7 +117,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
} }
func (self *ChainManager) State() *state.StateDB { func (self *ChainManager) State() *state.StateDB {
return self.CurrentBlock().State() return state.New(self.CurrentBlock().Root(), self.db)
} }
func (self *ChainManager) TransState() *state.StateDB { func (self *ChainManager) TransState() *state.StateDB {
@ -120,23 +125,21 @@ func (self *ChainManager) TransState() *state.StateDB {
} }
func (bc *ChainManager) setLastBlock() { func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) data, _ := bc.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.currentBlock = block
bc.lastBlockHash = block.Hash() bc.lastBlockHash = block.Hash()
bc.lastBlockNumber = block.Number.Uint64() bc.lastBlockNumber = block.Header().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(bc.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 TD=%v\n", bc.lastBlockNumber, bc.currentBlock.Hash(), bc.td)
} }
// Block creation & chain handling // Block creation & chain handling
@ -144,27 +147,28 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
bc.mu.RLock() bc.mu.RLock()
defer bc.mu.RUnlock() defer bc.mu.RUnlock()
var root interface{} var root []byte
hash := ZeroHash256 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)
} }
@ -175,37 +179,35 @@ func (bc *ChainManager) Reset() {
bc.mu.Lock() bc.mu.Lock()
defer bc.mu.Unlock() defer bc.mu.Unlock()
AddTestNetFunds(bc.genesisBlock) for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
bc.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 {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Number) chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
blocks := make([]*types.Block, int(self.currentBlock.Number.Int64())+1) blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
for block := self.currentBlock; block != nil; block = self.GetBlock(block.PrevHash) { for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
blocks[block.Number.Int64()] = block 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) bc.db.Put([]byte("LastBlock"), encodedBlock)
bc.currentBlock = block bc.currentBlock = block
bc.lastBlockHash = block.Hash() bc.lastBlockHash = block.Hash()
} }
@ -213,8 +215,8 @@ func (bc *ChainManager) insert(block *types.Block) {
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.RlpDataForStorage())
ethutil.Config.Db.Put(block.Hash(), encodedBlock) bc.db.Put(block.Hash(), encodedBlock)
} }
// Accessors // Accessors
@ -224,7 +226,7 @@ func (bc *ChainManager) Genesis() *types.Block {
// Block fetching methods // Block fetching methods
func (bc *ChainManager) HasBlock(hash []byte) bool { func (bc *ChainManager) HasBlock(hash []byte) bool {
data, _ := ethutil.Config.Db.Get(hash) data, _ := bc.db.Get(hash)
return len(data) != 0 return len(data) != 0
} }
@ -236,85 +238,98 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list) // XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
for i := uint64(0); i < max; i++ { for i := uint64(0); i < max; i++ {
block = self.GetBlock(block.Header().ParentHash)
chain = append(chain, block.Hash()) chain = append(chain, block.Hash())
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
if block.Number.Cmp(ethutil.Big0) <= 0 {
break break
} }
block = self.GetBlock(block.PrevHash)
} }
return return
} }
func (self *ChainManager) GetBlock(hash []byte) *types.Block { func (self *ChainManager) GetBlock(hash []byte) *types.Block {
data, _ := ethutil.Config.Db.Get(hash) data, _ := self.db.Get(hash)
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) GetUnclesInChain(block *types.Block, length int) (uncles []*types.Header) {
for i := 0; block != nil && i < length; i++ {
uncles = append(uncles, block.Uncles()...)
block = self.GetBlock(block.ParentHash())
}
return
}
func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) {
for i := 0; i < length; i++ {
block = self.GetBlock(block.ParentHash())
if block == nil {
break
}
blocks = append(blocks, block)
}
return
} }
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
block := self.currentBlock var block *types.Block
for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
}
if block != nil && block.Number.Uint64() == 0 && num != 0 { if num <= self.currentBlock.Number().Uint64() {
return nil block = self.currentBlock
for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
if block.Header().Number.Uint64() == num {
break
}
}
} }
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()) bc.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() {
@ -331,24 +346,26 @@ 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
} }
block.Td = td
self.mu.Lock() self.mu.Lock()
{ {
self.write(block) self.write(block)
cblock := self.currentBlock
if td.Cmp(self.td) > 0 { if td.Cmp(self.td) > 0 {
if block.Number.Cmp(new(big.Int).Add(self.currentBlock.Number, ethutil.Big1)) < 0 { 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.Number, block.Hash()[:4], self.currentBlock.Number, self.currentBlock.Hash()[:4]) 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.currentBlock.State().Copy() self.transState = state.New(cblock.Root(), self.db) //state.New(cblock.Trie().Copy())
} }
} }
@ -360,3 +377,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
return nil return nil
} }
// Satisfy state query interface
func (self *ChainManager) GetAccount(addr []byte) *state.StateObject {
return self.State().GetAccount(addr)
}

View file

@ -1,49 +1,39 @@
package core package core
import ( import (
"bytes"
"fmt" "fmt"
"os"
"path" "path"
"runtime" "runtime"
"strconv"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"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"
//logpkg "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/rlp"
) )
//var Logger logpkg.LogSystem
//var Log = logpkg.NewLogger("TEST")
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
//Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.InfoLevel)
//logpkg.AddLogSystem(Logger)
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH") 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 { func loadChain(fn string, t *testing.T) (types.Blocks, error) {
c1, err := ethutil.ReadAllFile(path.Join("..", "_data", fn)) fh, err := os.OpenFile(path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil { if err != nil {
fmt.Println(err) return nil, err
t.FailNow()
} }
value := ethutil.NewValueFromBytes([]byte(c1)) defer fh.Close()
blocks := make(types.Blocks, value.Len())
it := value.NewIterator() var chain types.Blocks
for it.Next() { if err := rlp.Decode(fh, &chain); err != nil {
blocks[it.Idx()] = types.NewBlockFromRlpValue(it.Value()) return nil, err
} }
return blocks return chain, nil
} }
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) { func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
@ -56,12 +46,24 @@ func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *
} }
func TestChainInsertions(t *testing.T) { func TestChainInsertions(t *testing.T) {
chain1 := loadChain("chain1", t) db, _ := ethdb.NewMemDatabase()
chain2 := loadChain("chain2", t)
chain1, err := loadChain("valid1", t)
if err != nil {
fmt.Println(err)
t.FailNow()
}
chain2, err := loadChain("valid2", t)
if err != nil {
fmt.Println(err)
t.FailNow()
}
var eventMux event.TypeMux var eventMux event.TypeMux
chainMan := NewChainManager(&eventMux) chainMan := NewChainManager(db, &eventMux)
txPool := NewTxPool(chainMan, nil, &eventMux) txPool := NewTxPool(&eventMux)
blockMan := NewBlockManager(txPool, chainMan, &eventMux) blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan) chainMan.SetProcessor(blockMan)
const max = 2 const max = 2
@ -73,5 +75,56 @@ func TestChainInsertions(t *testing.T) {
for i := 0; i < max; i++ { for i := 0; i < max; i++ {
<-done <-done
} }
fmt.Println(chainMan.CurrentBlock())
if bytes.Equal(chain2[len(chain2)-1].Hash(), chainMan.CurrentBlock().Hash()) {
t.Error("chain2 is canonical and shouldn't be")
}
if !bytes.Equal(chain1[len(chain1)-1].Hash(), chainMan.CurrentBlock().Hash()) {
t.Error("chain1 isn't canonical and should be")
}
}
func TestChainMultipleInsertions(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
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(db, &eventMux)
txPool := NewTxPool(&eventMux)
blockMan := NewBlockProcessor(db, txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan)
done := make(chan bool, max)
for i, chain := range chains {
// XXX the go routine would otherwise reference the same (chain[3]) variable and fail
i := i
chain := chain
go func() {
insertChain(done, chainMan, chain, t)
fmt.Println(i, "done")
}()
}
for i := 0; i < max; i++ {
<-done
}
if !bytes.Equal(chains[longest][len(chains[longest])-1].Hash(), chainMan.CurrentBlock().Hash()) {
t.Error("Invalid canonical chain")
}
} }

View file

@ -3,42 +3,41 @@ package core
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"time"
"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(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution { func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
evm := vm.New(env, vm.DebugVmTy) return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
return &Execution{vm: evm, address: address, input: input, Gas: gas, price: gasPrice, value: value}
} }
func (self *Execution) Addr() []byte { func (self *Execution) Addr() []byte {
return self.address return self.address
} }
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) { func (self *Execution) Call(codeAddr []byte, caller vm.ContextRef) ([]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.ContextRef) (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 {
caller.ReturnGas(self.Gas, self.price)
if self.vm.Env().Depth() == vm.MaxCallDepth {
// Consume all gas (by not returning it) and return a depth error
return nil, vm.DepthError{} return nil, vm.DepthError{}
} }
@ -55,22 +54,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)
env.State().Set(snapshot) if err != nil {
} 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
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.ContextRef) (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
@ -103,7 +104,7 @@ func (self *Filter) Find() []*state.Message {
// current parameters // current parameters
if self.bloomFilter(block) { if self.bloomFilter(block) {
// Get the messages of the block // Get the messages of the block
msgs, err := self.eth.BlockManager().GetMessages(block) msgs, err := self.eth.BlockProcessor().GetMessages(block)
if err != nil { if err != nil {
chainlogger.Warnln("err: filter get messages ", err) chainlogger.Warnln("err: filter get messages ", err)
@ -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

@ -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,37 @@ 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(db ethutil.Database) *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, genesis.Td = ethutil.Big0
// 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.Root(), db)
//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 {
@ -77,7 +77,6 @@ func NewTestManager() *TestManager {
fmt.Println("Could not create mem-db, failing") fmt.Println("Could not create mem-db, failing")
return nil return nil
} }
ethutil.Config.Db = db
testManager := &TestManager{} testManager := &TestManager{}
testManager.eventMux = new(event.TypeMux) testManager.eventMux = new(event.TypeMux)

View file

@ -1 +0,0 @@
package core

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"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" "github.com/ethereum/go-ethereum/state"
@ -28,18 +27,17 @@ import (
* 6) Derive new state root * 6) Derive new state root
*/ */
type StateTransition struct { type StateTransition struct {
coinbase, receiver []byte coinbase []byte
msg Message msg Message
gas, gasPrice *big.Int gas, gasPrice *big.Int
initialGas *big.Int initialGas *big.Int
value *big.Int value *big.Int
data []byte data []byte
state *state.StateDB state *state.StateDB
block *types.Block
cb, rec, sen *state.StateObject cb, rec, sen *state.StateObject
Env vm.Environment env vm.Environment
} }
type Message interface { type Message interface {
@ -69,16 +67,19 @@ func MessageGasValue(msg Message) *big.Int {
return new(big.Int).Mul(msg.Gas(), msg.GasPrice()) return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
} }
func NewStateTransition(coinbase *state.StateObject, msg Message, state *state.StateDB, block *types.Block) *StateTransition { func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateObject) *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} return &StateTransition{
} coinbase: coinbase.Address(),
env: env,
func (self *StateTransition) VmEnv() vm.Environment { msg: msg,
if self.Env == nil { gas: new(big.Int),
self.Env = NewEnv(self.state, self.msg, self.block) gasPrice: new(big.Int).Set(msg.GasPrice()),
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
state: env.State(),
cb: coinbase,
} }
return self.Env
} }
func (self *StateTransition) Coinbase() *state.StateObject { func (self *StateTransition) Coinbase() *state.StateObject {
@ -112,7 +113,7 @@ func (self *StateTransition) BuyGas() error {
sender := self.From() sender := self.From()
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 { if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", MessageGasValue(self.msg), 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()
@ -183,18 +184,25 @@ func (self *StateTransition) TransitionState() (ret []byte, err error) {
return return
} }
vmenv := self.VmEnv() vmenv := self.env
var ref vm.ClosureRef var ref vm.ContextRef
if MessageCreatesContract(msg) { if MessageCreatesContract(msg) {
self.rec = MakeContract(msg, 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.msg.Data(), self.gas, self.gasPrice, self.value) if err == nil {
ref.SetCode(ret) 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)
}
}
} else { } else {
ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value) 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
@ -211,20 +219,19 @@ func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
} }
func (self *StateTransition) RefundGas() { func (self *StateTransition) RefundGas() {
coinbaseSub := new(big.Int).Set(self.gas)
uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2)
for addr, ref := range self.state.Refunds() {
refund := ethutil.BigMin(uhalf, ref)
coinbaseSub.Add(self.gas, refund)
self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice()))
}
coinbase, sender := self.Coinbase(), self.From() coinbase, sender := self.Coinbase(), self.From()
coinbase.RefundGas(coinbaseSub, self.msg.GasPrice())
// Return remaining gas // Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice()) remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
sender.AddAmount(remaining) 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 { func (self *StateTransition) GasUsed() *big.Int {

View file

@ -1,16 +1,12 @@
package core package core
import ( import (
"bytes"
"container/list"
"fmt" "fmt"
"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/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
) )
var txplogger = logger.NewLogger("TXP") var txplogger = logger.NewLogger("TXP")
@ -26,101 +22,59 @@ const (
minGasPrice = 1000000 minGasPrice = 1000000
) )
var MinGasPrice = big.NewInt(10000000000000)
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
for e := pool.Front(); e != nil; e = e.Next() {
if it(e.Value.(*types.Transaction), e) {
break
}
}
}
func FindTx(pool *list.List, finder func(*types.Transaction, *list.Element) bool) *types.Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*types.Transaction); ok {
if finder(tx, e) {
return tx
}
}
}
return nil
}
type TxProcessor interface { type TxProcessor interface {
ProcessTransaction(tx *types.Transaction) ProcessTransaction(tx *types.Transaction)
} }
// The tx pool a thread safe transaction pool handler. In order to // The tx pool a thread safe transaction pool handler. In order to
// guarantee a non blocking pool we use a queue channel which can be // guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the // independently read without needing access to the actual pool.
// pool is being drained or synced for whatever reason the transactions
// will simple queue up and handled when the mutex is freed.
type TxPool struct { type TxPool struct {
// The mutex for accessing the Tx pool.
mutex sync.Mutex
// Queueing channel for reading and writing incoming // Queueing channel for reading and writing incoming
// transactions to // transactions to
queueChan chan *types.Transaction queueChan chan *types.Transaction
// Quiting channel // Quiting channel
quit chan bool quit chan bool
// The actual pool // The actual pool
pool *list.List //pool *list.List
txs map[string]*types.Transaction
SecondaryProcessor TxProcessor SecondaryProcessor TxProcessor
subscribers []chan TxMsg subscribers []chan TxMsg
chainManager *ChainManager eventMux *event.TypeMux
eventMux *event.TypeMux
} }
func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool { func NewTxPool(eventMux *event.TypeMux) *TxPool {
return &TxPool{ return &TxPool{
pool: list.New(), txs: make(map[string]*types.Transaction),
queueChan: make(chan *types.Transaction, txPoolQueueSize), queueChan: make(chan *types.Transaction, txPoolQueueSize),
quit: make(chan bool), quit: make(chan bool),
chainManager: chainManager, eventMux: eventMux,
eventMux: eventMux,
} }
} }
// Blocking function. Don't use directly. Use QueueTransaction instead
func (pool *TxPool) addTransaction(tx *types.Transaction) {
pool.mutex.Lock()
defer pool.mutex.Unlock()
pool.pool.PushBack(tx)
// Broadcast the transaction to the rest of the peers
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
// the merkle trie
block := pool.chainManager.CurrentBlock
// Something has gone horribly wrong if this happens
if block == nil {
return fmt.Errorf("No last block on the block chain")
}
if len(tx.To()) != 0 && len(tx.To()) != 20 { if len(tx.To()) != 0 && len(tx.To()) != 20 {
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To())) return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
} }
v, _, _ := tx.Curve() v, _, _ := tx.Curve()
if v > 28 || v < 27 { if v > 28 || v < 27 {
return fmt.Errorf("tx.v != (28 || 27)") return fmt.Errorf("tx.v != (28 || 27) => %v", v)
} }
/* XXX this kind of validation needs to happen elsewhere in the gui when sending txs.
Other clients should do their own validation. Value transfer could throw error
but doesn't necessarily invalidate the tx. Gas can still be payed for and miner
can still be rewarded for their inclusion and processing.
// Get the sender // Get the sender
senderAddr := tx.From() senderAddr := tx.From()
if senderAddr == nil { if senderAddr == nil {
return fmt.Errorf("invalid sender") return fmt.Errorf("invalid sender")
} }
sender := pool.chainManager.State().GetAccount(senderAddr) sender := pool.stateQuery.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
@ -128,21 +82,18 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
if sender.Balance().Cmp(totAmount) < 0 { if sender.Balance().Cmp(totAmount) < 0 {
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From()) return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
} }
*/
// Increment the nonce making each tx valid only once to prevent replay
// attacks
return nil return nil
} }
func (self *TxPool) Add(tx *types.Transaction) error { func (self *TxPool) addTx(tx *types.Transaction) {
hash := tx.Hash() self.txs[string(tx.Hash())] = tx
foundTx := FindTx(self.pool, func(tx *types.Transaction, e *list.Element) bool { }
return bytes.Compare(tx.Hash(), hash) == 0
})
if foundTx != nil { func (self *TxPool) Add(tx *types.Transaction) error {
return fmt.Errorf("Known transaction (%x)", hash[0:4]) if self.txs[string(tx.Hash())] != nil {
return fmt.Errorf("Known transaction (%x)", tx.Hash()[0:4])
} }
err := self.ValidateTransaction(tx) err := self.ValidateTransaction(tx)
@ -150,9 +101,16 @@ func (self *TxPool) Add(tx *types.Transaction) error {
return err return err
} }
self.addTransaction(tx) self.addTx(tx)
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.From()[:4], tx.To()[:4], tx.Value, tx.Hash()) var to string
if len(tx.To()) > 0 {
to = ethutil.Bytes2Hex(tx.To()[:4])
} else {
to = "[NEW_CONTRACT]"
}
txplogger.Debugf("(t) %x => %s (%v) %x\n", tx.From()[:4], to, tx.Value, tx.Hash())
// Notify the subscribers // Notify the subscribers
go self.eventMux.Post(TxPreEvent{tx}) go self.eventMux.Post(TxPreEvent{tx})
@ -161,7 +119,7 @@ func (self *TxPool) Add(tx *types.Transaction) error {
} }
func (self *TxPool) Size() int { func (self *TxPool) Size() int {
return self.pool.Len() return len(self.txs)
} }
func (self *TxPool) AddTransactions(txs []*types.Transaction) { func (self *TxPool) AddTransactions(txs []*types.Transaction) {
@ -174,64 +132,45 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) {
} }
} }
func (pool *TxPool) GetTransactions() []*types.Transaction { func (self *TxPool) GetTransactions() (txs types.Transactions) {
pool.mutex.Lock() txs = make(types.Transactions, self.Size())
defer pool.mutex.Unlock()
txList := make([]*types.Transaction, pool.pool.Len())
i := 0 i := 0
for e := pool.pool.Front(); e != nil; e = e.Next() { for _, tx := range self.txs {
tx := e.Value.(*types.Transaction) txs[i] = tx
txList[i] = tx
i++ i++
} }
return txList return
} }
func (pool *TxPool) RemoveInvalid(state *state.StateDB) { func (pool *TxPool) RemoveInvalid(query StateQuery) {
pool.mutex.Lock() var removedTxs types.Transactions
defer pool.mutex.Unlock() for _, tx := range pool.txs {
sender := query.GetAccount(tx.From())
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*types.Transaction)
sender := state.GetAccount(tx.From())
err := pool.ValidateTransaction(tx) err := pool.ValidateTransaction(tx)
fmt.Println(err, sender.Nonce, tx.Nonce())
if err != nil || sender.Nonce >= tx.Nonce() { if err != nil || sender.Nonce >= tx.Nonce() {
pool.pool.Remove(e) removedTxs = append(removedTxs, tx)
} }
} }
pool.RemoveSet(removedTxs)
} }
func (self *TxPool) RemoveSet(txs types.Transactions) { func (self *TxPool) RemoveSet(txs types.Transactions) {
self.mutex.Lock()
defer self.mutex.Unlock()
for _, tx := range txs { for _, tx := range txs {
EachTx(self.pool, func(t *types.Transaction, element *list.Element) bool { delete(self.txs, string(tx.Hash()))
if t == tx {
self.pool.Remove(element)
return true // To stop the loop
}
return false
})
} }
} }
func (pool *TxPool) Flush() []*types.Transaction { func (pool *TxPool) Flush() []*types.Transaction {
txList := pool.GetTransactions() txList := pool.GetTransactions()
pool.txs = make(map[string]*types.Transaction)
// Recreate a new list all together
// XXX Is this the fastest way?
pool.pool = list.New()
return txList return txList
} }
func (pool *TxPool) Start() { func (pool *TxPool) Start() {
//go pool.queueHandler()
} }
func (pool *TxPool) Stop() { func (pool *TxPool) Stop() {

View file

@ -0,0 +1,87 @@
package core
import (
"crypto/ecdsa"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/state"
)
// State query interface
type stateQuery struct{ db ethutil.Database }
func SQ() stateQuery {
db, _ := ethdb.NewMemDatabase()
return stateQuery{db: db}
}
func (self stateQuery) GetAccount(addr []byte) *state.StateObject {
return state.NewStateObject(addr, self.db)
}
func transaction() *types.Transaction {
return types.NewTransactionMessage(make([]byte, 20), ethutil.Big0, ethutil.Big0, ethutil.Big0, nil)
}
func setup() (*TxPool, *ecdsa.PrivateKey) {
var m event.TypeMux
key, _ := crypto.GenerateKey()
return NewTxPool(&m), key
}
func TestTxAdding(t *testing.T) {
pool, key := setup()
tx1 := transaction()
tx1.SignECDSA(key)
err := pool.Add(tx1)
if err != nil {
t.Error(err)
}
err = pool.Add(tx1)
if err == nil {
t.Error("added tx twice")
}
}
func TestAddInvalidTx(t *testing.T) {
pool, _ := setup()
tx1 := transaction()
err := pool.Add(tx1)
if err == nil {
t.Error("expected error")
}
}
func TestRemoveSet(t *testing.T) {
pool, _ := setup()
tx1 := transaction()
pool.addTx(tx1)
pool.RemoveSet(types.Transactions{tx1})
if pool.Size() > 0 {
t.Error("expected pool size to be 0")
}
}
func TestRemoveInvalid(t *testing.T) {
pool, key := setup()
tx1 := transaction()
pool.addTx(tx1)
pool.RemoveInvalid(SQ())
if pool.Size() > 0 {
t.Error("expected pool size to be 0")
}
tx1.SetNonce(1)
tx1.SignECDSA(key)
pool.addTx(tx1)
pool.RemoveInvalid(SQ())
if pool.Size() != 1 {
t.Error("expected pool size to be 1, is", pool.Size())
}
}

View file

@ -9,41 +9,233 @@ 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/state" "github.com/ethereum/go-ethereum/rlp"
"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 {
// Preset Hash for mock
HeaderHash []byte
ParentHeaderHash []byte
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 {
var extblock struct {
Header *Header
Txs []*Transaction
Uncles []*Header
TD *big.Int // optional
}
if err := s.Decode(&extblock); err != nil {
return err
}
self.header = extblock.Header
self.uncles = extblock.Uncles
self.transactions = extblock.Txs
self.Td = extblock.TD
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) 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) 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) Root() []byte { return self.header.Root }
func (self *Block) SetRoot(root []byte) { self.header.Root = root }
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
// Implement pow.Block
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 self.header.HashNoNonce() }
func (self *Block) Hash() []byte {
if self.HeaderHash != nil {
return self.HeaderHash
} else {
return self.header.Hash()
}
}
func (self *Block) ParentHash() []byte {
if self.ParentHeaderHash != nil {
return self.ParentHeaderHash
} else {
return self.header.ParentHash
}
}
func (self *Block) String() string {
return fmt.Sprintf(`BLOCK(%x): Size: %v TD: %v {
Header:
[
%v
]
Transactions:
%v
Uncles:
%v
}
`, self.header.Hash(), self.Size(), self.Td, 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 +257,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 []interface{}{self.header(), self.transactions, self.rlpUncles()}
}
// Implement pow.Block
func (self *Block) N() []byte { return self.Nonce }

View file

@ -1,6 +1,7 @@
package types package types
import ( import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -11,10 +12,11 @@ type DerivableList interface {
} }
func DeriveSha(list DerivableList) []byte { func DeriveSha(list DerivableList) []byte {
trie := trie.New(ethutil.Config.Db, "") db, _ := ethdb.NewMemDatabase()
trie := trie.New(nil, 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,11 +1,14 @@
package types package types
import ( import (
"bytes"
"crypto/ecdsa"
"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/rlp"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
) )
@ -14,22 +17,22 @@ func IsContractAddr(addr []byte) bool {
} }
type Transaction struct { type Transaction struct {
nonce uint64 AccountNonce uint64
recipient []byte Price *big.Int
value *big.Int GasLimit *big.Int
gas *big.Int Recipient []byte
gasPrice *big.Int Amount *big.Int
data []byte Payload []byte
v byte V uint64
r, s []byte R, S []byte
} }
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} 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} return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
} }
func NewTransactionFromBytes(data []byte) *Transaction { func NewTransactionFromBytes(data []byte) *Transaction {
@ -39,7 +42,7 @@ 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)
@ -47,33 +50,33 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
} }
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 (self *Transaction) Data() []byte { func (self *Transaction) Data() []byte {
return self.data return self.Payload
} }
func (self *Transaction) Gas() *big.Int { func (self *Transaction) Gas() *big.Int {
return self.gas return self.GasLimit
} }
func (self *Transaction) GasPrice() *big.Int { func (self *Transaction) GasPrice() *big.Int {
return self.gasPrice return self.Price
} }
func (self *Transaction) Value() *big.Int { func (self *Transaction) Value() *big.Int {
return self.value return self.Amount
} }
func (self *Transaction) Nonce() uint64 { func (self *Transaction) Nonce() uint64 {
return self.nonce return self.AccountNonce
} }
func (self *Transaction) SetNonce(nonce uint64) { func (self *Transaction) SetNonce(AccountNonce uint64) {
self.nonce = nonce self.AccountNonce = AccountNonce
} }
func (self *Transaction) From() []byte { func (self *Transaction) From() []byte {
@ -81,13 +84,13 @@ func (self *Transaction) From() []byte {
} }
func (self *Transaction) To() []byte { func (self *Transaction) To() []byte {
return self.recipient 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
} }
@ -108,8 +111,8 @@ 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
} }
@ -130,44 +133,41 @@ 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) SignECDSA(key *ecdsa.PrivateKey) error {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.recipient, tx.Value, tx.Data} return tx.Sign(crypto.FromECDSA(key))
// TODO Remove prefixing zero's
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 { func (tx *Transaction) RlpData() interface{} {
return ethutil.NewValue(tx.RlpData()) data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
} }
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()
} }
func (tx *Transaction) String() string { func (tx *Transaction) String() string {
@ -178,25 +178,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.From(), 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
@ -219,5 +222,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

@ -13,10 +13,12 @@ type VMEnv struct {
block *types.Block block *types.Block
msg Message msg Message
depth int depth int
chain *ChainManager
} }
func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv { func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv {
return &VMEnv{ return &VMEnv{
chain: chain,
state: state, state: state,
block: block, block: block,
msg: msg, msg: msg,
@ -24,17 +26,22 @@ func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv {
} }
func (self *VMEnv) Origin() []byte { return self.msg.From() } 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) 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) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() } 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) GetHash(n uint64) []byte {
if block := self.chain.GetBlockByNumber(n); block != nil {
return block.Hash()
}
return nil
}
func (self *VMEnv) AddLog(log state.Log) { func (self *VMEnv) AddLog(log state.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
@ -46,16 +53,16 @@ func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution
return NewExecution(self, addr, data, gas, price, value) return NewExecution(self, 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.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
return exe.Call(addr, me) return exe.Call(addr, me)
} }
func (self *VMEnv) CallCode(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) CallCode(me vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(me.Address(), data, gas, price, value) exe := self.vm(me.Address(), data, gas, price, value)
return exe.Call(addr, me) return exe.Call(addr, me)
} }
func (self *VMEnv) Create(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) { func (self *VMEnv) Create(me vm.ContextRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
exe := self.vm(addr, data, gas, price, value) exe := self.vm(addr, data, gas, price, value)
return exe.Create(me) return exe.Create(me)
} }

View file

@ -1,11 +1,13 @@
package eth package eth
import ( import (
"fmt"
"net" "net"
"sync" "sync"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"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"
ethlogger "github.com/ethereum/go-ethereum/logger" ethlogger "github.com/ethereum/go-ethereum/logger"
@ -16,9 +18,30 @@ import (
) )
const ( const (
seedNodeAddress = "poc-7.ethdev.com:30300" seedNodeAddress = "poc-8.ethdev.com:30303"
) )
type Config struct {
Name string
Version string
Identifier string
KeyStore string
DataDir string
LogFile string
LogLevel int
KeyRing string
MaxPeers int
Port string
NATType string
PMPGateway string
Shh bool
Dial bool
KeyManager *crypto.KeyManager
}
var logger = ethlogger.NewLogger("SERV") var logger = ethlogger.NewLogger("SERV")
type Ethereum struct { type Ethereum struct {
@ -32,13 +55,13 @@ type Ethereum struct {
//*** SERVICES *** //*** SERVICES ***
// State manager for processing new blocks and managing the over all states // State manager for processing new blocks and managing the over all states
blockManager *core.BlockManager blockProcessor *core.BlockProcessor
txPool *core.TxPool txPool *core.TxPool
chainManager *core.ChainManager chainManager *core.ChainManager
blockPool *BlockPool blockPool *BlockPool
whisper *whisper.Whisper whisper *whisper.Whisper
server *p2p.Server net *p2p.Server
eventMux *event.TypeMux eventMux *event.TypeMux
txSub event.Subscription txSub event.Subscription
blockSub event.Subscription blockSub event.Subscription
@ -47,6 +70,7 @@ type Ethereum struct {
keyManager *crypto.KeyManager keyManager *crypto.KeyManager
clientIdentity p2p.ClientIdentity clientIdentity p2p.ClientIdentity
logger ethlogger.LogSystem
synclock sync.Mutex synclock sync.Mutex
syncGroup sync.WaitGroup syncGroup sync.WaitGroup
@ -54,47 +78,82 @@ type Ethereum struct {
Mining bool Mining bool
} }
func New(db ethutil.Database, identity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, port string, maxPeers int) (*Ethereum, error) { func New(config *Config) (*Ethereum, error) {
// Boostrap database
logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel)
db, err := ethdb.NewLDBDatabase("blockchain")
if err != nil {
return nil, err
}
// Perform database sanity checks
d, _ := db.Get([]byte("ProtocolVersion"))
protov := ethutil.NewValue(d).Uint()
if protov != ProtocolVersion && protov != 0 {
return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, ethutil.Config.ExecPath+"/database")
}
// Create new keymanager
var keyManager *crypto.KeyManager
switch config.KeyStore {
case "db":
keyManager = crypto.NewDBKeyManager(db)
case "file":
keyManager = crypto.NewFileKeyManager(config.DataDir)
default:
return nil, fmt.Errorf("unknown keystore type: %s", config.KeyStore)
}
// Initialise the keyring
keyManager.Init(config.KeyRing, 0, false)
// Create a new client id for this instance. This will help identifying the node on the network
clientId := p2p.NewSimpleClientIdentity(config.Name, config.Version, config.Identifier, keyManager.PublicKey())
saveProtocolVersion(db) saveProtocolVersion(db)
ethutil.Config.Db = db //ethutil.Config.Db = db
eth := &Ethereum{ eth := &Ethereum{
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
quit: make(chan bool), quit: make(chan bool),
db: db, db: db,
keyManager: keyManager, keyManager: keyManager,
clientIdentity: identity, clientIdentity: clientId,
blacklist: p2p.NewBlacklist(), blacklist: p2p.NewBlacklist(),
eventMux: &event.TypeMux{}, eventMux: &event.TypeMux{},
logger: logger,
} }
eth.chainManager = core.NewChainManager(eth.EventMux()) eth.chainManager = core.NewChainManager(db, eth.EventMux())
eth.txPool = core.NewTxPool(eth.chainManager, eth.EventMux()) eth.txPool = core.NewTxPool(eth.EventMux())
eth.blockManager = core.NewBlockManager(eth.txPool, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockManager) eth.chainManager.SetProcessor(eth.blockProcessor)
eth.whisper = whisper.New() eth.whisper = whisper.New()
hasBlock := eth.chainManager.HasBlock hasBlock := eth.chainManager.HasBlock
insertChain := eth.chainManager.InsertChain insertChain := eth.chainManager.InsertChain
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify) eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
// Start services
eth.txPool.Start()
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool) ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()} protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
server := &p2p.Server{ nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
Identity: identity, if err != nil {
MaxPeers: maxPeers, return nil, err
Protocols: protocols, }
ListenAddr: ":" + port, fmt.Println(nat)
Blacklist: eth.blacklist,
NAT: nat, eth.net = &p2p.Server{
Identity: clientId,
MaxPeers: config.MaxPeers,
Protocols: protocols,
Blacklist: eth.blacklist,
NAT: p2p.UPNP(),
NoDial: !config.Dial,
} }
eth.server = server if len(config.Port) > 0 {
eth.net.ListenAddr = ":" + config.Port
}
return eth, nil return eth, nil
} }
@ -103,6 +162,10 @@ func (s *Ethereum) KeyManager() *crypto.KeyManager {
return s.keyManager return s.keyManager
} }
func (s *Ethereum) Logger() ethlogger.LogSystem {
return s.logger
}
func (s *Ethereum) ClientIdentity() p2p.ClientIdentity { func (s *Ethereum) ClientIdentity() p2p.ClientIdentity {
return s.clientIdentity return s.clientIdentity
} }
@ -111,8 +174,8 @@ func (s *Ethereum) ChainManager() *core.ChainManager {
return s.chainManager return s.chainManager
} }
func (s *Ethereum) BlockManager() *core.BlockManager { func (s *Ethereum) BlockProcessor() *core.BlockProcessor {
return s.blockManager return s.blockProcessor
} }
func (s *Ethereum) TxPool() *core.TxPool { func (s *Ethereum) TxPool() *core.TxPool {
@ -144,25 +207,31 @@ func (s *Ethereum) IsListening() bool {
} }
func (s *Ethereum) PeerCount() int { func (s *Ethereum) PeerCount() int {
return s.server.PeerCount() return s.net.PeerCount()
} }
func (s *Ethereum) Peers() []*p2p.Peer { func (s *Ethereum) Peers() []*p2p.Peer {
return s.server.Peers() return s.net.Peers()
} }
func (s *Ethereum) MaxPeers() int { func (s *Ethereum) MaxPeers() int {
return s.server.MaxPeers return s.net.MaxPeers
} }
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start(seed bool) error { func (s *Ethereum) Start(seed bool) error {
err := s.server.Start() err := s.net.Start()
if err != nil { if err != nil {
return err return err
} }
// Start services
s.txPool.Start()
s.blockPool.Start() s.blockPool.Start()
s.whisper.Start()
if s.whisper != nil {
s.whisper.Start()
}
// broadcast transactions // broadcast transactions
s.txSub = s.eventMux.Subscribe(core.TxPreEvent{}) s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
@ -191,7 +260,7 @@ func (self *Ethereum) SuggestPeer(addr string) error {
return err return err
} }
self.server.SuggestPeer(netaddr.IP, netaddr.Port, nil) self.net.SuggestPeer(netaddr.IP, netaddr.Port, nil)
return nil return nil
} }
@ -210,7 +279,9 @@ func (s *Ethereum) Stop() {
s.txPool.Stop() s.txPool.Stop()
s.eventMux.Stop() s.eventMux.Stop()
s.blockPool.Stop() s.blockPool.Stop()
s.whisper.Stop() if s.whisper != nil {
s.whisper.Stop()
}
logger.Infoln("Server stopped") logger.Infoln("Server stopped")
close(s.shutdownChan) close(s.shutdownChan)
@ -227,15 +298,17 @@ func (self *Ethereum) txBroadcastLoop() {
// automatically stops if unsubscribe // automatically stops if unsubscribe
for obj := range self.txSub.Chan() { for obj := range self.txSub.Chan() {
event := obj.(core.TxPreEvent) event := obj.(core.TxPreEvent)
self.server.Broadcast("eth", TxMsg, []interface{}{event.Tx.RlpData()}) self.net.Broadcast("eth", TxMsg, event.Tx.RlpData())
} }
} }
func (self *Ethereum) blockBroadcastLoop() { func (self *Ethereum) blockBroadcastLoop() {
// automatically stops if unsubscribe // automatically stops if unsubscribe
for obj := range self.txSub.Chan() { for obj := range self.blockSub.Chan() {
event := obj.(core.NewMinedBlockEvent) switch ev := obj.(type) {
self.server.Broadcast("eth", NewBlockMsg, event.Block.Value().Val) case core.NewMinedBlockEvent:
self.net.Broadcast("eth", NewBlockMsg, ev.Block.RlpData(), ev.Block.Td)
}
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@ const (
ErrInvalidBlock ErrInvalidBlock
ErrInvalidPoW ErrInvalidPoW
ErrUnrequestedBlock ErrUnrequestedBlock
ErrInsufficientChainInfo
) )
var errorToString = map[int]string{ var errorToString = map[int]string{
@ -30,6 +31,7 @@ var errorToString = map[int]string{
ErrInvalidBlock: "Invalid block", ErrInvalidBlock: "Invalid block",
ErrInvalidPoW: "Invalid PoW", ErrInvalidPoW: "Invalid PoW",
ErrUnrequestedBlock: "Unrequested block", ErrUnrequestedBlock: "Unrequested block",
ErrInsufficientChainInfo: "Insufficient chain info",
} }
type protocolError struct { type protocolError struct {
@ -52,18 +54,17 @@ func ProtocolError(code int, format string, params ...interface{}) (err *protoco
} }
func (self protocolError) Error() (message string) { func (self protocolError) Error() (message string) {
message = self.message if len(message) == 0 {
if message == "" { var ok bool
message, ok := errorToString[self.Code] self.message, ok = errorToString[self.Code]
if !ok { if !ok {
panic("invalid error code") panic("invalid error code")
} }
if self.format != "" { if self.format != "" {
message += ": " + fmt.Sprintf(self.format, self.params...) self.message += ": " + fmt.Sprintf(self.format, self.params...)
} }
self.message = message
} }
return return self.message
} }
func (self *protocolError) Fatal() bool { func (self *protocolError) Fatal() bool {

View file

@ -3,7 +3,7 @@ package eth
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math" "io"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -13,7 +13,7 @@ import (
) )
const ( const (
ProtocolVersion = 49 ProtocolVersion = 51
NetworkId = 0 NetworkId = 0
ProtocolLength = uint64(8) ProtocolLength = uint64(8)
ProtocolMaxMsgSize = 10 * 1024 * 1024 ProtocolMaxMsgSize = 10 * 1024 * 1024
@ -67,6 +67,8 @@ type newBlockMsgData struct {
TD *big.Int TD *big.Int
} }
const maxHashes = 255
type getBlockHashesMsgData struct { type getBlockHashesMsgData struct {
Hash []byte Hash []byte
Amount uint64 Amount uint64
@ -95,14 +97,13 @@ func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPoo
blockPool: blockPool, blockPool: blockPool,
rw: rw, rw: rw,
peer: peer, peer: peer,
id: (string)(peer.Identity().Pubkey()), id: fmt.Sprintf("%x", peer.Identity().Pubkey()[:8]),
} }
err = self.handleStatus() err = self.handleStatus()
if err == nil { if err == nil {
for { for {
err = self.handle() err = self.handle()
if err != nil { if err != nil {
fmt.Println(err)
self.blockPool.RemovePeer(self.id) self.blockPool.RemovePeer(self.id)
break break
} }
@ -117,112 +118,118 @@ func (self *ethProtocol) handle() error {
return err return err
} }
if msg.Size > ProtocolMaxMsgSize { if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
} }
// make sure that the payload has been fully consumed // make sure that the payload has been fully consumed
defer msg.Discard() defer msg.Discard()
switch msg.Code { switch msg.Code {
case GetTxMsg: // ignore
case StatusMsg: case StatusMsg:
return ProtocolError(ErrExtraStatusMsg, "") return self.protoError(ErrExtraStatusMsg, "")
case TxMsg: case TxMsg:
// TODO: rework using lazy RLP stream // TODO: rework using lazy RLP stream
var txs []*types.Transaction var txs []*types.Transaction
if err := msg.Decode(&txs); err != nil { if err := msg.Decode(&txs); err != nil {
return ProtocolError(ErrDecode, "%v", err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
self.txPool.AddTransactions(txs) self.txPool.AddTransactions(txs)
case GetBlockHashesMsg: case GetBlockHashesMsg:
var request getBlockHashesMsgData var request getBlockHashesMsgData
if err := msg.Decode(&request); err != nil { if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err) return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
}
//request.Amount = uint64(math.Min(float64(maxHashes), float64(request.Amount)))
if request.Amount > maxHashes {
request.Amount = maxHashes
} }
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount) hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...) return p2p.EncodeMsg(self.rw, BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
case BlockHashesMsg: case BlockHashesMsg:
// TODO: redo using lazy decode , this way very inefficient on known chains // TODO: redo using lazy decode , this way very inefficient on known chains
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size)) msgStream := rlp.NewStream(msg.Payload)
var err error var err error
var i int
iter := func() (hash []byte, ok bool) { iter := func() (hash []byte, ok bool) {
hash, err = msgStream.Bytes() hash, err = msgStream.Bytes()
if err == nil { if err == nil {
i++
ok = true ok = true
} else {
if err != io.EOF {
self.protoError(ErrDecode, "msg %v: after %v hashes : %v", msg, i, err)
}
} }
return return
} }
self.blockPool.AddBlockHashes(iter, self.id) self.blockPool.AddBlockHashes(iter, self.id)
if err != nil && err != rlp.EOL {
return ProtocolError(ErrDecode, "%v", err)
}
case GetBlocksMsg: case GetBlocksMsg:
var blockHashes [][]byte msgStream := rlp.NewStream(msg.Payload)
if err := msg.Decode(&blockHashes); err != nil {
return ProtocolError(ErrDecode, "%v", err)
}
max := int(math.Min(float64(len(blockHashes)), blockHashesBatchSize))
var blocks []interface{} var blocks []interface{}
for i, hash := range blockHashes { var i int
if i >= max { for {
break i++
var hash []byte
if err := msgStream.Decode(&hash); err != nil {
if err == io.EOF {
break
} else {
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
}
} }
block := self.chainManager.GetBlock(hash) block := self.chainManager.GetBlock(hash)
if block != nil { if block != nil {
blocks = append(blocks, block.Value().Raw()) blocks = append(blocks, block)
}
if i == blockHashesBatchSize {
break
} }
} }
return self.rw.EncodeMsg(BlocksMsg, blocks...) return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...)
case BlocksMsg: case BlocksMsg:
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size)) msgStream := rlp.NewStream(msg.Payload)
for { for {
var block *types.Block var block types.Block
if err := msgStream.Decode(&block); err != nil { if err := msgStream.Decode(&block); err != nil {
if err == rlp.EOL { if err == io.EOF {
break break
} else { } else {
return ProtocolError(ErrDecode, "%v", err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
} }
self.blockPool.AddBlock(block, self.id) self.blockPool.AddBlock(&block, self.id)
} }
case NewBlockMsg: case NewBlockMsg:
var request newBlockMsgData var request newBlockMsgData
if err := msg.Decode(&request); err != nil { if err := msg.Decode(&request); err != nil {
return ProtocolError(ErrDecode, "%v", err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
hash := request.Block.Hash() hash := request.Block.Hash()
// to simplify backend interface adding a new block // to simplify backend interface adding a new block
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer // uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
// (or selected as new best peer) // (or selected as new best peer)
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) { 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) self.blockPool.AddBlock(request.Block, self.id)
} }
default: default:
return ProtocolError(ErrInvalidMsgCode, "%v", msg.Code) return self.protoError(ErrInvalidMsgCode, "%v", msg.Code)
} }
return nil return nil
} }
type statusMsgData struct { type statusMsgData struct {
ProtocolVersion uint ProtocolVersion uint32
NetworkId uint NetworkId uint32
TD *big.Int TD *big.Int
CurrentBlock []byte CurrentBlock []byte
GenesisBlock []byte GenesisBlock []byte
@ -253,56 +260,56 @@ func (self *ethProtocol) handleStatus() error {
} }
if msg.Code != StatusMsg { if msg.Code != StatusMsg {
return ProtocolError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) return self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
} }
if msg.Size > ProtocolMaxMsgSize { if msg.Size > ProtocolMaxMsgSize {
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
} }
var status statusMsgData var status statusMsgData
if err := msg.Decode(&status); err != nil { if err := msg.Decode(&status); err != nil {
return ProtocolError(ErrDecode, "%v", err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
_, _, genesisBlock := self.chainManager.Status() _, _, genesisBlock := self.chainManager.Status()
if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 { if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
return ProtocolError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock) return self.protoError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
} }
if status.NetworkId != NetworkId { if status.NetworkId != NetworkId {
return ProtocolError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId) return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
} }
if ProtocolVersion != status.ProtocolVersion { if ProtocolVersion != status.ProtocolVersion {
return ProtocolError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion) return self.protoError(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.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.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
self.peer.Infoln("AddPeer(IGNORED)")
return nil return nil
} }
func (self *ethProtocol) requestBlockHashes(from []byte) error { func (self *ethProtocol) requestBlockHashes(from []byte) error {
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4]) self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
return self.rw.EncodeMsg(GetBlockHashesMsg, from, blockHashesBatchSize) return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(blockHashesBatchSize))
} }
func (self *ethProtocol) requestBlocks(hashes [][]byte) error { func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
self.peer.Debugf("fetching %v blocks", len(hashes)) self.peer.Debugf("fetching %v blocks", len(hashes))
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)) return p2p.EncodeMsg(self.rw, GetBlocksMsg, ethutil.ByteSliceToInterface(hashes)...)
} }
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) { func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
err = ProtocolError(code, format, params...) err = ProtocolError(code, format, params...)
if err.Fatal() { if err.Fatal() {
self.peer.Errorln(err) self.peer.Errorln("err %v", err)
// disconnect
} else { } else {
self.peer.Debugln(err) self.peer.Debugf("fyi %v", err)
} }
return return
} }
@ -310,10 +317,10 @@ func (self *ethProtocol) protoError(code int, format string, params ...interface
func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) { func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
err := ProtocolError(code, format, params...) err := ProtocolError(code, format, params...)
if err.Fatal() { if err.Fatal() {
self.peer.Errorln(err) self.peer.Errorln("err %v", err)
// disconnect // disconnect
} else { } else {
self.peer.Debugln(err) self.peer.Debugf("fyi %v", err)
} }
} }

View file

@ -1,37 +1,46 @@
package eth package eth
import ( import (
"bytes"
"io" "io"
"log"
"math/big" "math/big"
"os"
"testing" "testing"
"time"
"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/ethutil"
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
) )
var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
type testMsgReadWriter struct { type testMsgReadWriter struct {
in chan p2p.Msg in chan p2p.Msg
out chan p2p.Msg out []p2p.Msg
} }
func (self *testMsgReadWriter) In(msg p2p.Msg) { func (self *testMsgReadWriter) In(msg p2p.Msg) {
self.in <- msg self.in <- msg
} }
func (self *testMsgReadWriter) Out(msg p2p.Msg) { func (self *testMsgReadWriter) Out() (msg p2p.Msg, ok bool) {
self.in <- msg if len(self.out) > 0 {
msg = self.out[0]
self.out = self.out[1:]
ok = true
}
return
} }
func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error { func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
self.out <- msg self.out = append(self.out, msg)
return nil 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) { func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
msg, ok := <-self.in msg, ok := <-self.in
if !ok { if !ok {
@ -40,145 +49,83 @@ func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
return msg, nil return msg, nil
} }
func errorCheck(t *testing.T, expCode int, err error) { type testTxPool struct {
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 getTransactions func() []*types.Transaction
addTransactions func(txs []*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) { type testChainManager struct {
if self.getTransactions != nil { getBlockHashes func(hash []byte, amount uint64) (hashes [][]byte)
txs = self.getTransactions() getBlock func(hash []byte) *types.Block
} status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
return
} }
func (self *TestBackend) AddTransactions(txs []*types.Transaction) { type testBlockPool struct {
addBlockHashes func(next func() ([]byte, bool), peerId string)
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, peerError func(int, string, ...interface{})) (best bool)
removePeer func(peerId string)
}
// func (self *testTxPool) GetTransactions() (txs []*types.Transaction) {
// if self.getTransactions != nil {
// txs = self.getTransactions()
// }
// return
// }
func (self *testTxPool) AddTransactions(txs []*types.Transaction) {
if self.addTransactions != nil { if self.addTransactions != nil {
self.addTransactions(txs) self.addTransactions(txs)
} }
} }
func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) { func (self *testChainManager) GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte) {
if self.getBlockHashes != nil { if self.getBlockHashes != nil {
hashes = self.getBlockHashes(hash, amount) hashes = self.getBlockHashes(hash, amount)
} }
return return
} }
<<<<<<< HEAD func (self *testChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
<<<<<<< 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 { if self.status != nil {
td, currentBlock, genesisBlock = self.status() td, currentBlock, genesisBlock = self.status()
} }
return return
} }
<<<<<<< HEAD func (self *testChainManager) GetBlock(hash []byte) (block *types.Block) {
<<<<<<< HEAD if self.getBlock != nil {
======= block = self.getBlock(hash)
>>>>>>> eth protocol changes }
return
}
func (self *testBlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string) {
if self.addBlockHashes != nil {
self.addBlockHashes(next, peerId)
}
}
func (self *testBlockPool) AddBlock(block *types.Block, peerId string) {
if self.addBlock != nil {
self.addBlock(block, peerId)
}
}
func (self *testBlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool) {
if self.addPeer != nil {
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, peerError)
}
return
}
func (self *testBlockPool) RemovePeer(peerId string) {
if self.removePeer != nil {
self.removePeer(peerId)
}
}
// TODO: refactor this into p2p/client_identity // TODO: refactor this into p2p/client_identity
type peerId struct { type peerId struct {
pubkey []byte pubkey []byte
@ -201,32 +148,119 @@ func testPeer() *p2p.Peer {
return p2p.NewPeer(&peerId{}, []p2p.Cap{}) return p2p.NewPeer(&peerId{}, []p2p.Cap{})
} }
func TestErrNoStatusMsg(t *testing.T) { type ethProtocolTester struct {
<<<<<<< HEAD quit chan error
======= rw *testMsgReadWriter // p2p.MsgReadWriter
func TestEth(t *testing.T) { txPool *testTxPool // txPool
>>>>>>> initial commit for eth-p2p integration chainManager *testChainManager // chainManager
======= blockPool *testBlockPool // blockPool
>>>>>>> eth protocol changes t *testing.T
quit := make(chan bool) }
rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
testBackend := &TestBackend{} func newEth(t *testing.T) *ethProtocolTester {
var err error return &ethProtocolTester{
go func() { quit: make(chan error),
<<<<<<< HEAD rw: &testMsgReadWriter{in: make(chan p2p.Msg, 10)},
<<<<<<< HEAD txPool: &testTxPool{},
err = runEthProtocol(testBackend, testPeer(), rw) chainManager: &testChainManager{},
======= blockPool: &testBlockPool{},
err = runEthProtocol(testBackend, nil, rw) t: t,
>>>>>>> initial commit for eth-p2p integration }
======= }
err = runEthProtocol(testBackend, testPeer(), rw)
>>>>>>> eth protocol changes func (self *ethProtocolTester) reset() {
close(quit) self.rw = &testMsgReadWriter{in: make(chan p2p.Msg, 10)}
}() self.quit = make(chan error)
statusMsg := p2p.NewMsg(4) }
rw.In(statusMsg)
<-quit func (self *ethProtocolTester) checkError(expCode int, delay time.Duration) (err error) {
errorCheck(t, ErrNoStatusMsg, err) var timer = time.After(delay)
// read(t, remote, []byte("hello, world"), nil) select {
case err = <-self.quit:
case <-timer:
self.t.Errorf("no error after %v, expected %v", delay, expCode)
return
}
perr, ok := err.(*protocolError)
if ok && perr != nil {
if code := perr.Code; code != expCode {
self.t.Errorf("expected protocol error (code %v), got %v (%v)", expCode, code, err)
}
} else {
self.t.Errorf("expected protocol error (code %v), got %v", expCode, err)
}
return
}
func (self *ethProtocolTester) In(msg p2p.Msg) {
self.rw.In(msg)
}
func (self *ethProtocolTester) Out() (p2p.Msg, bool) {
return self.rw.Out()
}
func (self *ethProtocolTester) checkMsg(i int, code uint64, val interface{}) (msg p2p.Msg) {
if i >= len(self.rw.out) {
self.t.Errorf("expected at least %v msgs, got %v", i, len(self.rw.out))
return
}
msg = self.rw.out[i]
if msg.Code != code {
self.t.Errorf("expected msg code %v, got %v", code, msg.Code)
}
if val != nil {
if err := msg.Decode(val); err != nil {
self.t.Errorf("rlp encoding error: %v", err)
}
}
return
}
func (self *ethProtocolTester) run() {
err := runEthProtocol(self.txPool, self.chainManager, self.blockPool, testPeer(), self.rw)
self.quit <- err
}
func TestStatusMsgErrors(t *testing.T) {
logInit()
eth := newEth(t)
td := ethutil.Big1
currentBlock := []byte{1}
genesis := []byte{2}
eth.chainManager.status = func() (*big.Int, []byte, []byte) { return td, currentBlock, genesis }
go eth.run()
statusMsg := p2p.NewMsg(4)
eth.In(statusMsg)
delay := 1 * time.Second
eth.checkError(ErrNoStatusMsg, delay)
var status statusMsgData
eth.checkMsg(0, StatusMsg, &status) // first outgoing msg should be StatusMsg
if status.TD.Cmp(td) != 0 ||
status.ProtocolVersion != ProtocolVersion ||
status.NetworkId != NetworkId ||
status.TD.Cmp(td) != 0 ||
bytes.Compare(status.CurrentBlock, currentBlock) != 0 ||
bytes.Compare(status.GenesisBlock, genesis) != 0 {
t.Errorf("incorrect outgoing status")
}
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(48), uint32(0), td, currentBlock, genesis)
eth.In(statusMsg)
eth.checkError(ErrProtocolVersionMismatch, delay)
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(49), uint32(1), td, currentBlock, genesis)
eth.In(statusMsg)
eth.checkError(ErrNetworkIdMismatch, delay)
eth.reset()
go eth.run()
statusMsg = p2p.NewMsg(0, uint32(49), uint32(0), td, currentBlock, []byte{3})
eth.In(statusMsg)
eth.checkError(ErrGenesisBlockMismatch, delay)
} }

27
eth/test/README.md Normal file
View file

@ -0,0 +1,27 @@
= Integration tests for eth protocol and blockpool
This is a simple suite of tests to fire up a local test node with peers to test blockchain synchronisation and download.
The scripts call ethereum (assumed to be compiled in go-ethereum root).
To run a test:
. run.sh 00 02
Without arguments, all tests are run.
Peers are launched with preloaded imported chains. In order to prevent them from synchronizing with each other they are set with `-dial=false` and `-maxpeer 1` options. They log into `/tmp/eth.test/nodes/XX` where XX is the last two digits of their port.
Chains to import can be bootstrapped by letting nodes mine for some time. This is done with
. bootstrap.sh
Only the relative timing and forks matter so they should work if the bootstrap script is rerun.
The reference blockchain of tests are soft links to these import chains and check at the end of a test run.
Connecting to peers and exporting blockchain is scripted with JS files executed by the JSRE, see `tests/XX.sh`.
Each test is set with a timeout. This may vary on different computers so adjust sensibly.
If you kill a test before it completes, do not forget to kill all the background processes, since they will impact the result. Use:
killall ethereum

9
eth/test/bootstrap.sh Normal file
View file

@ -0,0 +1,9 @@
#!/bin/bash
# bootstrap chains - used to regenerate tests/chains/*.chain
mkdir -p chains
bash ./mine.sh 00 10
bash ./mine.sh 01 5 00
bash ./mine.sh 02 10 00
bash ./mine.sh 03 5 02
bash ./mine.sh 04 10 02

BIN
eth/test/chains/00.chain Executable file

Binary file not shown.

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