aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--block_pool.go351
-rw-r--r--cmd/ethereum/cmd.go2
-rw-r--r--cmd/ethereum/flags.go8
-rw-r--r--cmd/ethereum/main.go23
-rw-r--r--cmd/ethereum/repl/repl.go8
-rw-r--r--cmd/ethtest/.bowerrc5
-rw-r--r--cmd/ethtest/.editorconfig12
-rw-r--r--cmd/ethtest/.gitignore (renamed from wire/.gitignore)8
-rw-r--r--cmd/ethtest/.jshintrc50
-rw-r--r--cmd/ethtest/.npmignore9
-rw-r--r--cmd/ethtest/.travis.yml11
-rw-r--r--cmd/ethtest/LICENSE14
-rw-r--r--cmd/ethtest/README.md79
-rw-r--r--cmd/ethtest/bower.json51
-rw-r--r--cmd/ethtest/dist/ethereum.js20
-rw-r--r--cmd/ethtest/dist/ethereum.js.map29
-rw-r--r--cmd/ethtest/dist/ethereum.min.js1
-rw-r--r--cmd/ethtest/example/contract.html75
-rw-r--r--cmd/ethtest/example/index.html41
-rw-r--r--cmd/ethtest/example/node-app.js16
-rw-r--r--cmd/ethtest/gulpfile.js123
-rw-r--r--cmd/ethtest/index.js8
-rw-r--r--cmd/ethtest/index_qt.js5
-rw-r--r--cmd/ethtest/lib/abi.js218
-rw-r--r--cmd/ethtest/lib/autoprovider.js103
-rw-r--r--cmd/ethtest/lib/contract.js65
-rw-r--r--cmd/ethtest/lib/httprpc.js95
-rw-r--r--cmd/ethtest/lib/main.js494
-rw-r--r--cmd/ethtest/lib/qt.js45
-rw-r--r--cmd/ethtest/lib/websocket.js78
-rw-r--r--cmd/ethtest/package.json67
-rw-r--r--cmd/mist/assets/qml/browser.qml (renamed from cmd/mist/assets/qml/webapp.qml)37
-rw-r--r--cmd/mist/assets/qml/main.qml3
-rw-r--r--cmd/mist/assets/qml/views/whisper.qml76
-rw-r--r--cmd/mist/flags.go4
-rw-r--r--cmd/mist/gui.go122
-rw-r--r--cmd/mist/html_container.go2
-rw-r--r--cmd/mist/main.go12
-rw-r--r--cmd/mist/qml_container.go2
-rw-r--r--cmd/mist/ui_lib.go159
-rw-r--r--cmd/utils/cmd.go64
-rw-r--r--cmd/utils/vm_env.go12
-rw-r--r--cmd/utils/websockets.go2
-rw-r--r--core/block_manager.go101
-rw-r--r--core/chain_manager.go146
-rw-r--r--core/events.go3
-rw-r--r--core/filter.go13
-rw-r--r--core/genesis.go65
-rw-r--r--core/transaction_pool.go38
-rw-r--r--core/types/block.go482
-rw-r--r--core/types/block_test.go23
-rw-r--r--core/types/common.go5
-rw-r--r--core/types/derive_sha.go8
-rw-r--r--core/types/transaction.go125
-rw-r--r--core/vm_env.go12
-rw-r--r--eth/backend.go249
-rw-r--r--eth/block_pool.go1015
-rw-r--r--eth/block_pool_test.go198
-rw-r--r--eth/error.go71
-rw-r--r--eth/peer_util.go23
-rw-r--r--eth/protocol.go319
-rw-r--r--eth/protocol_test.go232
-rw-r--r--ethdb/database.go4
-rw-r--r--ethereum.go659
-rw-r--r--event/filter/generic_filter.go20
-rw-r--r--event/filter/old_filter.go94
-rw-r--r--events.go11
-rw-r--r--javascript/javascript_runtime.go4
-rw-r--r--javascript/types.go10
-rw-r--r--miner/miner.go17
-rw-r--r--nat.go12
-rw-r--r--natpmp.go55
-rw-r--r--natupnp.go338
-rw-r--r--p2p/server.go10
-rw-r--r--peer.go887
-rw-r--r--pow/block.go2
-rw-r--r--pow/ezp/pow.go14
-rw-r--r--ptrie/fullnode.go7
-rw-r--r--ptrie/trie.go25
-rw-r--r--ptrie/trie_test.go2
-rw-r--r--state/dump.go26
-rw-r--r--state/state_object.go42
-rw-r--r--state/statedb.go (renamed from state/state.go)37
-rw-r--r--ui/qt/qwhisper/message.go23
-rw-r--r--ui/qt/qwhisper/watch.go13
-rw-r--r--ui/qt/qwhisper/whisper.go51
-rw-r--r--ui/qt/qwhisper/whisper_test.go15
-rw-r--r--whisper/doc.go16
-rw-r--r--whisper/envelope.go12
-rw-r--r--whisper/main.go14
-rw-r--r--whisper/util.go11
-rw-r--r--whisper/whisper.go29
-rw-r--r--wire/README.md36
-rw-r--r--wire/client_identity.go56
-rw-r--r--wire/client_identity_test.go30
-rw-r--r--wire/messages2.go199
-rw-r--r--wire/messaging.go178
-rw-r--r--xeth/hexface.go17
-rw-r--r--xeth/js_types.go87
-rw-r--r--xeth/pipe.go2
-rw-r--r--xeth/vm_env.go12
-rw-r--r--xeth/world.go5
102 files changed, 5066 insertions, 3753 deletions
diff --git a/block_pool.go b/block_pool.go
deleted file mode 100644
index c618f6993..000000000
--- a/block_pool.go
+++ /dev/null
@@ -1,351 +0,0 @@
-package eth
-
-import (
- "bytes"
- "container/list"
- "fmt"
- "math"
- "math/big"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/wire"
-)
-
-var poollogger = logger.NewLogger("BPOOL")
-
-type block struct {
- from *Peer
- peer *Peer
- block *types.Block
- reqAt time.Time
- requested int
-}
-
-type BlockPool struct {
- mut sync.Mutex
-
- eth *Ethereum
-
- hashes [][]byte
- pool map[string]*block
-
- td *big.Int
- quit chan bool
-
- fetchingHashes bool
- downloadStartedAt time.Time
-
- ChainLength, BlocksProcessed int
-
- peer *Peer
-}
-
-func NewBlockPool(eth *Ethereum) *BlockPool {
- return &BlockPool{
- eth: eth,
- pool: make(map[string]*block),
- td: ethutil.Big0,
- quit: make(chan bool),
- }
-}
-
-func (self *BlockPool) Len() int {
- return len(self.hashes)
-}
-
-func (self *BlockPool) Reset() {
- self.pool = make(map[string]*block)
- self.hashes = nil
-}
-
-func (self *BlockPool) HasLatestHash() bool {
- self.mut.Lock()
- defer self.mut.Unlock()
-
- return self.pool[string(self.eth.ChainManager().CurrentBlock().Hash())] != nil
-}
-
-func (self *BlockPool) HasCommonHash(hash []byte) bool {
- return self.eth.ChainManager().GetBlock(hash) != nil
-}
-
-func (self *BlockPool) Blocks() (blocks types.Blocks) {
- for _, item := range self.pool {
- if item.block != nil {
- blocks = append(blocks, item.block)
- }
- }
-
- return
-}
-
-func (self *BlockPool) FetchHashes(peer *Peer) bool {
- highestTd := self.eth.HighestTDPeer()
-
- if (self.peer == nil && peer.td.Cmp(highestTd) >= 0) || (self.peer != nil && peer.td.Cmp(self.peer.td) > 0) || self.peer == peer {
- if self.peer != peer {
- poollogger.Infof("Found better suitable peer (%v vs %v)\n", self.td, peer.td)
-
- if self.peer != nil {
- self.peer.doneFetchingHashes = true
- }
- }
-
- self.peer = peer
- self.td = peer.td
-
- if !self.HasLatestHash() {
- self.fetchHashes()
- }
-
- return true
- }
-
- return false
-}
-
-func (self *BlockPool) fetchHashes() {
- peer := self.peer
-
- peer.doneFetchingHashes = false
-
- const amount = 256
- peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4])
- peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)}))
-}
-
-func (self *BlockPool) AddHash(hash []byte, peer *Peer) {
- self.mut.Lock()
- defer self.mut.Unlock()
-
- if self.pool[string(hash)] == nil {
- self.pool[string(hash)] = &block{peer, nil, nil, time.Now(), 0}
-
- self.hashes = append([][]byte{hash}, self.hashes...)
- }
-}
-
-func (self *BlockPool) Add(b *types.Block, peer *Peer) {
- self.addBlock(b, peer, false)
-}
-
-func (self *BlockPool) AddNew(b *types.Block, peer *Peer) {
- self.addBlock(b, peer, true)
-}
-
-func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) {
- self.mut.Lock()
- defer self.mut.Unlock()
-
- hash := string(b.Hash())
-
- if self.pool[hash] == nil && !self.eth.ChainManager().HasBlock(b.Hash()) {
- poollogger.Infof("Got unrequested block (%x...)\n", hash[0:4])
-
- self.hashes = append(self.hashes, b.Hash())
- self.pool[hash] = &block{peer, peer, b, time.Now(), 0}
-
- // The following is only performed on an unrequested new block
- if newBlock {
- fmt.Println("1.", !self.eth.ChainManager().HasBlock(b.PrevHash), ethutil.Bytes2Hex(b.Hash()[0:4]), ethutil.Bytes2Hex(b.PrevHash[0:4]))
- fmt.Println("2.", self.pool[string(b.PrevHash)] == nil)
- fmt.Println("3.", !self.fetchingHashes)
- if !self.eth.ChainManager().HasBlock(b.PrevHash) /*&& self.pool[string(b.PrevHash)] == nil*/ && !self.fetchingHashes {
- poollogger.Infof("Unknown chain, requesting (%x...)\n", b.PrevHash[0:4])
- peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{b.Hash(), uint32(256)}))
- }
- }
- } else if self.pool[hash] != nil {
- self.pool[hash].block = b
- }
-
- self.BlocksProcessed++
-}
-
-func (self *BlockPool) Remove(hash []byte) {
- self.mut.Lock()
- defer self.mut.Unlock()
-
- self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
- delete(self.pool, string(hash))
-}
-
-func (self *BlockPool) DistributeHashes() {
- self.mut.Lock()
- defer self.mut.Unlock()
-
- var (
- peerLen = self.eth.peers.Len()
- amount = 256 * peerLen
- dist = make(map[*Peer][][]byte)
- )
-
- num := int(math.Min(float64(amount), float64(len(self.pool))))
- for i, j := 0, 0; i < len(self.hashes) && j < num; i++ {
- hash := self.hashes[i]
- item := self.pool[string(hash)]
-
- if item != nil && item.block == nil {
- var peer *Peer
- lastFetchFailed := time.Since(item.reqAt) > 5*time.Second
-
- // Handle failed requests
- if lastFetchFailed && item.requested > 5 && item.peer != nil {
- if item.requested < 100 {
- // Select peer the hash was retrieved off
- peer = item.from
- } else {
- // Remove it
- self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
- delete(self.pool, string(hash))
- }
- } else if lastFetchFailed || item.peer == nil {
- // Find a suitable, available peer
- eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
- if peer == nil && len(dist[p]) < amount/peerLen && p.statusKnown {
- peer = p
- }
- })
- }
-
- if peer != nil {
- item.reqAt = time.Now()
- item.peer = peer
- item.requested++
-
- dist[peer] = append(dist[peer], hash)
- }
- }
- }
-
- for peer, hashes := range dist {
- peer.FetchBlocks(hashes)
- }
-
- if len(dist) > 0 {
- self.downloadStartedAt = time.Now()
- }
-}
-
-func (self *BlockPool) Start() {
- go self.downloadThread()
- go self.chainThread()
-}
-
-func (self *BlockPool) Stop() {
- close(self.quit)
-}
-
-func (self *BlockPool) downloadThread() {
- serviceTimer := time.NewTicker(100 * time.Millisecond)
-out:
- for {
- select {
- case <-self.quit:
- break out
- case <-serviceTimer.C:
- // Check if we're catching up. If not distribute the hashes to
- // the peers and download the blockchain
- self.fetchingHashes = false
- eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
- if p.statusKnown && p.FetchingHashes() {
- self.fetchingHashes = true
- }
- })
-
- if len(self.hashes) > 0 {
- self.DistributeHashes()
- }
-
- if self.ChainLength < len(self.hashes) {
- self.ChainLength = len(self.hashes)
- }
-
- if self.peer != nil &&
- !self.peer.doneFetchingHashes &&
- time.Since(self.peer.lastHashAt) > 10*time.Second &&
- time.Since(self.peer.lastHashRequestedAt) > 5*time.Second {
- self.fetchHashes()
- }
-
- /*
- if !self.fetchingHashes {
- blocks := self.Blocks()
- chain.BlockBy(chain.Number).Sort(blocks)
-
- if len(blocks) > 0 {
- if !self.eth.ChainManager().HasBlock(b.PrevHash) && self.pool[string(b.PrevHash)] == nil && !self.fetchingHashes {
- }
- }
- }
- */
- }
- }
-}
-
-func (self *BlockPool) chainThread() {
- procTimer := time.NewTicker(500 * time.Millisecond)
-out:
- for {
- select {
- case <-self.quit:
- break out
- case <-procTimer.C:
- blocks := self.Blocks()
- types.BlockBy(types.Number).Sort(blocks)
-
- // Find common block
- for i, block := range blocks {
- if self.eth.ChainManager().HasBlock(block.PrevHash) {
- blocks = blocks[i:]
- break
- }
- }
-
- if len(blocks) > 0 {
- if self.eth.ChainManager().HasBlock(blocks[0].PrevHash) {
- for i, block := range blocks[1:] {
- // NOTE: The Ith element in this loop refers to the previous block in
- // outer "blocks"
- if bytes.Compare(block.PrevHash, blocks[i].Hash()) != 0 {
- blocks = blocks[:i]
-
- break
- }
- }
- } else {
- blocks = nil
- }
- }
-
- if len(blocks) > 0 {
- chainman := self.eth.ChainManager()
-
- err := chainman.InsertChain(blocks)
- if err != nil {
- poollogger.Debugln(err)
-
- self.Reset()
-
- if self.peer != nil && self.peer.conn != nil {
- poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
- }
-
- // This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
- self.eth.BlacklistPeer(self.peer)
- self.peer.StopWithReason(DiscBadPeer)
- self.td = ethutil.Big0
- self.peer = nil
- }
-
- for _, block := range blocks {
- self.Remove(block.Hash())
- }
- }
- }
- }
-}
diff --git a/cmd/ethereum/cmd.go b/cmd/ethereum/cmd.go
index 8710d6136..d8b9ea487 100644
--- a/cmd/ethereum/cmd.go
+++ b/cmd/ethereum/cmd.go
@@ -21,9 +21,9 @@ import (
"io/ioutil"
"os"
- "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
"github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/javascript"
)
diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go
index 85aca47c3..d27b739c3 100644
--- a/cmd/ethereum/flags.go
+++ b/cmd/ethereum/flags.go
@@ -38,7 +38,8 @@ var (
StartRpc bool
StartWebSockets bool
RpcPort int
- UseUPnP bool
+ NatType string
+ PMPGateway string
OutboundPort string
ShowGenesis bool
AddPeer string
@@ -57,6 +58,7 @@ var (
DumpHash string
DumpNumber int
VmType int
+ ImportChain string
)
// flags specific to cli client
@@ -84,7 +86,8 @@ func Init() {
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
- flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
+ flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
+ flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
@@ -102,6 +105,7 @@ func Init() {
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.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
+ flag.StringVar(&ImportChain, "chain", "", "Imports fiven chain")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go
index 30107c145..3f9a2a838 100644
--- a/cmd/ethereum/main.go
+++ b/cmd/ethereum/main.go
@@ -21,6 +21,7 @@ import (
"fmt"
"os"
"runtime"
+ "time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types"
@@ -38,6 +39,10 @@ var clilogger = logger.NewLogger("CLI")
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
+ defer func() {
+ logger.Flush()
+ }()
+
utils.HandleInterrupt()
// precedence: code-internal flag default < config file < environment variables < command line
@@ -69,9 +74,9 @@ func main() {
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
- clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
+ clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
- ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
+ ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
if Dump {
var block *types.Block
@@ -93,9 +98,6 @@ func main() {
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
fmt.Printf("%s\n", block.State().Dump())
@@ -112,6 +114,16 @@ func main() {
utils.StartMining(ethereum)
}
+ if len(ImportChain) > 0 {
+ start := time.Now()
+ err := utils.ImportChain(ethereum, ImportChain)
+ if err != nil {
+ clilogger.Infoln(err)
+ }
+ clilogger.Infoln("export done in", time.Since(start))
+ return
+ }
+
// better reworked as cases
if StartJsConsole {
InitJsConsole(ethereum)
@@ -131,5 +143,4 @@ func main() {
// this blocks the thread
ethereum.WaitForShutdown()
- logger.Flush()
}
diff --git a/cmd/ethereum/repl/repl.go b/cmd/ethereum/repl/repl.go
index a5146fecd..822aaa19d 100644
--- a/cmd/ethereum/repl/repl.go
+++ b/cmd/ethereum/repl/repl.go
@@ -24,7 +24,7 @@ import (
"os"
"path"
- "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/logger"
@@ -86,12 +86,6 @@ func (self *JSRepl) Stop() {
}
func (self *JSRepl) parseInput(code string) {
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("[native] error", r)
- }
- }()
-
value, err := self.re.Run(code)
if err != nil {
fmt.Println(err)
diff --git a/cmd/ethtest/.bowerrc b/cmd/ethtest/.bowerrc
new file mode 100644
index 000000000..c3a8813e8
--- /dev/null
+++ b/cmd/ethtest/.bowerrc
@@ -0,0 +1,5 @@
+{
+ "directory": "example/js/",
+ "cwd": "./",
+ "analytics": false
+} \ No newline at end of file
diff --git a/cmd/ethtest/.editorconfig b/cmd/ethtest/.editorconfig
new file mode 100644
index 000000000..60a2751d3
--- /dev/null
+++ b/cmd/ethtest/.editorconfig
@@ -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 \ No newline at end of file
diff --git a/wire/.gitignore b/cmd/ethtest/.gitignore
index f725d58d1..399b6dc88 100644
--- a/wire/.gitignore
+++ b/cmd/ethtest/.gitignore
@@ -4,9 +4,15 @@
# 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
diff --git a/cmd/ethtest/.jshintrc b/cmd/ethtest/.jshintrc
new file mode 100644
index 000000000..c0ec5f89d
--- /dev/null
+++ b/cmd/ethtest/.jshintrc
@@ -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
+} \ No newline at end of file
diff --git a/cmd/ethtest/.npmignore b/cmd/ethtest/.npmignore
new file mode 100644
index 000000000..5bbffe4fd
--- /dev/null
+++ b/cmd/ethtest/.npmignore
@@ -0,0 +1,9 @@
+example/js
+node_modules
+test
+.gitignore
+.editorconfig
+.travis.yml
+.npmignore
+component.json
+testling.html \ No newline at end of file
diff --git a/cmd/ethtest/.travis.yml b/cmd/ethtest/.travis.yml
new file mode 100644
index 000000000..fafacbd5a
--- /dev/null
+++ b/cmd/ethtest/.travis.yml
@@ -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
diff --git a/cmd/ethtest/LICENSE b/cmd/ethtest/LICENSE
new file mode 100644
index 000000000..0f187b873
--- /dev/null
+++ b/cmd/ethtest/LICENSE
@@ -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/>. \ No newline at end of file
diff --git a/cmd/ethtest/README.md b/cmd/ethtest/README.md
new file mode 100644
index 000000000..865b62c6b
--- /dev/null
+++ b/cmd/ethtest/README.md
@@ -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 \ No newline at end of file
diff --git a/cmd/ethtest/bower.json b/cmd/ethtest/bower.json
new file mode 100644
index 000000000..cedae9023
--- /dev/null
+++ b/cmd/ethtest/bower.json
@@ -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"
+ ]
+} \ No newline at end of file
diff --git a/cmd/ethtest/dist/ethereum.js b/cmd/ethtest/dist/ethereum.js
new file mode 100644
index 000000000..b64c15b9e
--- /dev/null
+++ b/cmd/ethtest/dist/ethereum.js
@@ -0,0 +1,20 @@
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+var findIndex=function(array,callback){for(var end=!1,i=0;i<array.length&&!end;i++)end=callback(array[i]);return end?i-1:-1},findMethodIndex=function(json,methodName){return findIndex(json,function(method){return method.name===methodName})},padLeft=function(number,n){return new Array(2*n-number.toString().length+1).join("0")+number},setupInputTypes=function(){var prefixedType=function(prefix){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=parseInt(type.slice(expected.length))/8,padLeft(value,padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?value:formatter(value),padding)}},formatBool=function(value){return value?"1":"0"};return[prefixedType("uint"),prefixedType("int"),namedType("address",20),namedType("bool",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,val,bytes="",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes=bytes+index+"x0",method=json[index],i=0;i<method.inputs.length;i++){for(found=!1,j=0;j<inputTypes.length&&!found;j++)val=parseInt(params[i]).toString(16),found=inputTypes[j](method.inputs[i].type,val);found||console.error("unsupported json type: "+method.inputs[i].type),bytes+=found}return bytes}},setupOutputTypes=function(){var prefixedType=function(prefix){return function(type){var padding,expected=prefix;return 0!==type.indexOf(expected)?-1:(padding=parseInt(type.slice(expected.length))/8,2*padding)}},namedType=function(name,padding){return function(type){return name===type?2*padding:-1}},formatInt=function(value){return parseInt(value,16)},formatBool=function(value){return"1"===value?!0:!1};return[{padding:prefixedType("uint"),format:formatInt},{padding:prefixedType("int"),format:formatInt},{padding:namedType("address",20)},{padding:namedType("bool",1),format:formatBool}]},outputTypes=setupOutputTypes(),fromAbiOutput=function(json,methodName,output){var result,method,i,padding,j,res,formatter,index=findMethodIndex(json,methodName);if(-1!==index){for(output=output.slice(2),result=[],method=json[index],i=0;i<method.outputs.length;i++){for(padding=-1,j=0;j<outputTypes.length&&-1===padding;j++)padding=outputTypes[j].padding(method.outputs[i].type);-1!==padding&&(res=output.slice(0,padding),formatter=outputTypes[j-1].format,result.push(formatter?formatter(res):res),output=output.slice(padding))}return result}},inputParser=function(json){var parser={};return json.forEach(function(method){parser[method.name]=function(){var params=Array.prototype.slice.call(arguments);return toAbiInput(json,method.name,params)}}),parser},outputParser=function(json){var parser={};return json.forEach(function(method){parser[method.name]=function(output){return fromAbiOutput(json,method.name,output)}}),parser};module.exports={inputParser:inputParser,outputParser:outputParser};
+},{}],2:[function(require,module,exports){
+var AutoProvider=function(userOptions){var options,self,closeWithSuccess,ws;if(!web3.haveProvider()){if(this.sendQueue=[],this.onmessageQueue=[],navigator.qt)return void(this.provider=new web3.providers.QtProvider);userOptions=userOptions||{},options={httprpc:userOptions.httprpc||"http://localhost:8080",websockets:userOptions.websockets||"ws://localhost:40404/eth"},self=this,closeWithSuccess=function(success){ws.close(),success?self.provider=new web3.providers.WebSocketProvider(options.websockets):(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})},ws=new WebSocket(options.websockets),ws.onopen=function(){closeWithSuccess(!0)},ws.onerror=function(){closeWithSuccess(!1)}}};AutoProvider.prototype.send=function(payload){return this.provider?void this.provider.send(payload):void this.sendQueue.push(payload)},Object.defineProperty(AutoProvider.prototype,"onmessage",{set:function(handler){return this.provider?void(this.provider.onmessage=handler):void this.onmessageQueue.push(handler)}}),module.exports=AutoProvider;
+},{}],3:[function(require,module,exports){
+var abi,contract;abi=require("./abi"),contract=function(address,desc){var inputParser=abi.inputParser(desc),outputParser=abi.outputParser(desc),contract={};return desc.forEach(function(method){contract[method.name]=function(){var params=Array.prototype.slice.call(arguments),parsed=inputParser[method.name].apply(null,params),onSuccess=function(result){return outputParser[method.name](result)};return{call:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.call(extra).then(onSuccess)},transact:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.transact(extra).then(onSuccess)}}}}),contract},module.exports=contract;
+},{"./abi":1}],4:[function(require,module,exports){
+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}}var HttpRpcProvider=function(host){this.handlers=[],this.host=host};HttpRpcProvider.prototype.sendRequest=function(payload,cb){var data=formatJsonRpcObject(payload),request=new XMLHttpRequest;request.open("POST",this.host,!0),request.send(JSON.stringify(data)),request.onreadystatechange=function(){4===request.readyState&&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);!parsed.error&&(parsed.result instanceof Array?0!==parsed.result.length:parsed.result)&&self.handlers.forEach(function(handler){handler.call(self,{_event:payload.call,_id:id,data:parsed.result})})})},Object.defineProperty(HttpRpcProvider.prototype,"onmessage",{set:function(handler){this.handlers.push(handler)}}),module.exports=HttpRpcProvider;
+},{}],5:[function(require,module,exports){
+function flattenPromise(obj){return obj instanceof Promise?Promise.resolve(obj):obj instanceof Array?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)})}):obj instanceof Object?new Promise(function(resolve){var keys=Object.keys(obj),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)})}):Promise.resolve(obj)}function messageHandler(data){if(void 0!==data._event)return void web3.trigger(data._event,data._id,data.data);if(data._id){var cb=web3._callbacks[data._id];cb&&(cb.call(this,data.error,data.data),delete web3._callbacks[data._id])}}var ethWatch,shhWatch,ProviderManager,Filter,ethMethods=function(){var blockCall=function(args){return"string"==typeof args[0]?"eth_blockByHash":"eth_blockByNumber"},transactionCall=function(args){return"string"==typeof args[0]?"eth_transactionByHash":"eth_transactionByNumber"},uncleCall=function(args){return"string"==typeof args[0]?"eth_uncleByHash":"eth_uncleByNumber"},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},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"}]},dbMethods=function(){return[{name:"put",call:"db_put"},{name:"get",call:"db_get"},{name:"putString",call:"db_putString"},{name:"getString",call:"db_getString"}]},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"}]},ethWatchMethods=function(){var newFilter=function(args){return"string"==typeof args[0]?"eth_newFilterString":"eth_newFilter"};return[{name:"newFilter",call:newFilter},{name:"uninstallFilter",call:"eth_uninstallFilter"},{name:"getMessages",call:"eth_filterLogs"}]},shhWatchMethods=function(){return[{name:"newFilter",call:"shh_newFilter"},{name:"uninstallFilter",call:"shh_uninstallFilter"},{name:"getMessage",call:"shh_getMessages"}]},setupMethods=function(obj,methods){methods.forEach(function(method){obj[method.name]=function(){return flattenPromise(Array.prototype.slice.call(arguments)).then(function(args){var call="function"==typeof method.call?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){return err?void reject(err):void resolve(result)})})}).catch(function(err){console.error(err)})}})},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){return err?void reject(err):void resolve(result)})})},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){return err?void reject(err):void resolve(result)})})}).catch(function(err){console.error(err)})}),Object.defineProperty(obj,property.name,proto)})},web3={_callbacks:{},_events:{},providers:{},toHex:function(str){var i,n,hex="";for(i=0;i<str.length;i++)n=str.charCodeAt(i).toString(16),hex+=n.length<2?"0"+n:n;return hex},toAscii:function(hex){var code,str="",i=0,l=hex.length;for("0x"===hex.substring(0,2)&&(i=2);l>i&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},toDecimal:function(val){return parseInt(val,16)},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+="00";return"0x"+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){return void 0===web3._events[event]&&(web3._events[event]={}),web3._events[event][id]=cb,this},off:function(event,id){return void 0!==web3._events[event]&&delete web3._events[event][id],this},trigger:function(event,id,data){var cb,callbacks=web3._events[event];callbacks&&callbacks[id]&&(cb=callbacks[id])(data)}};setupMethods(web3.eth,ethMethods()),setupProperties(web3.eth,ethProperties()),setupMethods(web3.db,dbMethods()),setupMethods(web3.shh,shhMethods()),ethWatch={changed:"eth_changed"},setupMethods(ethWatch,ethWatchMethods()),shhWatch={changed:"shh_changed"},setupMethods(shhWatch,shhWatchMethods()),ProviderManager=function(){var self,poll;this.queued=[],this.polls=[],this.ready=!1,this.provider=void 0,this.id=1,self=this,(poll=function(){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,12e3)})()},ProviderManager.prototype.send=function(data,cb){data._id=this.id,cb&&(web3._callbacks[data._id]=cb),data.args=data.args||[],this.id++,void 0!==this.provider?this.provider.send(data):(console.warn("provider is not set"),this.queued.push(data))},ProviderManager.prototype.set=function(provider){void 0!==this.provider&&void 0!==this.provider.unload&&this.provider.unload(),this.provider=provider,this.ready=!0},ProviderManager.prototype.sendQueued=function(){for(var i=0;this.queued.length;i++)this.send(this.queued[i])},ProviderManager.prototype.installed=function(){return void 0!==this.provider},ProviderManager.prototype.startPolling=function(data,pollId){this.provider&&this.provider.poll&&this.polls.push({data:data,id:pollId})},ProviderManager.prototype.stopPolling=function(pollId){var i,poll;for(i=this.polls.length;i--;)poll=this.polls[i],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},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()},module.exports=web3;
+},{}],6:[function(require,module,exports){
+var QtProvider=function(){this.handlers=[];var self=this;navigator.qt.onmessage=function(message){self.handlers.forEach(function(handler){handler.call(self,JSON.parse(message.data))})}};QtProvider.prototype.send=function(payload){navigator.qt.postMessage(JSON.stringify(payload))},Object.defineProperty(QtProvider.prototype,"onmessage",{set:function(handler){this.handlers.push(handler)}}),module.exports=QtProvider;
+},{}],7:[function(require,module,exports){
+var WebSocketProvider=function(host){this.handlers=[],this.queued=[],this.ready=!1,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=!0;for(var i=0;i<self.queued.length;i++)self.send(self.queued[i])}};WebSocketProvider.prototype.send=function(payload){if(this.ready){var data=JSON.stringify(payload);this.ws.send(data)}else this.queued.push(payload)},WebSocketProvider.prototype.onMessage=function(handler){this.handlers.push(handler)},WebSocketProvider.prototype.unload=function(){this.ws.close()},Object.defineProperty(WebSocketProvider.prototype,"onmessage",{set:function(provider){this.onMessage(provider)}}),module.exports=WebSocketProvider;
+},{}],"web3":[function(require,module,exports){
+var web3=require("./lib/main");web3.providers.WebSocketProvider=require("./lib/websocket"),web3.providers.HttpRpcProvider=require("./lib/httprpc"),web3.providers.QtProvider=require("./lib/qt"),web3.providers.AutoProvider=require("./lib/autoprovider"),web3.contract=require("./lib/contract"),module.exports=web3;
+},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},[])
+
+
+//# sourceMappingURL=ethereum.js.map \ No newline at end of file
diff --git a/cmd/ethtest/dist/ethereum.js.map b/cmd/ethtest/dist/ethereum.js.map
new file mode 100644
index 000000000..b544cab53
--- /dev/null
+++ b/cmd/ethtest/dist/ethereum.js.map
@@ -0,0 +1,29 @@
+{
+ "version": 3,
+ "sources": [
+ "node_modules/browserify/node_modules/browser-pack/_prelude.js",
+ "lib/abi.js",
+ "lib/autoprovider.js",
+ "lib/contract.js",
+ "lib/httprpc.js",
+ "lib/main.js",
+ "lib/qt.js",
+ "lib/websocket.js",
+ "index.js"
+ ],
+ "names": [],
+ "mappings": "AAAA;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA;;ACAA",
+ "file": "generated.js",
+ "sourceRoot": "",
+ "sourcesContent": [
+ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})",
+ "var findIndex=function(array,callback){for(var end=!1,i=0;i<array.length&&!end;i++)end=callback(array[i]);return end?i-1:-1},findMethodIndex=function(json,methodName){return findIndex(json,function(method){return method.name===methodName})},padLeft=function(number,n){return new Array(2*n-number.toString().length+1).join(\"0\")+number},setupInputTypes=function(){var prefixedType=function(prefix){return function(type,value){var padding,expected=prefix;return 0!==type.indexOf(expected)?!1:(padding=parseInt(type.slice(expected.length))/8,padLeft(value,padding))}},namedType=function(name,padding,formatter){return function(type,value){return type!==name?!1:padLeft(formatter?value:formatter(value),padding)}},formatBool=function(value){return value?\"1\":\"0\"};return[prefixedType(\"uint\"),prefixedType(\"int\"),namedType(\"address\",20),namedType(\"bool\",1,formatBool)]},inputTypes=setupInputTypes(),toAbiInput=function(json,methodName,params){var method,i,found,j,val,bytes=\"\",index=findMethodIndex(json,methodName);if(-1!==index){for(bytes=bytes+index+\"x0\",method=json[index],i=0;i<method.inputs.length;i++){for(found=!1,j=0;j<inputTypes.length&&!found;j++)val=parseInt(params[i]).toString(16),found=inputTypes[j](method.inputs[i].type,val);found||console.error(\"unsupported json type: \"+method.inputs[i].type),bytes+=found}return bytes}},setupOutputTypes=function(){var prefixedType=function(prefix){return function(type){var padding,expected=prefix;return 0!==type.indexOf(expected)?-1:(padding=parseInt(type.slice(expected.length))/8,2*padding)}},namedType=function(name,padding){return function(type){return name===type?2*padding:-1}},formatInt=function(value){return parseInt(value,16)},formatBool=function(value){return\"1\"===value?!0:!1};return[{padding:prefixedType(\"uint\"),format:formatInt},{padding:prefixedType(\"int\"),format:formatInt},{padding:namedType(\"address\",20)},{padding:namedType(\"bool\",1),format:formatBool}]},outputTypes=setupOutputTypes(),fromAbiOutput=function(json,methodName,output){var result,method,i,padding,j,res,formatter,index=findMethodIndex(json,methodName);if(-1!==index){for(output=output.slice(2),result=[],method=json[index],i=0;i<method.outputs.length;i++){for(padding=-1,j=0;j<outputTypes.length&&-1===padding;j++)padding=outputTypes[j].padding(method.outputs[i].type);-1!==padding&&(res=output.slice(0,padding),formatter=outputTypes[j-1].format,result.push(formatter?formatter(res):res),output=output.slice(padding))}return result}},inputParser=function(json){var parser={};return json.forEach(function(method){parser[method.name]=function(){var params=Array.prototype.slice.call(arguments);return toAbiInput(json,method.name,params)}}),parser},outputParser=function(json){var parser={};return json.forEach(function(method){parser[method.name]=function(output){return fromAbiOutput(json,method.name,output)}}),parser};module.exports={inputParser:inputParser,outputParser:outputParser};",
+ "var AutoProvider=function(userOptions){var options,self,closeWithSuccess,ws;if(!web3.haveProvider()){if(this.sendQueue=[],this.onmessageQueue=[],navigator.qt)return void(this.provider=new web3.providers.QtProvider);userOptions=userOptions||{},options={httprpc:userOptions.httprpc||\"http://localhost:8080\",websockets:userOptions.websockets||\"ws://localhost:40404/eth\"},self=this,closeWithSuccess=function(success){ws.close(),success?self.provider=new web3.providers.WebSocketProvider(options.websockets):(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})},ws=new WebSocket(options.websockets),ws.onopen=function(){closeWithSuccess(!0)},ws.onerror=function(){closeWithSuccess(!1)}}};AutoProvider.prototype.send=function(payload){return this.provider?void this.provider.send(payload):void this.sendQueue.push(payload)},Object.defineProperty(AutoProvider.prototype,\"onmessage\",{set:function(handler){return this.provider?void(this.provider.onmessage=handler):void this.onmessageQueue.push(handler)}}),module.exports=AutoProvider;",
+ "var abi,contract;abi=require(\"./abi\"),contract=function(address,desc){var inputParser=abi.inputParser(desc),outputParser=abi.outputParser(desc),contract={};return desc.forEach(function(method){contract[method.name]=function(){var params=Array.prototype.slice.call(arguments),parsed=inputParser[method.name].apply(null,params),onSuccess=function(result){return outputParser[method.name](result)};return{call:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.call(extra).then(onSuccess)},transact:function(extra){return extra=extra||{},extra.to=address,extra.data=parsed,web3.eth.transact(extra).then(onSuccess)}}}}),contract},module.exports=contract;",
+ "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}}var HttpRpcProvider=function(host){this.handlers=[],this.host=host};HttpRpcProvider.prototype.sendRequest=function(payload,cb){var data=formatJsonRpcObject(payload),request=new XMLHttpRequest;request.open(\"POST\",this.host,!0),request.send(JSON.stringify(data)),request.onreadystatechange=function(){4===request.readyState&&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);!parsed.error&&(parsed.result instanceof Array?0!==parsed.result.length:parsed.result)&&self.handlers.forEach(function(handler){handler.call(self,{_event:payload.call,_id:id,data:parsed.result})})})},Object.defineProperty(HttpRpcProvider.prototype,\"onmessage\",{set:function(handler){this.handlers.push(handler)}}),module.exports=HttpRpcProvider;",
+ "function flattenPromise(obj){return obj instanceof Promise?Promise.resolve(obj):obj instanceof Array?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)})}):obj instanceof Object?new Promise(function(resolve){var keys=Object.keys(obj),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)})}):Promise.resolve(obj)}function messageHandler(data){if(void 0!==data._event)return void web3.trigger(data._event,data._id,data.data);if(data._id){var cb=web3._callbacks[data._id];cb&&(cb.call(this,data.error,data.data),delete web3._callbacks[data._id])}}var ethWatch,shhWatch,ProviderManager,Filter,ethMethods=function(){var blockCall=function(args){return\"string\"==typeof args[0]?\"eth_blockByHash\":\"eth_blockByNumber\"},transactionCall=function(args){return\"string\"==typeof args[0]?\"eth_transactionByHash\":\"eth_transactionByNumber\"},uncleCall=function(args){return\"string\"==typeof args[0]?\"eth_uncleByHash\":\"eth_uncleByNumber\"},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},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\"}]},dbMethods=function(){return[{name:\"put\",call:\"db_put\"},{name:\"get\",call:\"db_get\"},{name:\"putString\",call:\"db_putString\"},{name:\"getString\",call:\"db_getString\"}]},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\"}]},ethWatchMethods=function(){var newFilter=function(args){return\"string\"==typeof args[0]?\"eth_newFilterString\":\"eth_newFilter\"};return[{name:\"newFilter\",call:newFilter},{name:\"uninstallFilter\",call:\"eth_uninstallFilter\"},{name:\"getMessages\",call:\"eth_filterLogs\"}]},shhWatchMethods=function(){return[{name:\"newFilter\",call:\"shh_newFilter\"},{name:\"uninstallFilter\",call:\"shh_uninstallFilter\"},{name:\"getMessage\",call:\"shh_getMessages\"}]},setupMethods=function(obj,methods){methods.forEach(function(method){obj[method.name]=function(){return flattenPromise(Array.prototype.slice.call(arguments)).then(function(args){var call=\"function\"==typeof method.call?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){return err?void reject(err):void resolve(result)})})}).catch(function(err){console.error(err)})}})},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){return err?void reject(err):void resolve(result)})})},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){return err?void reject(err):void resolve(result)})})}).catch(function(err){console.error(err)})}),Object.defineProperty(obj,property.name,proto)})},web3={_callbacks:{},_events:{},providers:{},toHex:function(str){var i,n,hex=\"\";for(i=0;i<str.length;i++)n=str.charCodeAt(i).toString(16),hex+=n.length<2?\"0\"+n:n;return hex},toAscii:function(hex){var code,str=\"\",i=0,l=hex.length;for(\"0x\"===hex.substring(0,2)&&(i=2);l>i&&(code=hex.charCodeAt(i),0!==code);i+=2)str+=String.fromCharCode(parseInt(hex.substr(i,2),16));return str},toDecimal:function(val){return parseInt(val,16)},fromAscii:function(str,pad){pad=void 0===pad?32:pad;for(var hex=this.toHex(str);hex.length<2*pad;)hex+=\"00\";return\"0x\"+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){return void 0===web3._events[event]&&(web3._events[event]={}),web3._events[event][id]=cb,this},off:function(event,id){return void 0!==web3._events[event]&&delete web3._events[event][id],this},trigger:function(event,id,data){var cb,callbacks=web3._events[event];callbacks&&callbacks[id]&&(cb=callbacks[id])(data)}};setupMethods(web3.eth,ethMethods()),setupProperties(web3.eth,ethProperties()),setupMethods(web3.db,dbMethods()),setupMethods(web3.shh,shhMethods()),ethWatch={changed:\"eth_changed\"},setupMethods(ethWatch,ethWatchMethods()),shhWatch={changed:\"shh_changed\"},setupMethods(shhWatch,shhWatchMethods()),ProviderManager=function(){var self,poll;this.queued=[],this.polls=[],this.ready=!1,this.provider=void 0,this.id=1,self=this,(poll=function(){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,12e3)})()},ProviderManager.prototype.send=function(data,cb){data._id=this.id,cb&&(web3._callbacks[data._id]=cb),data.args=data.args||[],this.id++,void 0!==this.provider?this.provider.send(data):(console.warn(\"provider is not set\"),this.queued.push(data))},ProviderManager.prototype.set=function(provider){void 0!==this.provider&&void 0!==this.provider.unload&&this.provider.unload(),this.provider=provider,this.ready=!0},ProviderManager.prototype.sendQueued=function(){for(var i=0;this.queued.length;i++)this.send(this.queued[i])},ProviderManager.prototype.installed=function(){return void 0!==this.provider},ProviderManager.prototype.startPolling=function(data,pollId){this.provider&&this.provider.poll&&this.polls.push({data:data,id:pollId})},ProviderManager.prototype.stopPolling=function(pollId){var i,poll;for(i=this.polls.length;i--;)poll=this.polls[i],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},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()},module.exports=web3;",
+ "var QtProvider=function(){this.handlers=[];var self=this;navigator.qt.onmessage=function(message){self.handlers.forEach(function(handler){handler.call(self,JSON.parse(message.data))})}};QtProvider.prototype.send=function(payload){navigator.qt.postMessage(JSON.stringify(payload))},Object.defineProperty(QtProvider.prototype,\"onmessage\",{set:function(handler){this.handlers.push(handler)}}),module.exports=QtProvider;",
+ "var WebSocketProvider=function(host){this.handlers=[],this.queued=[],this.ready=!1,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=!0;for(var i=0;i<self.queued.length;i++)self.send(self.queued[i])}};WebSocketProvider.prototype.send=function(payload){if(this.ready){var data=JSON.stringify(payload);this.ws.send(data)}else this.queued.push(payload)},WebSocketProvider.prototype.onMessage=function(handler){this.handlers.push(handler)},WebSocketProvider.prototype.unload=function(){this.ws.close()},Object.defineProperty(WebSocketProvider.prototype,\"onmessage\",{set:function(provider){this.onMessage(provider)}}),module.exports=WebSocketProvider;",
+ "var web3=require(\"./lib/main\");web3.providers.WebSocketProvider=require(\"./lib/websocket\"),web3.providers.HttpRpcProvider=require(\"./lib/httprpc\"),web3.providers.QtProvider=require(\"./lib/qt\"),web3.providers.AutoProvider=require(\"./lib/autoprovider\"),web3.contract=require(\"./lib/contract\"),module.exports=web3;"
+ ]
+} \ No newline at end of file
diff --git a/cmd/ethtest/dist/ethereum.min.js b/cmd/ethtest/dist/ethereum.min.js
new file mode 100644
index 000000000..38ce01aa8
--- /dev/null
+++ b/cmd/ethtest/dist/ethereum.min.js
@@ -0,0 +1 @@
+require=function t(e,n,r){function o(s,a){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[s]={exports:{}};e[s][0].call(l.exports,function(t){var n=e[s][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)o(r[s]);return o}({1:[function(t,e){var n=function(t,e){for(var n=!1,r=0;r<t.length&&!n;r++)n=e(t[r]);return n?r-1:-1},r=function(t,e){return n(t,function(t){return t.name===e})},o=function(t,e){return new Array(2*e-t.toString().length+1).join("0")+t},i=function(){var t=function(t){return function(e,n){var r,i=t;return 0!==e.indexOf(i)?!1:(r=parseInt(e.slice(i.length))/8,o(n,r))}},e=function(t,e,n){return function(r,i){return r!==t?!1:o(n?i:n(i),e)}},n=function(t){return t?"1":"0"};return[t("uint"),t("int"),e("address",20),e("bool",1,n)]},s=i(),a=function(t,e,n){var o,i,a,u,c,l="",h=r(t,e);if(-1!==h){for(l=l+h+"x0",o=t[h],i=0;i<o.inputs.length;i++){for(a=!1,u=0;u<s.length&&!a;u++)c=parseInt(n[i]).toString(16),a=s[u](o.inputs[i].type,c);a||console.error("unsupported json type: "+o.inputs[i].type),l+=a}return l}},u=function(){var t=function(t){return function(e){var n,r=t;return 0!==e.indexOf(r)?-1:(n=parseInt(e.slice(r.length))/8,2*n)}},e=function(t,e){return function(n){return t===n?2*e:-1}},n=function(t){return parseInt(t,16)},r=function(t){return"1"===t?!0:!1};return[{padding:t("uint"),format:n},{padding:t("int"),format:n},{padding:e("address",20)},{padding:e("bool",1),format:r}]},c=u(),l=function(t,e,n){var o,i,s,a,u,l,h,p=r(t,e);if(-1!==p){for(n=n.slice(2),o=[],i=t[p],s=0;s<i.outputs.length;s++){for(a=-1,u=0;u<c.length&&-1===a;u++)a=c[u].padding(i.outputs[s].type);-1!==a&&(l=n.slice(0,a),h=c[u-1].format,o.push(h?h(l):l),n=n.slice(a))}return o}},h=function(t){var e={};return t.forEach(function(n){e[n.name]=function(){var e=Array.prototype.slice.call(arguments);return a(t,n.name,e)}}),e},p=function(t){var e={};return t.forEach(function(n){e[n.name]=function(e){return l(t,n.name,e)}}),e};e.exports={inputParser:h,outputParser:p}},{}],2:[function(t,e){var n=function(t){var e,n,r,o;if(!web3.haveProvider()){if(this.sendQueue=[],this.onmessageQueue=[],navigator.qt)return void(this.provider=new web3.providers.QtProvider);t=t||{},e={httprpc:t.httprpc||"http://localhost:8080",websockets:t.websockets||"ws://localhost:40404/eth"},n=this,r=function(t){o.close(),t?n.provider=new web3.providers.WebSocketProvider(e.websockets):(n.provider=new web3.providers.HttpRpcProvider(e.httprpc),n.poll=n.provider.poll.bind(n.provider)),n.sendQueue.forEach(function(t){n.provider(t)}),n.onmessageQueue.forEach(function(t){n.provider.onmessage=t})},o=new WebSocket(e.websockets),o.onopen=function(){r(!0)},o.onerror=function(){r(!1)}}};n.prototype.send=function(t){return this.provider?void this.provider.send(t):void this.sendQueue.push(t)},Object.defineProperty(n.prototype,"onmessage",{set:function(t){return this.provider?void(this.provider.onmessage=t):void this.onmessageQueue.push(t)}}),e.exports=n},{}],3:[function(t,e){var n,r;n=t("./abi"),r=function(t,e){var r=n.inputParser(e),o=n.outputParser(e),i={};return e.forEach(function(e){i[e.name]=function(){var n=Array.prototype.slice.call(arguments),i=r[e.name].apply(null,n),s=function(t){return o[e.name](t)};return{call:function(e){return e=e||{},e.to=t,e.data=i,web3.eth.call(e).then(s)},transact:function(e){return e=e||{},e.to=t,e.data=i,web3.eth.transact(e).then(s)}}}}),i},e.exports=r},{"./abi":1}],4:[function(t,e){function n(t){return{jsonrpc:"2.0",method:t.call,params:t.args,id:t._id}}function r(t){var e=JSON.parse(t);return{_id:e.id,data:e.result,error:e.error}}var o=function(t){this.handlers=[],this.host=t};o.prototype.sendRequest=function(t,e){var r=n(t),o=new XMLHttpRequest;o.open("POST",this.host,!0),o.send(JSON.stringify(r)),o.onreadystatechange=function(){4===o.readyState&&e&&e(o)}},o.prototype.send=function(t){var e=this;this.sendRequest(t,function(t){e.handlers.forEach(function(n){n.call(e,r(t.responseText))})})},o.prototype.poll=function(t,e){var n=this;this.sendRequest(t,function(r){var o=JSON.parse(r.responseText);!o.error&&(o.result instanceof Array?0!==o.result.length:o.result)&&n.handlers.forEach(function(r){r.call(n,{_event:t.call,_id:e,data:o.result})})})},Object.defineProperty(o.prototype,"onmessage",{set:function(t){this.handlers.push(t)}}),e.exports=o},{}],5:[function(t,e){function n(t){return t instanceof Promise?Promise.resolve(t):t instanceof Array?new Promise(function(e){var r=t.map(function(t){return n(t)});return Promise.all(r).then(function(n){for(var r=0;r<t.length;r++)t[r]=n[r];e(t)})}):t instanceof Object?new Promise(function(e){var r=Object.keys(t),o=r.map(function(e){return n(t[e])});return Promise.all(o).then(function(n){for(var o=0;o<r.length;o++)t[r[o]]=n[o];e(t)})}):Promise.resolve(t)}function r(t){if(void 0!==t._event)return void g.trigger(t._event,t._id,t.data);if(t._id){var e=g._callbacks[t._id];e&&(e.call(this,t.error,t.data),delete g._callbacks[t._id])}}var o,i,s,a,u=function(){var t=function(t){return"string"==typeof t[0]?"eth_blockByHash":"eth_blockByNumber"},e=function(t){return"string"==typeof t[0]?"eth_transactionByHash":"eth_transactionByNumber"},n=function(t){return"string"==typeof t[0]?"eth_uncleByHash":"eth_uncleByNumber"},r=[{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:t},{name:"transaction",call:e},{name:"uncle",call:n},{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 r},c=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"}]},l=function(){return[{name:"put",call:"db_put"},{name:"get",call:"db_get"},{name:"putString",call:"db_putString"},{name:"getString",call:"db_getString"}]},h=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"}]},p=function(){var t=function(t){return"string"==typeof t[0]?"eth_newFilterString":"eth_newFilter"};return[{name:"newFilter",call:t},{name:"uninstallFilter",call:"eth_uninstallFilter"},{name:"getMessages",call:"eth_filterLogs"}]},d=function(){return[{name:"newFilter",call:"shh_newFilter"},{name:"uninstallFilter",call:"shh_uninstallFilter"},{name:"getMessage",call:"shh_getMessages"}]},f=function(t,e){e.forEach(function(e){t[e.name]=function(){return n(Array.prototype.slice.call(arguments)).then(function(t){var n="function"==typeof e.call?e.call(t):e.call;return{call:n,args:t}}).then(function(t){return new Promise(function(e,n){g.provider.send(t,function(t,r){return t?void n(t):void e(r)})})}).catch(function(t){console.error(t)})}})},v=function(t,e){e.forEach(function(e){var r={};r.get=function(){return new Promise(function(t,n){g.provider.send({call:e.getter},function(e,r){return e?void n(e):void t(r)})})},e.setter&&(r.set=function(t){return n([t]).then(function(t){return new Promise(function(n){g.provider.send({call:e.setter,args:t},function(t,e){return t?void reject(t):void n(e)})})}).catch(function(t){console.error(t)})}),Object.defineProperty(t,e.name,r)})},g={_callbacks:{},_events:{},providers:{},toHex:function(t){var e,n,r="";for(e=0;e<t.length;e++)n=t.charCodeAt(e).toString(16),r+=n.length<2?"0"+n:n;return r},toAscii:function(t){var e,n="",r=0,o=t.length;for("0x"===t.substring(0,2)&&(r=2);o>r&&(e=t.charCodeAt(r),0!==e);r+=2)n+=String.fromCharCode(parseInt(t.substr(r,2),16));return n},toDecimal:function(t){return parseInt(t,16)},fromAscii:function(t,e){e=void 0===e?32:e;for(var n=this.toHex(t);n.length<2*e;)n+="00";return"0x"+n},eth:{prototype:Object(),watch:function(t){return new a(t,o)}},db:{prototype:Object()},shh:{prototype:Object(),watch:function(t){return new a(t,i)}},on:function(t,e,n){return void 0===g._events[t]&&(g._events[t]={}),g._events[t][e]=n,this},off:function(t,e){return void 0!==g._events[t]&&delete g._events[t][e],this},trigger:function(t,e,n){var r,o=g._events[t];o&&o[e]&&(r=o[e])(n)}};f(g.eth,u()),v(g.eth,c()),f(g.db,l()),f(g.shh,h()),o={changed:"eth_changed"},f(o,p()),i={changed:"shh_changed"},f(i,d()),s=function(){var t,e;this.queued=[],this.polls=[],this.ready=!1,this.provider=void 0,this.id=1,t=this,(e=function(){t.provider&&t.provider.poll&&t.polls.forEach(function(e){e.data._id=t.id,t.id++,t.provider.poll(e.data,e.id)}),setTimeout(e,12e3)})()},s.prototype.send=function(t,e){t._id=this.id,e&&(g._callbacks[t._id]=e),t.args=t.args||[],this.id++,void 0!==this.provider?this.provider.send(t):(console.warn("provider is not set"),this.queued.push(t))},s.prototype.set=function(t){void 0!==this.provider&&void 0!==this.provider.unload&&this.provider.unload(),this.provider=t,this.ready=!0},s.prototype.sendQueued=function(){for(var t=0;this.queued.length;t++)this.send(this.queued[t])},s.prototype.installed=function(){return void 0!==this.provider},s.prototype.startPolling=function(t,e){this.provider&&this.provider.poll&&this.polls.push({data:t,id:e})},s.prototype.stopPolling=function(t){var e,n;for(e=this.polls.length;e--;)n=this.polls[e],n.id===t&&this.polls.splice(e,1)},g.provider=new s,g.setProvider=function(t){t.onmessage=r,g.provider.set(t),g.provider.sendQueued()},g.haveProvider=function(){return!!g.provider.provider},a=function(t,e){this.impl=e,this.callbacks=[];var n=this;this.promise=e.newFilter(t),this.promise.then(function(t){n.id=t,g.on(e.changed,t,n.trigger.bind(n)),g.provider.startPolling({call:e.changed,args:[t]},t)})},a.prototype.arrived=function(t){this.changed(t)},a.prototype.changed=function(t){var e=this;this.promise.then(function(){e.callbacks.push(t)})},a.prototype.trigger=function(t){for(var e=0;e<this.callbacks.length;e++)this.callbacks[e].call(this,t)},a.prototype.uninstall=function(){var t=this;this.promise.then(function(e){t.impl.uninstallFilter(e),g.provider.stopPolling(e),g.off(impl.changed,e)})},a.prototype.messages=function(){var t=this;return this.promise.then(function(e){return t.impl.getMessages(e)})},a.prototype.logs=function(){return this.messages()},e.exports=g},{}],6:[function(t,e){var n=function(){this.handlers=[];var t=this;navigator.qt.onmessage=function(e){t.handlers.forEach(function(n){n.call(t,JSON.parse(e.data))})}};n.prototype.send=function(t){navigator.qt.postMessage(JSON.stringify(t))},Object.defineProperty(n.prototype,"onmessage",{set:function(t){this.handlers.push(t)}}),e.exports=n},{}],7:[function(t,e){var n=function(t){this.handlers=[],this.queued=[],this.ready=!1,this.ws=new WebSocket(t);var e=this;this.ws.onmessage=function(t){for(var n=0;n<e.handlers.length;n++)e.handlers[n].call(e,JSON.parse(t.data),t)},this.ws.onopen=function(){e.ready=!0;for(var t=0;t<e.queued.length;t++)e.send(e.queued[t])}};n.prototype.send=function(t){if(this.ready){var e=JSON.stringify(t);this.ws.send(e)}else this.queued.push(t)},n.prototype.onMessage=function(t){this.handlers.push(t)},n.prototype.unload=function(){this.ws.close()},Object.defineProperty(n.prototype,"onmessage",{set:function(t){this.onMessage(t)}}),e.exports=n},{}],web3:[function(t,e){var n=t("./lib/main");n.providers.WebSocketProvider=t("./lib/websocket"),n.providers.HttpRpcProvider=t("./lib/httprpc"),n.providers.QtProvider=t("./lib/qt"),n.providers.AutoProvider=t("./lib/autoprovider"),n.contract=t("./lib/contract"),e.exports=n},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},[]); \ No newline at end of file
diff --git a/cmd/ethtest/example/contract.html b/cmd/ethtest/example/contract.html
new file mode 100644
index 000000000..44f0b03a1
--- /dev/null
+++ b/cmd/ethtest/example/contract.html
@@ -0,0 +1,75 @@
+<!doctype>
+<html>
+
+<head>
+<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
+<script type="text/javascript" src="../dist/ethereum.js"></script>
+<script type="text/javascript">
+
+ var web3 = require('web3');
+ web3.setProvider(new web3.providers.AutoProvider());
+
+ // solidity source code
+ var source = "" +
+ "contract test {\n" +
+ " function multiply(uint a) returns(uint d) {\n" +
+ " return a * 7;\n" +
+ " }\n" +
+ "}\n";
+
+ // contract description, this will be autogenerated somehow
+ var desc = [{
+ "name": "multiply",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ }];
+
+ var contract;
+
+ function createExampleContract() {
+ // hide create button
+ document.getElementById('create').style.visibility = 'hidden';
+ document.getElementById('source').innerText = source;
+
+ // create contract
+ web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
+ contract = web3.contract(address, desc);
+ document.getElementById('call').style.visibility = 'visible';
+ });
+ }
+
+ function callExampleContract() {
+ // this should be generated by ethereum
+ var param = document.getElementById('value').value;
+
+ // call the contract
+ contract.multiply(param).call().then(function(res) {
+ document.getElementById('result').innerText = res[0];
+ });
+ }
+
+</script>
+</head>
+<body>
+ <h1>contract</h1>
+ <div id="source"></div>
+ <div id='create'>
+ <button type="button" onClick="createExampleContract();">create example contract</button>
+ </div>
+ <div id='call' style='visibility: hidden;'>
+ <input type="number" id="value" onkeyup='callExampleContract()'></input>
+ </div>
+ <div id="result"></div>
+</body>
+</html>
+
diff --git a/cmd/ethtest/example/index.html b/cmd/ethtest/example/index.html
new file mode 100644
index 000000000..d0bf094ef
--- /dev/null
+++ b/cmd/ethtest/example/index.html
@@ -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>
+
diff --git a/cmd/ethtest/example/node-app.js b/cmd/ethtest/example/node-app.js
new file mode 100644
index 000000000..f63fa9115
--- /dev/null
+++ b/cmd/ethtest/example/node-app.js
@@ -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);
+}); \ No newline at end of file
diff --git a/cmd/ethtest/gulpfile.js b/cmd/ethtest/gulpfile.js
new file mode 100644
index 000000000..9e0717d8b
--- /dev/null
+++ b/cmd/ethtest/gulpfile.js
@@ -0,0 +1,123 @@
+#!/usr/bin/env node
+
+'use strict';
+
+var path = require('path');
+
+var del = require('del');
+var gulp = require('gulp');
+var browserify = require('browserify');
+var jshint = require('gulp-jshint');
+var uglify = require('gulp-uglify');
+var rename = require('gulp-rename');
+var envify = require('envify/custom');
+var unreach = require('unreachable-branch-transform');
+var source = require('vinyl-source-stream');
+var exorcist = require('exorcist');
+var bower = require('bower');
+
+var DEST = './dist/';
+
+var build = function(src, dst) {
+ return browserify({
+ debug: true,
+ insert_global_vars: false,
+ detectGlobals: false,
+ bundleExternal: false
+ })
+ .require('./' + src + '.js', {expose: 'web3'})
+ .add('./' + src + '.js')
+ .transform('envify', {
+ NODE_ENV: 'build'
+ })
+ .transform('unreachable-branch-transform')
+ .transform('uglifyify', {
+ mangle: false,
+ compress: {
+ dead_code: false,
+ conditionals: true,
+ unused: false,
+ hoist_funs: true,
+ hoist_vars: true,
+ negate_iife: false
+ },
+ beautify: true,
+ warnings: true
+ })
+ .bundle()
+ .pipe(exorcist(path.join( DEST, dst + '.js.map')))
+ .pipe(source(dst + '.js'))
+ .pipe(gulp.dest( DEST ));
+};
+
+var buildDev = function(src, dst) {
+ return browserify({
+ debug: true,
+ insert_global_vars: false,
+ detectGlobals: false,
+ bundleExternal: false
+ })
+ .require('./' + src + '.js', {expose: 'web3'})
+ .add('./' + src + '.js')
+ .transform('envify', {
+ NODE_ENV: 'build'
+ })
+ .transform('unreachable-branch-transform')
+ .bundle()
+ .pipe(exorcist(path.join( DEST, dst + '.js.map')))
+ .pipe(source(dst + '.js'))
+ .pipe(gulp.dest( DEST ));
+};
+
+var uglifyFile = function(file) {
+ return gulp.src( DEST + file + '.js')
+ .pipe(uglify())
+ .pipe(rename(file + '.min.js'))
+ .pipe(gulp.dest( DEST ));
+};
+
+gulp.task('bower', function(cb){
+ bower.commands.install().on('end', function (installed){
+ console.log(installed);
+ cb();
+ });
+});
+
+gulp.task('lint', function(){
+ return gulp.src(['./*.js', './lib/*.js'])
+ .pipe(jshint())
+ .pipe(jshint.reporter('default'));
+});
+
+gulp.task('clean', ['lint'], function(cb) {
+ del([ DEST ], cb);
+});
+
+gulp.task('build', ['clean'], function () {
+ return build('index', 'ethereum');
+});
+
+gulp.task('buildQt', ['clean'], function () {
+ return build('index_qt', 'ethereum');
+});
+
+gulp.task('buildDev', ['clean'], function () {
+ return buildDev('index', 'ethereum');
+});
+
+gulp.task('uglify', ['build'], function(){
+ return uglifyFile('ethereum');
+});
+
+gulp.task('uglifyQt', ['buildQt'], function () {
+ return uglifyFile('ethereum');
+});
+
+gulp.task('watch', function() {
+ gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
+});
+
+gulp.task('default', ['bower', 'lint', 'build', 'uglify']);
+gulp.task('qt', ['bower', 'lint', 'buildQt', 'uglifyQt']);
+gulp.task('dev', ['bower', 'lint', 'buildDev']);
+
diff --git a/cmd/ethtest/index.js b/cmd/ethtest/index.js
new file mode 100644
index 000000000..c2de7e735
--- /dev/null
+++ b/cmd/ethtest/index.js
@@ -0,0 +1,8 @@
+var web3 = require('./lib/main');
+web3.providers.WebSocketProvider = require('./lib/websocket');
+web3.providers.HttpRpcProvider = require('./lib/httprpc');
+web3.providers.QtProvider = require('./lib/qt');
+web3.providers.AutoProvider = require('./lib/autoprovider');
+web3.contract = require('./lib/contract');
+
+module.exports = web3;
diff --git a/cmd/ethtest/index_qt.js b/cmd/ethtest/index_qt.js
new file mode 100644
index 000000000..d5e47597e
--- /dev/null
+++ b/cmd/ethtest/index_qt.js
@@ -0,0 +1,5 @@
+var web3 = require('./lib/main');
+web3.providers.QtProvider = require('./lib/qt');
+web3.contract = require('./lib/contract');
+
+module.exports = web3;
diff --git a/cmd/ethtest/lib/abi.js b/cmd/ethtest/lib/abi.js
new file mode 100644
index 000000000..2cff503d3
--- /dev/null
+++ b/cmd/ethtest/lib/abi.js
@@ -0,0 +1,218 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file abi.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+// TODO: make these be actually accurate instead of falling back onto JS's doubles.
+var hexToDec = function (hex) {
+ return parseInt(hex, 16).toString();
+};
+
+var decToHex = function (dec) {
+ return parseInt(dec).toString(16);
+};
+
+var findIndex = function (array, callback) {
+ var end = false;
+ var i = 0;
+ for (; i < array.length && !end; i++) {
+ end = callback(array[i]);
+ }
+ return end ? i - 1 : -1;
+};
+
+var findMethodIndex = function (json, methodName) {
+ return findIndex(json, function (method) {
+ return method.name === methodName;
+ });
+};
+
+var padLeft = function (string, chars) {
+ return Array(chars - string.length + 1).join("0") + string;
+};
+
+var setupInputTypes = function () {
+ var prefixedType = function (prefix) {
+ return function (type, value) {
+ var expected = prefix;
+ if (type.indexOf(expected) !== 0) {
+ return false;
+ }
+
+ var padding = parseInt(type.slice(expected.length)) / 8;
+ if (typeof value === "number")
+ value = value.toString(16);
+ else if (value.indexOf('0x') === 0)
+ value = value.substr(2);
+ else
+ value = (+value).toString(16);
+ return padLeft(value, padding * 2);
+ };
+ };
+
+ var namedType = function (name, padding, formatter) {
+ return function (type, value) {
+ if (type !== name) {
+ return false;
+ }
+
+ return padLeft(formatter ? formatter(value) : value, padding * 2);
+ };
+ };
+
+ var formatBool = function (value) {
+ return value ? '0x1' : '0x0';
+ };
+
+ return [
+ prefixedType('uint'),
+ prefixedType('int'),
+ prefixedType('hash'),
+ namedType('address', 20),
+ namedType('bool', 1, formatBool),
+ ];
+};
+
+var inputTypes = setupInputTypes();
+
+var toAbiInput = function (json, methodName, params) {
+ var bytes = "";
+ var index = findMethodIndex(json, methodName);
+
+ if (index === -1) {
+ return;
+ }
+
+ bytes = "0x" + padLeft(index.toString(16), 2);
+ var method = json[index];
+
+ for (var i = 0; i < method.inputs.length; i++) {
+ var found = false;
+ for (var j = 0; j < inputTypes.length && !found; j++) {
+ found = inputTypes[j](method.inputs[i].type, params[i]);
+ }
+ if (!found) {
+ console.error('unsupported json type: ' + method.inputs[i].type);
+ }
+ bytes += found;
+ }
+ return bytes;
+};
+
+var setupOutputTypes = function () {
+ var prefixedType = function (prefix) {
+ return function (type) {
+ var expected = prefix;
+ if (type.indexOf(expected) !== 0) {
+ return -1;
+ }
+
+ var padding = parseInt(type.slice(expected.length)) / 8;
+ return padding * 2;
+ };
+ };
+
+ var namedType = function (name, padding) {
+ return function (type) {
+ return name === type ? padding * 2 : -1;
+ };
+ };
+
+ var formatInt = function (value) {
+ return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
+ };
+
+ var formatHash = function (value) {
+ return "0x" + value;
+ };
+
+ var formatBool = function (value) {
+ return value === '1' ? true : false;
+ };
+
+ return [
+ { padding: prefixedType('uint'), format: formatInt },
+ { padding: prefixedType('int'), format: formatInt },
+ { padding: prefixedType('hash'), format: formatHash },
+ { padding: namedType('address', 20) },
+ { padding: namedType('bool', 1), format: formatBool }
+ ];
+};
+
+var outputTypes = setupOutputTypes();
+
+var fromAbiOutput = function (json, methodName, output) {
+ var index = findMethodIndex(json, methodName);
+
+ if (index === -1) {
+ return;
+ }
+
+ output = output.slice(2);
+
+ var result = [];
+ var method = json[index];
+ for (var i = 0; i < method.outputs.length; i++) {
+ var padding = -1;
+ for (var j = 0; j < outputTypes.length && padding === -1; j++) {
+ padding = outputTypes[j].padding(method.outputs[i].type);
+ }
+
+ if (padding === -1) {
+ // not found output parsing
+ continue;
+ }
+ var res = output.slice(0, padding);
+ var formatter = outputTypes[j - 1].format;
+ result.push(formatter ? formatter(res) : ("0x" + res));
+ output = output.slice(padding);
+ }
+
+ return result;
+};
+
+var inputParser = function (json) {
+ var parser = {};
+ json.forEach(function (method) {
+ parser[method.name] = function () {
+ var params = Array.prototype.slice.call(arguments);
+ return toAbiInput(json, method.name, params);
+ };
+ });
+
+ return parser;
+};
+
+var outputParser = function (json) {
+ var parser = {};
+ json.forEach(function (method) {
+ parser[method.name] = function (output) {
+ return fromAbiOutput(json, method.name, output);
+ };
+ });
+
+ return parser;
+};
+
+module.exports = {
+ inputParser: inputParser,
+ outputParser: outputParser
+};
diff --git a/cmd/ethtest/lib/autoprovider.js b/cmd/ethtest/lib/autoprovider.js
new file mode 100644
index 000000000..bfbc3ab6e
--- /dev/null
+++ b/cmd/ethtest/lib/autoprovider.js
@@ -0,0 +1,103 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file autoprovider.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * Marian Oancea <marian@ethdev.com>
+ * @date 2014
+ */
+
+/*
+ * @brief if qt object is available, uses QtProvider,
+ * if not tries to connect over websockets
+ * if it fails, it uses HttpRpcProvider
+ */
+
+// TODO: work out which of the following two lines it is supposed to be...
+//if (process.env.NODE_ENV !== 'build') {
+if ("build" !== 'build') {/*
+ var WebSocket = require('ws'); // jshint ignore:line
+ var web3 = require('./main.js'); // jshint ignore:line
+*/}
+
+var AutoProvider = function (userOptions) {
+ if (web3.haveProvider()) {
+ return;
+ }
+
+ // before we determine what provider we are, we have to cache request
+ this.sendQueue = [];
+ this.onmessageQueue = [];
+
+ if (navigator.qt) {
+ this.provider = new web3.providers.QtProvider();
+ return;
+ }
+
+ userOptions = userOptions || {};
+ var options = {
+ httprpc: userOptions.httprpc || 'http://localhost:8080',
+ websockets: userOptions.websockets || 'ws://localhost:40404/eth'
+ };
+
+ var self = this;
+ var closeWithSuccess = function (success) {
+ ws.close();
+ if (success) {
+ self.provider = new web3.providers.WebSocketProvider(options.websockets);
+ } else {
+ self.provider = new web3.providers.HttpRpcProvider(options.httprpc);
+ self.poll = self.provider.poll.bind(self.provider);
+ }
+ self.sendQueue.forEach(function (payload) {
+ self.provider(payload);
+ });
+ self.onmessageQueue.forEach(function (handler) {
+ self.provider.onmessage = handler;
+ });
+ };
+
+ var ws = new WebSocket(options.websockets);
+
+ ws.onopen = function() {
+ closeWithSuccess(true);
+ };
+
+ ws.onerror = function() {
+ closeWithSuccess(false);
+ };
+};
+
+AutoProvider.prototype.send = function (payload) {
+ if (this.provider) {
+ this.provider.send(payload);
+ return;
+ }
+ this.sendQueue.push(payload);
+};
+
+Object.defineProperty(AutoProvider.prototype, 'onmessage', {
+ set: function (handler) {
+ if (this.provider) {
+ this.provider.onmessage = handler;
+ return;
+ }
+ this.onmessageQueue.push(handler);
+ }
+});
+
+module.exports = AutoProvider;
diff --git a/cmd/ethtest/lib/contract.js b/cmd/ethtest/lib/contract.js
new file mode 100644
index 000000000..17b077484
--- /dev/null
+++ b/cmd/ethtest/lib/contract.js
@@ -0,0 +1,65 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file contract.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2014
+ */
+
+// TODO: work out which of the following two lines it is supposed to be...
+//if (process.env.NODE_ENV !== 'build') {
+if ("build" !== 'build') {/*
+ var web3 = require('./web3'); // jshint ignore:line
+*/}
+var abi = require('./abi');
+
+var contract = function (address, desc) {
+ var inputParser = abi.inputParser(desc);
+ var outputParser = abi.outputParser(desc);
+
+ var contract = {};
+
+ desc.forEach(function (method) {
+ contract[method.name] = function () {
+ var params = Array.prototype.slice.call(arguments);
+ var parsed = inputParser[method.name].apply(null, params);
+
+ var onSuccess = function (result) {
+ return outputParser[method.name](result);
+ };
+
+ return {
+ call: function (extra) {
+ extra = extra || {};
+ extra.to = address;
+ extra.data = parsed;
+ return web3.eth.call(extra).then(onSuccess);
+ },
+ transact: function (extra) {
+ extra = extra || {};
+ extra.to = address;
+ extra.data = parsed;
+ return web3.eth.transact(extra).then(onSuccess);
+ }
+ };
+ };
+ });
+
+ return contract;
+};
+
+module.exports = contract;
diff --git a/cmd/ethtest/lib/httprpc.js b/cmd/ethtest/lib/httprpc.js
new file mode 100644
index 000000000..ee6b5c307
--- /dev/null
+++ b/cmd/ethtest/lib/httprpc.js
@@ -0,0 +1,95 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file httprpc.js
+ * @authors:
+ * Marek Kotewicz <marek@ethdev.com>
+ * Marian Oancea <marian@ethdev.com>
+ * @date 2014
+ */
+
+// TODO: work out which of the following two lines it is supposed to be...
+//if (process.env.NODE_ENV !== 'build') {
+if ("build" !== "build") {/*
+ var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
+*/}
+
+var HttpRpcProvider = function (host) {
+ this.handlers = [];
+ this.host = host;
+};
+
+function formatJsonRpcObject(object) {
+ return {
+ jsonrpc: '2.0',
+ method: object.call,
+ params: object.args,
+ id: object._id
+ };
+}
+
+function formatJsonRpcMessage(message) {
+ var object = JSON.parse(message);
+
+ return {
+ _id: object.id,
+ data: object.result,
+ error: object.error
+ };
+}
+
+HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
+ var data = formatJsonRpcObject(payload);
+
+ var request = new XMLHttpRequest();
+ request.open("POST", this.host, true);
+ request.send(JSON.stringify(data));
+ request.onreadystatechange = function () {
+ if (request.readyState === 4 && cb) {
+ cb(request);
+ }
+ };
+};
+
+HttpRpcProvider.prototype.send = function (payload) {
+ var self = this;
+ this.sendRequest(payload, function (request) {
+ self.handlers.forEach(function (handler) {
+ handler.call(self, formatJsonRpcMessage(request.responseText));
+ });
+ });
+};
+
+HttpRpcProvider.prototype.poll = function (payload, id) {
+ var self = this;
+ this.sendRequest(payload, function (request) {
+ var parsed = JSON.parse(request.responseText);
+ if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {
+ return;
+ }
+ self.handlers.forEach(function (handler) {
+ handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
+ });
+ });
+};
+
+Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
+ set: function (handler) {
+ this.handlers.push(handler);
+ }
+});
+
+module.exports = HttpRpcProvider;
diff --git a/cmd/ethtest/lib/main.js b/cmd/ethtest/lib/main.js
new file mode 100644
index 000000000..59c60cfa8
--- /dev/null
+++ b/cmd/ethtest/lib/main.js
@@ -0,0 +1,494 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file main.js
+ * @authors:
+ * Jeffrey Wilcke <jeff@ethdev.com>
+ * Marek Kotewicz <marek@ethdev.com>
+ * Marian Oancea <marian@ethdev.com>
+ * Gav Wood <g@ethdev.com>
+ * @date 2014
+ */
+
+function flattenPromise (obj) {
+ if (obj instanceof Promise) {
+ return Promise.resolve(obj);
+ }
+
+ if (obj instanceof Array) {
+ return new Promise(function (resolve) {
+ var promises = obj.map(function (o) {
+ return flattenPromise(o);
+ });
+
+ return Promise.all(promises).then(function (res) {
+ for (var i = 0; i < obj.length; i++) {
+ obj[i] = res[i];
+ }
+ resolve(obj);
+ });
+ });
+ }
+
+ if (obj instanceof Object) {
+ return new Promise(function (resolve) {
+ var keys = Object.keys(obj);
+ var promises = keys.map(function (key) {
+ return flattenPromise(obj[key]);
+ });
+
+ return Promise.all(promises).then(function (res) {
+ for (var i = 0; i < keys.length; i++) {
+ obj[keys[i]] = res[i];
+ }
+ resolve(obj);
+ });
+ });
+ }
+
+ return Promise.resolve(obj);
+}
+
+var web3Methods = function () {
+ return [
+ { name: 'sha3', call: 'web3_sha3' }
+ ];
+};
+
+var ethMethods = function () {
+ var blockCall = function (args) {
+ return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
+ };
+
+ var transactionCall = function (args) {
+ return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
+ };
+
+ var uncleCall = function (args) {
+ return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
+ };
+
+ var methods = [
+ { name: 'balanceAt', call: 'eth_balanceAt' },
+ { name: 'stateAt', call: 'eth_stateAt' },
+ { name: 'storageAt', call: 'eth_storageAt' },
+ { name: 'countAt', call: 'eth_countAt'},
+ { name: 'codeAt', call: 'eth_codeAt' },
+ { name: 'transact', call: 'eth_transact' },
+ { name: 'call', call: 'eth_call' },
+ { name: 'block', call: blockCall },
+ { name: 'transaction', call: transactionCall },
+ { name: 'uncle', call: uncleCall },
+ { name: 'compilers', call: 'eth_compilers' },
+ { name: 'lll', call: 'eth_lll' },
+ { name: 'solidity', call: 'eth_solidity' },
+ { name: 'serpent', call: 'eth_serpent' },
+ { name: 'logs', call: 'eth_logs' }
+ ];
+ return methods;
+};
+
+var ethProperties = function () {
+ return [
+ { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
+ { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
+ { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
+ { name: 'gasPrice', getter: 'eth_gasPrice' },
+ { name: 'account', getter: 'eth_account' },
+ { name: 'accounts', getter: 'eth_accounts' },
+ { name: 'peerCount', getter: 'eth_peerCount' },
+ { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
+ { name: 'number', getter: 'eth_number'}
+ ];
+};
+
+var dbMethods = function () {
+ return [
+ { name: 'put', call: 'db_put' },
+ { name: 'get', call: 'db_get' },
+ { name: 'putString', call: 'db_putString' },
+ { name: 'getString', call: 'db_getString' }
+ ];
+};
+
+var shhMethods = function () {
+ return [
+ { name: 'post', call: 'shh_post' },
+ { name: 'newIdentity', call: 'shh_newIdentity' },
+ { name: 'haveIdentity', call: 'shh_haveIdentity' },
+ { name: 'newGroup', call: 'shh_newGroup' },
+ { name: 'addToGroup', call: 'shh_addToGroup' }
+ ];
+};
+
+var ethWatchMethods = function () {
+ var newFilter = function (args) {
+ return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
+ };
+
+ return [
+ { name: 'newFilter', call: newFilter },
+ { name: 'uninstallFilter', call: 'eth_uninstallFilter' },
+ { name: 'getMessages', call: 'eth_filterLogs' }
+ ];
+};
+
+var shhWatchMethods = function () {
+ return [
+ { name: 'newFilter', call: 'shh_newFilter' },
+ { name: 'uninstallFilter', call: 'shh_uninstallFilter' },
+ { name: 'getMessage', call: 'shh_getMessages' }
+ ];
+};
+
+var setupMethods = function (obj, methods) {
+ methods.forEach(function (method) {
+ obj[method.name] = function () {
+ return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
+ var call = typeof method.call === "function" ? method.call(args) : method.call;
+ return {call: call, args: args};
+ }).then(function (request) {
+ return new Promise(function (resolve, reject) {
+ web3.provider.send(request, function (err, result) {
+ if (!err) {
+ resolve(result);
+ return;
+ }
+ reject(err);
+ });
+ });
+ }).catch(function(err) {
+ console.error(err);
+ });
+ };
+ });
+};
+
+var setupProperties = function (obj, properties) {
+ properties.forEach(function (property) {
+ var proto = {};
+ proto.get = function () {
+ return new Promise(function(resolve, reject) {
+ web3.provider.send({call: property.getter}, function(err, result) {
+ if (!err) {
+ resolve(result);
+ return;
+ }
+ reject(err);
+ });
+ });
+ };
+ if (property.setter) {
+ proto.set = function (val) {
+ return flattenPromise([val]).then(function (args) {
+ return new Promise(function (resolve) {
+ web3.provider.send({call: property.setter, args: args}, function (err, result) {
+ if (!err) {
+ resolve(result);
+ return;
+ }
+ reject(err);
+ });
+ });
+ }).catch(function (err) {
+ console.error(err);
+ });
+ };
+ }
+ Object.defineProperty(obj, property.name, proto);
+ });
+};
+
+// TODO: import from a dependency, don't duplicate.
+var hexToDec = function (hex) {
+ return parseInt(hex, 16).toString();
+};
+
+var decToHex = function (dec) {
+ return parseInt(dec).toString(16);
+};
+
+
+var web3 = {
+ _callbacks: {},
+ _events: {},
+ providers: {},
+
+ toAscii: function(hex) {
+ // Find termination
+ var str = "";
+ var i = 0, l = hex.length;
+ if (hex.substring(0, 2) === '0x')
+ i = 2;
+ for(; i < l; i+=2) {
+ var code = hex.charCodeAt(i);
+ if(code === 0) {
+ break;
+ }
+
+ str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+
+ return str;
+ },
+
+ fromAscii: function(str, pad) {
+ pad = pad === undefined ? 32 : pad;
+ var hex = this.toHex(str);
+ while(hex.length < pad*2)
+ hex += "00";
+ return "0x" + hex;
+ },
+
+ toDecimal: function (val) {
+ return hexToDec(val.substring(2));
+ },
+
+ fromDecimal: function (val) {
+ return "0x" + decToHex(val);
+ },
+
+ toEth: function(str) {
+ var val = typeof str === "string" ? str.indexOf('0x') == 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
+ var unit = 0;
+ var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
+ while (val > 3000 && unit < units.length - 1)
+ {
+ val /= 1000;
+ unit++;
+ }
+ var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
+ while (true) {
+ var o = s;
+ s = s.replace(/(\d)(\d\d\d[\.\,])/, function($0, $1, $2) { return $1 + ',' + $2; });
+ if (o == s)
+ break;
+ }
+ return s + ' ' + units[unit];
+ },
+
+ eth: {
+ prototype: Object(), // jshint ignore:line
+ watch: function (params) {
+ return new Filter(params, ethWatch);
+ }
+ },
+
+ db: {
+ prototype: Object() // jshint ignore:line
+ },
+
+ shh: {
+ prototype: Object(), // jshint ignore:line
+ watch: function (params) {
+ return new Filter(params, shhWatch);
+ }
+ },
+
+ on: function(event, id, cb) {
+ if(web3._events[event] === undefined) {
+ web3._events[event] = {};
+ }
+
+ web3._events[event][id] = cb;
+ return this;
+ },
+
+ off: function(event, id) {
+ if(web3._events[event] !== undefined) {
+ delete web3._events[event][id];
+ }
+
+ return this;
+ },
+
+ trigger: function(event, id, data) {
+ var callbacks = web3._events[event];
+ if (!callbacks || !callbacks[id]) {
+ return;
+ }
+ var cb = callbacks[id];
+ cb(data);
+ }
+};
+
+setupMethods(web3, web3Methods());
+setupMethods(web3.eth, ethMethods());
+setupProperties(web3.eth, ethProperties());
+setupMethods(web3.db, dbMethods());
+setupMethods(web3.shh, shhMethods());
+
+var ethWatch = {
+ changed: 'eth_changed'
+};
+setupMethods(ethWatch, ethWatchMethods());
+var shhWatch = {
+ changed: 'shh_changed'
+};
+setupMethods(shhWatch, shhWatchMethods());
+
+var ProviderManager = function() {
+ this.queued = [];
+ this.polls = [];
+ this.ready = false;
+ this.provider = undefined;
+ this.id = 1;
+
+ var self = this;
+ var poll = function () {
+ if (self.provider && self.provider.poll) {
+ self.polls.forEach(function (data) {
+ data.data._id = self.id;
+ self.id++;
+ self.provider.poll(data.data, data.id);
+ });
+ }
+ setTimeout(poll, 12000);
+ };
+ poll();
+};
+
+ProviderManager.prototype.send = function(data, cb) {
+ data._id = this.id;
+ if (cb) {
+ web3._callbacks[data._id] = cb;
+ }
+
+ data.args = data.args || [];
+ this.id++;
+
+ if(this.provider !== undefined) {
+ this.provider.send(data);
+ } else {
+ console.warn("provider is not set");
+ this.queued.push(data);
+ }
+};
+
+ProviderManager.prototype.set = function(provider) {
+ if(this.provider !== undefined && this.provider.unload !== undefined) {
+ this.provider.unload();
+ }
+
+ this.provider = provider;
+ this.ready = true;
+};
+
+ProviderManager.prototype.sendQueued = function() {
+ for(var i = 0; this.queued.length; i++) {
+ // Resend
+ this.send(this.queued[i]);
+ }
+};
+
+ProviderManager.prototype.installed = function() {
+ return this.provider !== undefined;
+};
+
+ProviderManager.prototype.startPolling = function (data, pollId) {
+ if (!this.provider || !this.provider.poll) {
+ return;
+ }
+ this.polls.push({data: data, id: pollId});
+};
+
+ProviderManager.prototype.stopPolling = function (pollId) {
+ for (var i = this.polls.length; i--;) {
+ var poll = this.polls[i];
+ if (poll.id === pollId) {
+ this.polls.splice(i, 1);
+ }
+ }
+};
+
+web3.provider = new ProviderManager();
+
+web3.setProvider = function(provider) {
+ provider.onmessage = messageHandler;
+ web3.provider.set(provider);
+ web3.provider.sendQueued();
+};
+
+web3.haveProvider = function() {
+ return !!web3.provider.provider;
+};
+
+var Filter = function(options, impl) {
+ this.impl = impl;
+ this.callbacks = [];
+
+ var self = this;
+ this.promise = impl.newFilter(options);
+ this.promise.then(function (id) {
+ self.id = id;
+ web3.on(impl.changed, id, self.trigger.bind(self));
+ web3.provider.startPolling({call: impl.changed, args: [id]}, id);
+ });
+};
+
+Filter.prototype.arrived = function(callback) {
+ this.changed(callback);
+};
+
+Filter.prototype.changed = function(callback) {
+ var self = this;
+ this.promise.then(function(id) {
+ self.callbacks.push(callback);
+ });
+};
+
+Filter.prototype.trigger = function(messages) {
+ for(var i = 0; i < this.callbacks.length; i++) {
+ this.callbacks[i].call(this, messages);
+ }
+};
+
+Filter.prototype.uninstall = function() {
+ var self = this;
+ this.promise.then(function (id) {
+ self.impl.uninstallFilter(id);
+ web3.provider.stopPolling(id);
+ web3.off(impl.changed, id);
+ });
+};
+
+Filter.prototype.messages = function() {
+ var self = this;
+ return this.promise.then(function (id) {
+ return self.impl.getMessages(id);
+ });
+};
+
+Filter.prototype.logs = function () {
+ return this.messages();
+};
+
+function messageHandler(data) {
+ if(data._event !== undefined) {
+ web3.trigger(data._event, data._id, data.data);
+ return;
+ }
+
+ if(data._id) {
+ var cb = web3._callbacks[data._id];
+ if (cb) {
+ cb.call(this, data.error, data.data);
+ delete web3._callbacks[data._id];
+ }
+ }
+}
+
+module.exports = web3;
diff --git a/cmd/ethtest/lib/qt.js b/cmd/ethtest/lib/qt.js
new file mode 100644
index 000000000..f02239547
--- /dev/null
+++ b/cmd/ethtest/lib/qt.js
@@ -0,0 +1,45 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file qt.js
+ * @authors:
+ * Jeffrey Wilcke <jeff@ethdev.com>
+ * Marek Kotewicz <marek@ethdev.com>
+ * @date 2014
+ */
+
+var QtProvider = function() {
+ this.handlers = [];
+
+ var self = this;
+ navigator.qt.onmessage = function (message) {
+ self.handlers.forEach(function (handler) {
+ handler.call(self, JSON.parse(message.data));
+ });
+ };
+};
+
+QtProvider.prototype.send = function(payload) {
+ navigator.qt.postMessage(JSON.stringify(payload));
+};
+
+Object.defineProperty(QtProvider.prototype, "onmessage", {
+ set: function(handler) {
+ this.handlers.push(handler);
+ }
+});
+
+module.exports = QtProvider;
diff --git a/cmd/ethtest/lib/websocket.js b/cmd/ethtest/lib/websocket.js
new file mode 100644
index 000000000..24a072531
--- /dev/null
+++ b/cmd/ethtest/lib/websocket.js
@@ -0,0 +1,78 @@
+/*
+ This file is part of ethereum.js.
+
+ ethereum.js is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ethereum.js is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file websocket.js
+ * @authors:
+ * Jeffrey Wilcke <jeff@ethdev.com>
+ * Marek Kotewicz <marek@ethdev.com>
+ * Marian Oancea <marian@ethdev.com>
+ * @date 2014
+ */
+
+// TODO: work out which of the following two lines it is supposed to be...
+//if (process.env.NODE_ENV !== 'build') {
+if ("build" !== "build") {/*
+ var WebSocket = require('ws'); // jshint ignore:line
+*/}
+
+var WebSocketProvider = function(host) {
+ // onmessage handlers
+ this.handlers = [];
+ // queue will be filled with messages if send is invoked before the ws is ready
+ this.queued = [];
+ this.ready = false;
+
+ this.ws = new WebSocket(host);
+
+ var self = this;
+ this.ws.onmessage = function(event) {
+ for(var i = 0; i < self.handlers.length; i++) {
+ self.handlers[i].call(self, JSON.parse(event.data), event);
+ }
+ };
+
+ this.ws.onopen = function() {
+ self.ready = true;
+
+ for(var i = 0; i < self.queued.length; i++) {
+ // Resend
+ self.send(self.queued[i]);
+ }
+ };
+};
+
+WebSocketProvider.prototype.send = function(payload) {
+ if(this.ready) {
+ var data = JSON.stringify(payload);
+
+ this.ws.send(data);
+ } else {
+ this.queued.push(payload);
+ }
+};
+
+WebSocketProvider.prototype.onMessage = function(handler) {
+ this.handlers.push(handler);
+};
+
+WebSocketProvider.prototype.unload = function() {
+ this.ws.close();
+};
+Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
+ set: function(provider) { this.onMessage(provider); }
+});
+
+module.exports = WebSocketProvider;
diff --git a/cmd/ethtest/package.json b/cmd/ethtest/package.json
new file mode 100644
index 000000000..24141ea2e
--- /dev/null
+++ b/cmd/ethtest/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "ethereum.js",
+ "namespace": "ethereum",
+ "version": "0.0.5",
+ "description": "Ethereum Compatible JavaScript API",
+ "main": "./index.js",
+ "directories": {
+ "lib": "./lib"
+ },
+ "dependencies": {
+ "es6-promise": "*",
+ "ws": "*",
+ "xmlhttprequest": "*"
+ },
+ "devDependencies": {
+ "bower": ">=1.3.0",
+ "browserify": ">=6.0",
+ "del": ">=0.1.1",
+ "envify": "^3.0.0",
+ "exorcist": "^0.1.6",
+ "gulp": ">=3.4.0",
+ "gulp-jshint": ">=1.5.0",
+ "gulp-rename": ">=1.2.0",
+ "gulp-uglify": ">=1.0.0",
+ "jshint": ">=2.5.0",
+ "uglifyify": "^2.6.0",
+ "unreachable-branch-transform": "^0.1.0",
+ "vinyl-source-stream": "^1.0.0"
+ },
+ "scripts": {
+ "build": "gulp",
+ "watch": "gulp watch",
+ "lint": "gulp lint"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ethereum/ethereum.js.git"
+ },
+ "homepage": "https://github.com/ethereum/ethereum.js",
+ "bugs": {
+ "url": "https://github.com/ethereum/ethereum.js/issues"
+ },
+ "keywords": [
+ "ethereum",
+ "javascript",
+ "API"
+ ],
+ "author": "ethdev.com",
+ "authors": [
+ {
+ "name": "Jeffery Wilcke",
+ "email": "jeff@ethdev.com",
+ "url": "https://github.com/obscuren"
+ },
+ {
+ "name": "Marek Kotewicz",
+ "email": "marek@ethdev.com",
+ "url": "https://github.com/debris"
+ },
+ {
+ "name": "Marian Oancea",
+ "email": "marian@ethdev.com",
+ "url": "https://github.com/cubedro"
+ }
+ ],
+ "license": "LGPL-3.0"
+}
diff --git a/cmd/mist/assets/qml/webapp.qml b/cmd/mist/assets/qml/browser.qml
index bd7399dc9..abaab4f15 100644
--- a/cmd/mist/assets/qml/webapp.qml
+++ b/cmd/mist/assets/qml/browser.qml
@@ -66,7 +66,11 @@ Rectangle {
onMessages: {
// Bit of a cheat to get proper JSON
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
- webview.postEvent("messages", [m, id]);
+ webview.postEvent("messages", id, m);
+ }
+
+ function onShhMessage(message, id) {
+ webview.postEvent("shhChanged", id, message)
}
Item {
@@ -327,6 +331,33 @@ Rectangle {
require(1);
eth.uninstallFilter(data.args[0])
break;
+
+
+ case "shhNewFilter":
+ require(1);
+ var id = shh.watch(data.args[0], window);
+ postData(data._id, id);
+ break;
+
+ case "newIdentity":
+ postData(data._id, shh.newIdentity())
+ break
+
+ case "post":
+ require(1);
+ var params = data.args[0];
+ var fields = ["payload", "to", "from"];
+ for(var i = 0; i < fields.length; i++) {
+ params[fields[i]] = params[fields[i]] || "";
+ }
+ if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
+ params.topics = params.topics || [];
+ params.priority = params.priority || 1000;
+ params.ttl = params.ttl || 100;
+
+ console.log(JSON.stringify(params))
+ shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
+ break;
}
} catch(e) {
console.log(data.call + ": " + e)
@@ -348,8 +379,8 @@ Rectangle {
function postData(seed, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
}
- function postEvent(event, data) {
- webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
+ function postEvent(event, id, data) {
+ webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
}
function onWatchedCb(data, id) {
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml
index a08a8b4ef..06a7bc2a8 100644
--- a/cmd/mist/assets/qml/main.qml
+++ b/cmd/mist/assets/qml/main.qml
@@ -45,11 +45,12 @@ ApplicationWindow {
// Takes care of loading all default plugins
Component.onCompleted: {
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;
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
diff --git a/cmd/mist/assets/qml/views/whisper.qml b/cmd/mist/assets/qml/views/whisper.qml
new file mode 100644
index 000000000..56c4f1b07
--- /dev/null
+++ b/cmd/mist/assets/qml/views/whisper.qml
@@ -0,0 +1,76 @@
+
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Dialogs 1.0;
+import QtQuick.Window 2.1;
+import QtQuick.Controls.Styles 1.1
+import Ethereum 1.0
+
+Rectangle {
+ id: root
+ property var title: "Whisper Traffic"
+ property var iconSource: "../facet.png"
+ property var menuItem
+
+ objectName: "whisperView"
+ anchors.fill: parent
+
+ property var identity: ""
+ Component.onCompleted: {
+ identity = shh.newIdentity()
+ console.log("New identity:", identity)
+
+ var t = shh.watch({}, root)
+ }
+
+ function onShhMessage(message, i) {
+ whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
+ }
+
+ RowLayout {
+ id: input
+ anchors {
+ left: parent.left
+ leftMargin: 20
+ top: parent.top
+ topMargin: 20
+ }
+
+ TextField {
+ id: to
+ placeholderText: "To"
+ }
+ TextField {
+ id: data
+ placeholderText: "Data"
+ }
+ TextField {
+ id: topics
+ placeholderText: "topic1, topic2, topic3, ..."
+ }
+ Button {
+ text: "Send"
+ onClicked: {
+ shh.post([eth.toHex(data.text)], "", identity, topics.text.split(","), 500, 50)
+ }
+ }
+ }
+
+ TableView {
+ id: txTableView
+ anchors {
+ top: input.bottom
+ topMargin: 10
+ bottom: parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+ TableViewColumn{ id: fromRole; role: "from" ; title: "From"; width: 300 }
+ TableViewColumn{ role: "payload" ; title: "Payload" ; width: parent.width - fromRole.width - 2 }
+
+ model: ListModel {
+ id: whisperModel
+ }
+ }
+}
diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go
index e49408181..fcee28f19 100644
--- a/cmd/mist/flags.go
+++ b/cmd/mist/flags.go
@@ -36,10 +36,12 @@ var (
Identifier string
KeyRing string
KeyStore string
+ PMPGateway string
StartRpc bool
StartWebSockets bool
RpcPort int
UseUPnP bool
+ NatType string
OutboundPort string
ShowGenesis bool
AddPeer string
@@ -111,10 +113,12 @@ func Init() {
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
+ flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
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(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
+ flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go
index 7775889cc..e5e18bbaa 100644
--- a/cmd/mist/gui.go
+++ b/cmd/mist/gui.go
@@ -30,50 +30,19 @@ import (
"strings"
"time"
- "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/miner"
- "github.com/ethereum/go-ethereum/wire"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/ui/qt/qwhisper"
"github.com/ethereum/go-ethereum/xeth"
"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")
type Gui struct {
@@ -87,7 +56,8 @@ type Gui struct {
eth *eth.Ethereum
// The public Ethereum library
- uiLib *UiLib
+ uiLib *UiLib
+ whisper *qwhisper.Whisper
txDb *ethdb.LDBDatabase
@@ -97,7 +67,7 @@ type Gui struct {
pipe *xeth.JSXEth
Session string
- clientIdentity *wire.SimpleClientIdentity
+ clientIdentity *p2p.SimpleClientIdentity
config *ethutil.ConfigManager
plugins map[string]plugin
@@ -107,7 +77,7 @@ type Gui struct {
}
// Create GUI, but doesn't start it
-func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *wire.SimpleClientIdentity, session string, logLevel int) *Gui {
+func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *p2p.SimpleClientIdentity, session string, logLevel int) *Gui {
db, err := ethdb.NewLDBDatabase("tx_database")
if err != nil {
panic(err)
@@ -138,10 +108,12 @@ func (gui *Gui) Start(assetPath string) {
gui.engine = qml.NewEngine()
context := gui.engine.Context()
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
+ gui.whisper = qwhisper.New(gui.eth.Whisper())
// Expose the eth library and the ui library to QML
context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib)
+ context.SetVar("shh", gui.whisper)
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
@@ -249,7 +221,7 @@ func (gui *Gui) setInitialChain(ancientBlocks bool) {
sBlk := gui.eth.ChainManager().LastBlockHash()
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)
}
@@ -297,7 +269,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
addr := gui.address()
var inout string
- if bytes.Compare(tx.Sender(), addr) == 0 {
+ if bytes.Compare(tx.From(), addr) == 0 {
inout = "send"
} else {
inout = "recv"
@@ -317,7 +289,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
if send.Len() != 0 {
s = strings.Trim(send.Str(), "\x00")
} else {
- s = ethutil.Bytes2Hex(tx.Sender())
+ s = ethutil.Bytes2Hex(tx.From())
}
if rec.Len() != 0 {
r = strings.Trim(rec.Str(), "\x00")
@@ -350,7 +322,7 @@ func (gui *Gui) readPreviousTransactions() {
}
func (gui *Gui) processBlock(block *types.Block, initial bool) {
- name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
+ name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase()).Str(), "\x00")
b := xeth.NewJSBlock(block)
b.Name = name
@@ -391,6 +363,8 @@ func (gui *Gui) update() {
gui.setPeerInfo()
}()
+ gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
+
for _, plugin := range gui.plugins {
guilogger.Infoln("Loading plugin ", plugin.Name)
@@ -409,8 +383,7 @@ func (gui *Gui) update() {
miningLabel := gui.getObjectByName("miningLabel")
events := gui.eth.EventMux().Subscribe(
- eth.ChainSyncEvent{},
- eth.PeerListEvent{},
+ //eth.PeerListEvent{},
core.NewBlockEvent{},
core.TxPreEvent{},
core.TxPostEvent{},
@@ -427,7 +400,7 @@ func (gui *Gui) update() {
switch ev := ev.(type) {
case core.NewBlockEvent:
gui.processBlock(ev.Block, false)
- if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 {
+ if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
}
@@ -448,7 +421,7 @@ func (gui *Gui) update() {
tx := ev.Tx
object := state.GetAccount(gui.address())
- if bytes.Compare(tx.Sender(), gui.address()) == 0 {
+ if bytes.Compare(tx.From(), gui.address()) == 0 {
object.SubAmount(tx.Value())
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
@@ -460,28 +433,27 @@ func (gui *Gui) update() {
gui.setWalletValue(object.Balance(), nil)
state.UpdateStateObject(object)
-
- case eth.PeerListEvent:
- gui.setPeerInfo()
}
case <-peerUpdateTicker.C:
gui.setPeerInfo()
case <-generalUpdateTicker.C:
- statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number.String()
+ 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
+ /*
+ 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))
+ 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()
@@ -509,7 +481,7 @@ Heap Alloc: %d
CGNext: %x
NumGC: %d
`, Version, runtime.Version(),
- eth.ProtocolVersion, eth.P2PVersion,
+ eth.ProtocolVersion, 2,
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
memStats.Alloc, memStats.HeapAlloc,
memStats.NextGC, memStats.NumGC,
@@ -531,3 +503,35 @@ func (gui *Gui) privateKey() string {
func (gui *Gui) address() []byte {
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))
+ }
+*/
diff --git a/cmd/mist/html_container.go b/cmd/mist/html_container.go
index b3fc219fa..bd11ccd57 100644
--- a/cmd/mist/html_container.go
+++ b/cmd/mist/html_container.go
@@ -139,7 +139,7 @@ func (app *HtmlApplication) Window() *qml.Window {
}
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)
}
diff --git a/cmd/mist/main.go b/cmd/mist/main.go
index 7c38d9977..6f578ff48 100644
--- a/cmd/mist/main.go
+++ b/cmd/mist/main.go
@@ -23,8 +23,8 @@ import (
"runtime"
"time"
- "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger"
"gopkg.in/qml.v1"
)
@@ -58,8 +58,8 @@ func run() error {
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
- clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
- ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
+ 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)
@@ -104,16 +104,10 @@ func main() {
utils.HandleInterrupt()
- if StartWebSockets {
- utils.StartWebSockets(ethereum)
- }
-
// we need to run the interrupt callbacks in case gui is closed
// this skips if we got here by actual interrupt stopping the GUI
if !interrupted {
utils.RunInterruptCallbacks(os.Interrupt)
}
- // this blocks the thread
- ethereum.WaitForShutdown()
logger.Flush()
}
diff --git a/cmd/mist/qml_container.go b/cmd/mist/qml_container.go
index a0a46f9b1..ed24737d0 100644
--- a/cmd/mist/qml_container.go
+++ b/cmd/mist/qml_container.go
@@ -66,7 +66,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
// Events
func (app *QmlApplication) NewBlock(block *types.Block) {
- pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
+ pblock := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
app.win.Call("onNewBlockCb", pblock)
}
diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go
index fdbde50fd..4a92f6479 100644
--- a/cmd/mist/ui_lib.go
+++ b/cmd/mist/ui_lib.go
@@ -24,11 +24,12 @@ import (
"strconv"
"strings"
- "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/event/filter"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/state"
@@ -57,6 +58,7 @@ type UiLib struct {
jsEngine *javascript.JSRE
filterCallbacks map[int][]int
+ filterManager *filter.FilterManager
miner *miner.Miner
}
@@ -64,6 +66,7 @@ type UiLib struct {
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
lib.miner = miner.New(eth.KeyManager().Address(), eth)
+ lib.filterManager = filter.NewFilterManager(eth.EventMux())
return lib
}
@@ -123,7 +126,8 @@ func (self *UiLib) LookupAddress(name string) string {
}
func (self *UiLib) PastPeers() *ethutil.List {
- return ethutil.NewList(eth.PastPeers())
+ return ethutil.NewList([]string{})
+ //return ethutil.NewList(eth.PastPeers())
}
func (self *UiLib) ImportTx(rlpTx string) {
@@ -191,7 +195,7 @@ func (ui *UiLib) Connect(button qml.Object) {
}
func (ui *UiLib) ConnectToPeer(addr string) {
- ui.eth.ConnectToPeer(addr)
+ ui.eth.SuggestPeer(addr)
}
func (ui *UiLib) AssetPath(p string) string {
@@ -221,12 +225,89 @@ func (self *UiLib) StartDebugger() {
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) 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) {
filter := qt.NewFilterFromMap(object, self.eth)
filter.MessageCallback = func(messages state.Messages) {
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
}
- id = self.eth.InstallFilter(filter)
+ id = self.filterManager.InstallFilter(filter)
return id
}
@@ -239,12 +320,12 @@ func (self *UiLib) NewFilterString(typ string) (id int) {
fmt.Println("QML is lagging")
}
}
- id = self.eth.InstallFilter(filter)
+ id = self.filterManager.InstallFilter(filter)
return id
}
func (self *UiLib) Messages(id int) *ethutil.List {
- filter := self.eth.GetFilter(id)
+ filter := self.filterManager.GetFilter(id)
if filter != nil {
messages := xeth.ToJSMessages(filter.Find())
@@ -255,7 +336,7 @@ func (self *UiLib) Messages(id int) *ethutil.List {
}
func (self *UiLib) UninstallFilter(id int) {
- self.eth.UninstallFilter(id)
+ self.filterManager.UninstallFilter(id)
}
func mapToTxParams(object map[string]interface{}) map[string]string {
@@ -308,67 +389,3 @@ func mapToTxParams(object map[string]interface{}) map[string]string {
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
- }
-}
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index db7bcd35e..466c51383 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -4,23 +4,25 @@ import (
"fmt"
"io"
"log"
+ "net"
"os"
"os/signal"
"path"
"path/filepath"
"regexp"
"runtime"
- "time"
"bitbucket.org/kardianos/osext"
- "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"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/wire"
"github.com/ethereum/go-ethereum/xeth"
)
@@ -144,17 +146,32 @@ func NewDatabase() ethutil.Database {
return db
}
-func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity {
- return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
+func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
+ return p2p.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier, pubkey)
}
-func NewEthereum(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
- ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
+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)
}
- ethereum.Port = OutboundPort
- ethereum.MaxPeers = MaxPeer
return ethereum
}
@@ -268,11 +285,6 @@ func StartMining(ethereum *eth.Ethereum) bool {
if gminer == nil {
gminer = miner.New(addr, ethereum)
}
- // Give it some time to connect with peers
- time.Sleep(3 * time.Second)
- for !ethereum.IsUpToDate() {
- time.Sleep(5 * time.Second)
- }
gminer.Start()
}()
RegisterInterrupt(func(os.Signal) {
@@ -315,7 +327,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
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)
if err != nil {
@@ -325,3 +337,25 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
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
+}
diff --git a/cmd/utils/vm_env.go b/cmd/utils/vm_env.go
index 461a797c2..be6249e82 100644
--- a/cmd/utils/vm_env.go
+++ b/cmd/utils/vm_env.go
@@ -30,15 +30,15 @@ func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *
}
func (self *VMEnv) Origin() []byte { return self.transactor }
-func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
-func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
-func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
-func (self *VMEnv) Time() int64 { return self.block.Time }
-func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
+func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
+func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
+func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
+func (self *VMEnv) Time() int64 { return self.block.Time() }
+func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
+func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) State() *state.StateDB { return self.state }
-func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) AddLog(log state.Log) {
diff --git a/cmd/utils/websockets.go b/cmd/utils/websockets.go
index cf9ebba92..e4bc1b185 100644
--- a/cmd/utils/websockets.go
+++ b/cmd/utils/websockets.go
@@ -1,7 +1,7 @@
package utils
import (
- "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/websocket"
diff --git a/core/block_manager.go b/core/block_manager.go
index 2567e39c4..8a5455306 100644
--- a/core/block_manager.go
+++ b/core/block_manager.go
@@ -2,7 +2,6 @@ package core
import (
"bytes"
- "container/list"
"errors"
"fmt"
"math/big"
@@ -14,10 +13,11 @@ import (
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/wire"
+ "gopkg.in/fatih/set.v0"
)
var statelogger = logger.NewLogger("BLOCK")
@@ -38,13 +38,12 @@ type EthManager interface {
BlockManager() *BlockManager
ChainManager() *ChainManager
TxPool() *TxPool
- Broadcast(msgType wire.MsgType, data []interface{})
PeerCount() int
IsMining() bool
IsListening() bool
- Peers() *list.List
+ Peers() []*p2p.Peer
KeyManager() *crypto.KeyManager
- ClientIdentity() wire.ClientIdentity
+ ClientIdentity() p2p.ClientIdentity
Db() ethutil.Database
EventMux() *event.TypeMux
}
@@ -84,8 +83,8 @@ func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event
}
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
- coinbase := statedb.GetOrNewStateObject(block.Coinbase)
- coinbase.SetGasPool(block.CalcGasLimit(parent))
+ coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
+ coinbase.SetGasPool(CalcGasLimit(parent, block))
// Process the transactions on to current block
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
@@ -158,7 +157,7 @@ done:
}
block.Reward = cumulativeSum
- block.GasUsed = totalUsedGas
+ block.Header().GasUsed = totalUsedGas
return receipts, handled, unhandled, erroneous, err
}
@@ -168,14 +167,15 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
sm.mutex.Lock()
defer sm.mutex.Unlock()
- if sm.bc.HasBlock(block.Hash()) {
- return nil, nil, &KnownBlockError{block.Number, block.Hash()}
+ header := block.Header()
+ if sm.bc.HasBlock(header.Hash()) {
+ return nil, nil, &KnownBlockError{header.Number, header.Hash()}
}
- if !sm.bc.HasBlock(block.PrevHash) {
- return nil, nil, ParentError(block.PrevHash)
+ if !sm.bc.HasBlock(header.ParentHash) {
+ return nil, nil, ParentError(header.ParentHash)
}
- parent := sm.bc.GetBlock(block.PrevHash)
+ parent := sm.bc.GetBlock(header.ParentHash)
return sm.ProcessWithParent(block, parent)
}
@@ -183,13 +183,7 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
sm.lastAttemptedBlock = block
- state := parent.State().Copy()
-
- // Defer the Undo on the Trie. If the block processing happened
- // we don't want to undo but since undo only happens on dirty
- // nodes this won't happen because Commit would have been called
- // before that.
- defer state.Reset()
+ state := state.New(parent.Trie().Copy())
// Block validation
if err = sm.ValidateBlock(block, parent); err != nil {
@@ -201,23 +195,24 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
return
}
+ header := block.Header()
+
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)
return
}
txSha := types.DeriveSha(block.Transactions())
- if bytes.Compare(txSha, block.TxSha) != 0 {
- err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
+ if bytes.Compare(txSha, header.TxHash) != 0 {
+ err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
return
}
receiptSha := types.DeriveSha(receipts)
- if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
- //chainlogger.Debugf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
- fmt.Printf("%x\n", ethutil.Encode(receipts))
- err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
+ if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
+ fmt.Println("receipts", receipts)
+ err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
return
}
@@ -227,8 +222,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
state.Update(ethutil.Big0)
- if !block.State().Cmp(state) {
- err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
+ if !bytes.Equal(header.Root, state.Root()) {
+ err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
return
}
@@ -240,7 +235,7 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
messages := state.Manifest().Messages
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())
@@ -252,14 +247,14 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
uncleDiff := new(big.Int)
- for _, uncle := range block.Uncles {
+ for _, uncle := range block.Uncles() {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
td := new(big.Int)
td = td.Add(sm.bc.Td(), uncleDiff)
- td = td.Add(td, block.Difficulty)
+ td = td.Add(td, block.Header().Difficulty)
// The new TD will only be accepted if the new difficulty is
// is greater than the previous.
@@ -275,13 +270,13 @@ func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
expd := CalcDifficulty(block, parent)
- if expd.Cmp(block.Difficulty) < 0 {
- return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
+ if expd.Cmp(block.Header().Difficulty) < 0 {
+ return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
}
- diff := block.Time - parent.Time
+ diff := block.Header().Time - parent.Header().Time
if diff < 0 {
- 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
@@ -293,7 +288,7 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
// Verify the nonce of the block. Return an error if it's not valid
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
@@ -302,24 +297,28 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
reward := new(big.Int).Set(BlockReward)
- knownUncles := ethutil.Set(parent.Uncles)
- nonces := ethutil.NewSet(block.Nonce)
- for _, uncle := range block.Uncles {
+ knownUncles := set.New()
+ for _, uncle := range parent.Uncles() {
+ knownUncles.Add(string(uncle.Hash()))
+ }
+
+ nonces := ethutil.NewSet(block.Header().Nonce)
+ for _, uncle := range block.Uncles() {
if nonces.Include(uncle.Nonce) {
// Error not unique
return UncleError("Uncle not unique")
}
- uncleParent := sm.bc.GetBlock(uncle.PrevHash)
+ uncleParent := sm.bc.GetBlock(uncle.ParentHash)
if uncleParent == nil {
- return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
+ return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
}
- if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
+ if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 {
return UncleError("Uncle too old")
}
- if knownUncles.Include(uncle.Hash()) {
+ if knownUncles.Has(string(uncle.Hash())) {
return UncleError("Uncle in chain")
}
@@ -335,15 +334,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
}
// 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
account.AddAmount(reward)
statedb.Manifest().AddMessage(&state.Message{
- To: block.Coinbase,
+ To: block.Header().Coinbase,
Input: nil,
Origin: nil,
- Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number,
+ Block: block.Hash(), Timestamp: int64(block.Header().Time), Coinbase: block.Header().Coinbase, Number: block.Header().Number,
Value: new(big.Int).Add(reward, block.Reward),
})
@@ -351,15 +350,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
}
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
- if !sm.bc.HasBlock(block.PrevHash) {
- return nil, ParentError(block.PrevHash)
+ if !sm.bc.HasBlock(block.Header().ParentHash) {
+ return nil, ParentError(block.Header().ParentHash)
}
sm.lastAttemptedBlock = block
var (
- parent = sm.bc.GetBlock(block.PrevHash)
- state = parent.State().Copy()
+ parent = sm.bc.GetBlock(block.Header().ParentHash)
+ state = state.New(parent.Trie().Copy())
)
defer state.Reset()
diff --git a/core/chain_manager.go b/core/chain_manager.go
index 794ae0011..485c195d5 100644
--- a/core/chain_manager.go
+++ b/core/chain_manager.go
@@ -1,6 +1,7 @@
package core
import (
+ "bytes"
"fmt"
"math/big"
"sync"
@@ -9,11 +10,13 @@ import (
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/state"
)
var chainlogger = logger.NewLogger("CHAIN")
+/*
func AddTestNetFunds(block *types.Block) {
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
@@ -31,20 +34,41 @@ func AddTestNetFunds(block *types.Block) {
block.State().UpdateStateObject(account)
}
}
+*/
func CalcDifficulty(block, parent *types.Block) *big.Int {
diff := new(big.Int)
- adjust := new(big.Int).Rsh(parent.Difficulty, 10)
- if block.Time >= parent.Time+5 {
- diff.Sub(parent.Difficulty, adjust)
+ bh, ph := block.Header(), parent.Header()
+ adjust := new(big.Int).Rsh(ph.Difficulty, 10)
+ if bh.Time >= ph.Time+5 {
+ diff.Sub(ph.Difficulty, adjust)
} else {
- diff.Add(parent.Difficulty, adjust)
+ diff.Add(ph.Difficulty, adjust)
}
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 {
//eth EthManager
processor types.BlockProcessor
@@ -90,7 +114,7 @@ func (self *ChainManager) CurrentBlock() *types.Block {
func NewChainManager(mux *event.TypeMux) *ChainManager {
bc := &ChainManager{}
- bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
+ bc.genesisBlock = GenesisBlock()
bc.eventMux = mux
bc.setLastBlock()
@@ -100,12 +124,19 @@ func NewChainManager(mux *event.TypeMux) *ChainManager {
return bc
}
+func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
+ self.mu.RLock()
+ defer self.mu.RUnlock()
+
+ return self.td, self.currentBlock.Hash(), self.Genesis().Hash()
+}
+
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
self.processor = proc
}
func (self *ChainManager) State() *state.StateDB {
- return self.CurrentBlock().State()
+ return state.New(self.CurrentBlock().Trie())
}
func (self *ChainManager) TransState() *state.StateDB {
@@ -115,13 +146,11 @@ func (self *ChainManager) TransState() *state.StateDB {
func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
- // Prep genesis
- AddTestNetFunds(bc.genesisBlock)
-
- block := types.NewBlockFromBytes(data)
- bc.currentBlock = block
+ var block types.Block
+ rlp.Decode(bytes.NewReader(data), &block)
+ bc.currentBlock = &block
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)
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
@@ -137,27 +166,28 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
bc.mu.RLock()
defer bc.mu.RUnlock()
- var root interface{}
- hash := ZeroHash256
+ var root []byte
+ parentHash := ZeroHash256
if bc.CurrentBlock != nil {
- root = bc.currentBlock.Root()
- hash = bc.lastBlockHash
+ root = bc.currentBlock.Header().Root
+ parentHash = bc.lastBlockHash
}
- block := types.CreateBlock(
- root,
- hash,
+ block := types.NewBlock(
+ parentHash,
coinbase,
+ root,
ethutil.BigPow(2, 32),
nil,
"")
parent := bc.currentBlock
if parent != nil {
- block.Difficulty = CalcDifficulty(block, parent)
- block.Number = new(big.Int).Add(bc.currentBlock.Number, ethutil.Big1)
- block.GasLimit = block.CalcGasLimit(bc.currentBlock)
+ header := block.Header()
+ header.Difficulty = CalcDifficulty(block, parent)
+ header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
+ header.GasLimit = CalcGasLimit(parent, block)
}
@@ -168,36 +198,34 @@ func (bc *ChainManager) Reset() {
bc.mu.Lock()
defer bc.mu.Unlock()
- AddTestNetFunds(bc.genesisBlock)
+ for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
+ ethutil.Config.Db.Delete(block.Hash())
+ }
- bc.genesisBlock.Trie().Sync()
// Prepare the genesis block
bc.write(bc.genesisBlock)
bc.insert(bc.genesisBlock)
bc.currentBlock = bc.genesisBlock
bc.setTotalDifficulty(ethutil.Big("0"))
-
- // Set the last know difficulty (might be 0x0 as initial value, Genesis)
- bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
}
func (self *ChainManager) Export() []byte {
self.mu.RLock()
defer self.mu.RUnlock()
- chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Number)
+ chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
- blocks := make([]*types.Block, int(self.currentBlock.Number.Int64())+1)
- for block := self.currentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
- blocks[block.Number.Int64()] = block
+ blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
+ for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
+ blocks[block.NumberU64()] = block
}
return ethutil.Encode(blocks)
}
func (bc *ChainManager) insert(block *types.Block) {
- encodedBlock := block.RlpEncode()
+ encodedBlock := ethutil.Encode(block)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
bc.currentBlock = block
bc.lastBlockHash = block.Hash()
@@ -206,7 +234,7 @@ func (bc *ChainManager) insert(block *types.Block) {
func (bc *ChainManager) write(block *types.Block) {
bc.writeBlockInfo(block)
- encodedBlock := block.RlpEncode()
+ encodedBlock := ethutil.Encode(block)
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
}
@@ -221,7 +249,7 @@ func (bc *ChainManager) HasBlock(hash []byte) bool {
return len(data) != 0
}
-func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
+func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
block := self.GetBlock(hash)
if block == nil {
return
@@ -231,11 +259,11 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
for i := uint64(0); i < max; i++ {
chain = append(chain, block.Hash())
- if block.Number.Cmp(ethutil.Big0) <= 0 {
+ if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
break
}
- block = self.GetBlock(block.PrevHash)
+ block = self.GetBlock(block.Header().ParentHash)
}
return
@@ -246,8 +274,13 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
if len(data) == 0 {
return nil
}
+ var block types.Block
+ if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
+ fmt.Println(err)
+ return nil
+ }
- return types.NewBlockFromBytes(data)
+ return &block
}
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
@@ -255,13 +288,13 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
defer self.mu.RUnlock()
block := self.currentBlock
- for ; block != nil; block = self.GetBlock(block.PrevHash) {
- if block.Number.Uint64() == num {
+ for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
+ if block.Header().Number.Uint64() == num {
break
}
}
- if block != nil && block.Number.Uint64() == 0 && num != 0 {
+ if block != nil && block.Header().Number.Uint64() == 0 && num != 0 {
return nil
}
@@ -274,40 +307,28 @@ func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
}
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
- parent := self.GetBlock(block.PrevHash)
+ parent := self.GetBlock(block.Header().ParentHash)
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)
- for _, uncle := range block.Uncles {
+ for _, uncle := range block.Uncles() {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
td := new(big.Int)
td = td.Add(parentTd, uncleDiff)
- td = td.Add(td, block.Difficulty)
+ td = td.Add(td, block.Header().Difficulty)
return td, nil
}
-func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
- bi := types.BlockInfo{}
- data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
- bi.RlpDecode(data)
-
- return bi
-}
-
// Unexported method for writing extra non-essential block info to the db
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
bc.lastBlockNumber++
- bi := types.BlockInfo{Number: bc.lastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.td}
-
- // For now we use the block hash with the words "info" appended as key
- ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}
func (bc *ChainManager) Stop() {
@@ -324,7 +345,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
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(err)
return err
@@ -332,16 +354,16 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
self.mu.Lock()
{
-
self.write(block)
+ cblock := self.currentBlock
if td.Cmp(self.td) > 0 {
- if block.Number.Cmp(new(big.Int).Add(self.currentBlock.Number, ethutil.Big1)) < 0 {
- chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Number, block.Hash()[:4], self.currentBlock.Number, self.currentBlock.Hash()[:4])
+ if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
+ chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
}
self.setTotalDifficulty(td)
self.insert(block)
- self.transState = self.currentBlock.State().Copy()
+ self.transState = state.New(cblock.Trie().Copy())
}
}
diff --git a/core/events.go b/core/events.go
index deeba3e98..fe106da49 100644
--- a/core/events.go
+++ b/core/events.go
@@ -10,3 +10,6 @@ type TxPostEvent struct{ Tx *types.Transaction }
// NewBlockEvent is posted when a block has been imported.
type NewBlockEvent struct{ Block *types.Block }
+
+// NewMinedBlockEvent is posted when a block has been imported.
+type NewMinedBlockEvent struct{ Block *types.Block }
diff --git a/core/filter.go b/core/filter.go
index fb992782d..7c34748df 100644
--- a/core/filter.go
+++ b/core/filter.go
@@ -76,13 +76,14 @@ func (self *Filter) SetSkip(skip int) {
// Run filters messages with the current parameters set
func (self *Filter) Find() []*state.Message {
+ earliestBlock := self.eth.ChainManager().CurrentBlock()
var earliestBlockNo uint64 = uint64(self.earliest)
if self.earliest == -1 {
- earliestBlockNo = self.eth.ChainManager().CurrentBlock().Number.Uint64()
+ earliestBlockNo = earliestBlock.NumberU64()
}
var latestBlockNo uint64 = uint64(self.latest)
if self.latest == -1 {
- latestBlockNo = self.eth.ChainManager().CurrentBlock().Number.Uint64()
+ latestBlockNo = earliestBlock.NumberU64()
}
var (
@@ -93,7 +94,7 @@ func (self *Filter) Find() []*state.Message {
for i := 0; !quit && block != nil; i++ {
// Quit on latest
switch {
- case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0:
+ case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
quit = true
case self.max <= len(messages):
break
@@ -113,7 +114,7 @@ func (self *Filter) Find() []*state.Message {
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)))
@@ -176,7 +177,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
var fromIncluded, toIncluded bool
if len(self.from) > 0 {
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
break
}
@@ -187,7 +188,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
if len(self.to) > 0 {
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
break
}
diff --git a/core/genesis.go b/core/genesis.go
index 707154759..10b40516f 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -3,8 +3,10 @@ package core
import (
"math/big"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/state"
)
/*
@@ -17,36 +19,35 @@ var ZeroHash512 = make([]byte, 64)
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
-var GenesisHeader = []interface{}{
- // Previous hash (none)
- ZeroHash256,
- // Empty uncles
- EmptyShaList,
- // Coinbase
- ZeroHash160,
- // Root state
- EmptyShaList,
- // tx root
- EmptyListRoot,
- // receipt root
- EmptyListRoot,
- // bloom
- ZeroHash512,
- // Difficulty
- //ethutil.BigPow(2, 22),
- big.NewInt(131072),
- // Number
- ethutil.Big0,
- // Block upper gas bound
- big.NewInt(1000000),
- // Block gas used
- ethutil.Big0,
- // Time
- ethutil.Big0,
- // Extra
- nil,
- // Nonce
- crypto.Sha3(big.NewInt(42).Bytes()),
-}
+func GenesisBlock() *types.Block {
+ genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
+ genesis.Header().Number = ethutil.Big0
+ genesis.Header().GasLimit = big.NewInt(1000000)
+ genesis.Header().GasUsed = ethutil.Big0
+ genesis.Header().Time = 0
+
+ genesis.SetUncles([]*types.Header{})
+ genesis.SetTransactions(types.Transactions{})
+ genesis.SetReceipts(types.Receipts{})
-var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
+ 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
+}
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 58c2255a4..1149d4cfb 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -11,7 +11,6 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/wire"
)
var txplogger = logger.NewLogger("TXP")
@@ -19,7 +18,9 @@ var txplogger = logger.NewLogger("TXP")
const txPoolQueueSize = 50
type TxPoolHook chan *types.Transaction
-type TxMsgTy byte
+type TxMsg struct {
+ Tx *types.Transaction
+}
const (
minGasPrice = 1000000
@@ -27,11 +28,6 @@ const (
var MinGasPrice = big.NewInt(10000000000000)
-type TxMsg struct {
- Tx *types.Transaction
- Type TxMsgTy
-}
-
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
for e := pool.Front(); e != nil; e = e.Next() {
if it(e.Value.(*types.Transaction), e) {
@@ -76,19 +72,17 @@ type TxPool struct {
subscribers []chan TxMsg
- broadcaster types.Broadcaster
chainManager *ChainManager
eventMux *event.TypeMux
}
-func NewTxPool(chainManager *ChainManager, broadcaster types.Broadcaster, eventMux *event.TypeMux) *TxPool {
+func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool {
return &TxPool{
pool: list.New(),
queueChan: make(chan *types.Transaction, txPoolQueueSize),
quit: make(chan bool),
chainManager: chainManager,
eventMux: eventMux,
- broadcaster: broadcaster,
}
}
@@ -100,7 +94,7 @@ func (pool *TxPool) addTransaction(tx *types.Transaction) {
pool.pool.PushBack(tx)
// Broadcast the transaction to the rest of the peers
- pool.broadcaster.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()})
+ pool.eventMux.Post(TxPreEvent{tx})
}
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
@@ -122,7 +116,11 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
}
// Get the sender
- sender := pool.chainManager.State().GetAccount(tx.Sender())
+ senderAddr := tx.From()
+ if senderAddr == nil {
+ return fmt.Errorf("invalid sender")
+ }
+ sender := pool.chainManager.State().GetAccount(senderAddr)
totAmount := new(big.Int).Set(tx.Value())
// Make sure there's enough in the sender's account. Having insufficient
@@ -166,7 +164,17 @@ func (self *TxPool) Size() int {
return self.pool.Len()
}
-func (pool *TxPool) CurrentTransactions() []*types.Transaction {
+func (self *TxPool) AddTransactions(txs []*types.Transaction) {
+ for _, tx := range txs {
+ if err := self.Add(tx); err != nil {
+ txplogger.Infoln(err)
+ } else {
+ txplogger.Infof("tx %x\n", tx.Hash()[0:4])
+ }
+ }
+}
+
+func (pool *TxPool) GetTransactions() []*types.Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
@@ -189,7 +197,7 @@ func (pool *TxPool) RemoveInvalid(state *state.StateDB) {
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*types.Transaction)
- sender := state.GetAccount(tx.Sender())
+ sender := state.GetAccount(tx.From())
err := pool.ValidateTransaction(tx)
if err != nil || sender.Nonce >= tx.Nonce() {
pool.pool.Remove(e)
@@ -213,7 +221,7 @@ func (self *TxPool) RemoveSet(txs types.Transactions) {
}
func (pool *TxPool) Flush() []*types.Transaction {
- txList := pool.CurrentTransactions()
+ txList := pool.GetTransactions()
// Recreate a new list all together
// XXX Is this the fastest way?
diff --git a/core/types/block.go b/core/types/block.go
index 2d889f35f..054767d67 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -9,408 +9,258 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/ptrie"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/trie"
)
-type BlockInfo struct {
- Number uint64
- Hash []byte
- Parent []byte
- TD *big.Int
-}
-
-func (bi *BlockInfo) RlpDecode(data []byte) {
- decoder := ethutil.NewValueFromBytes(data)
-
- bi.Number = decoder.Get(0).Uint()
- bi.Hash = decoder.Get(1).Bytes()
- bi.Parent = decoder.Get(2).Bytes()
- bi.TD = decoder.Get(3).BigInt()
-}
-
-func (bi *BlockInfo) RlpEncode() []byte {
- return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD})
-}
-
-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
-
-func (self BlockBy) Sort(blocks Blocks) {
- bs := blockSorter{
- blocks: blocks,
- by: self,
- }
- sort.Sort(bs)
-}
-
-type blockSorter struct {
- blocks Blocks
- by func(b1, b2 *Block) bool
-}
-
-func (self blockSorter) Len() int { return len(self.blocks) }
-func (self blockSorter) Swap(i, j int) {
- self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
-}
-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 }
-
-type Block struct {
+type Header struct {
// Hash to the previous block
- PrevHash ethutil.Bytes
+ ParentHash ethutil.Bytes
// Uncles of this block
- Uncles Blocks
- UncleSha []byte
+ UncleHash []byte
// The coin base address
Coinbase []byte
// Block Trie state
- //state *ethutil.Trie
- state *state.StateDB
+ Root []byte
+ // Tx sha
+ TxHash []byte
+ // Receipt sha
+ ReceiptHash []byte
+ // Bloom
+ Bloom []byte
// 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
+ // Creation time
+ Time uint64
// 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)
+func (self *Header) rlpData(withNonce bool) []interface{} {
+ 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)
+ }
- return block
+ return fields
}
-// New block takes a raw encoded string
-func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
- block := &Block{}
- block.RlpValueDecode(rlpValue)
+func (self *Header) RlpData() interface{} {
+ return self.rlpData(true)
+}
- return block
+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)))
}
-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(),
+type Block struct {
+ header *Header
+ uncles []*Header
+ transactions Transactions
+ Td *big.Int
+
+ receipts Receipts
+ Reward *big.Int
+}
+
+func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, extra string) *Block {
+ header := &Header{
+ Root: root,
+ ParentHash: parentHash,
+ Coinbase: coinbase,
+ Difficulty: difficulty,
+ Nonce: nonce,
+ Time: uint64(time.Now().Unix()),
Extra: extra,
- UncleSha: nil,
GasUsed: new(big.Int),
GasLimit: new(big.Int),
}
- block.SetUncles([]*Block{})
- block.state = state.New(trie.New(ethutil.Config.Db, root))
+ block := &Block{header: header, Reward: new(big.Int)}
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 NewBlockWithHeader(header *Header) *Block {
+ return &Block{header: header}
}
-func (block *Block) State() *state.StateDB {
- return block.state
-}
+func (self *Block) DecodeRLP(s *rlp.Stream) error {
+ if _, err := s.List(); err != nil {
+ return err
+ }
-func (block *Block) Transactions() Transactions {
- return block.transactions
-}
+ var header Header
+ if err := s.Decode(&header); err != nil {
+ return err
+ }
-func (block *Block) CalcGasLimit(parent *Block) *big.Int {
- if block.Number.Cmp(big.NewInt(0)) == 0 {
- return ethutil.BigPow(10, 6)
+ var transactions []*Transaction
+ if err := s.Decode(&transactions); err != nil {
+ return err
}
- // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
+ var uncleHeaders []*Header
+ if err := s.Decode(&uncleHeaders); err != nil {
+ return err
+ }
- 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())
+ var tdBytes []byte
+ if err := s.Decode(&tdBytes); err != nil {
+ // If this block comes from the network that's fine. If loaded from disk it should be there
+ // Blocks don't store their Td when propagated over the network
+ } else {
+ self.Td = ethutil.BigD(tdBytes)
+ }
- result := new(big.Int).Add(previous, curInt)
- result.Div(result, big.NewInt(1024))
+ if err := s.ListEnd(); err != nil {
+ return err
+ }
- min := big.NewInt(125000)
+ self.header = &header
+ self.uncles = uncleHeaders
+ self.transactions = transactions
- return ethutil.BigMax(min, result)
+ return nil
}
-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) Header() *Header {
+ return self.header
}
-func (self *Block) GetTransaction(hash []byte) *Transaction {
- for _, tx := range self.transactions {
- if bytes.Compare(tx.Hash(), hash) == 0 {
- return tx
- }
- }
-
- return nil
+func (self *Block) Uncles() []*Header {
+ return self.uncles
}
-// Sync the block's state and contract respectively
-func (block *Block) Sync() {
- block.state.Sync()
+func (self *Block) SetUncles(uncleHeaders []*Header) {
+ self.uncles = uncleHeaders
+ self.header.UncleHash = crypto.Sha3(ethutil.Encode(uncleHeaders))
}
-func (block *Block) Undo() {
- // Sync the block state itself
- block.state.Reset()
+func (self *Block) Transactions() Transactions {
+ return self.transactions
}
-/////// 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()
+func (self *Block) Transaction(hash []byte) *Transaction {
+ for _, transaction := range self.transactions {
+ if bytes.Equal(hash, transaction.Hash()) {
+ return transaction
+ }
}
-
- return encR
+ return nil
}
-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 (self *Block) SetTransactions(transactions Transactions) {
+ self.transactions = transactions
+ self.header.TxHash = DeriveSha(transactions)
}
-func (block *Block) SetUncles(uncles []*Block) {
- block.Uncles = uncles
- block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles()))
+func (self *Block) Receipts() Receipts {
+ return self.receipts
}
func (self *Block) SetReceipts(receipts Receipts) {
self.receipts = receipts
- self.ReceiptSha = DeriveSha(receipts)
- self.LogsBloom = CreateBloom(receipts)
+ self.header.ReceiptHash = DeriveSha(receipts)
+ self.header.Bloom = 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 (self *Block) RlpData() interface{} {
+ return []interface{}{self.header, self.transactions, self.uncles}
}
-func (block *Block) RlpDecode(data []byte) {
- rlpValue := ethutil.NewValueFromBytes(data)
- block.RlpValueDecode(rlpValue)
+func (self *Block) RlpDataForStorage() interface{} {
+ return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
}
-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))
- }
- }
+// Header accessors (add as you need them)
+func (self *Block) Number() *big.Int { return self.header.Number }
+func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
+func (self *Block) ParentHash() []byte { return self.header.ParentHash }
+func (self *Block) Bloom() []byte { return self.header.Bloom }
+func (self *Block) Coinbase() []byte { return self.header.Coinbase }
+func (self *Block) Time() int64 { return int64(self.header.Time) }
+func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
+func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
+func (self *Block) Hash() []byte { return self.header.Hash() }
+func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
+func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
+func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
+// Implement block.Pow
+func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
+func (self *Block) N() []byte { return self.header.Nonce }
+func (self *Block) HashNoNonce() []byte {
+ return crypto.Sha3(ethutil.Encode(self.header.rlpData(false)))
}
-func (self *Block) 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 (self *Block) String() string {
+ return fmt.Sprintf(`BLOCK(%x): Size: %v {
+Header:
+[
+%v
+]
+Transactions:
+%v
+Uncles:
+%v
}
-
-func NewUncleBlockFromValue(header *ethutil.Value) *Block {
- block := &Block{}
- block.setHeader(header)
-
- return block
+`, self.header.Hash(), self.Size(), self.header, self.transactions, self.uncles)
}
-func (block *Block) Trie() *trie.Trie {
- return block.state.Trie
-}
-
-func (block *Block) Root() interface{} {
- return block.state.Root()
+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)
}
-func (block *Block) Diff() *big.Int {
- return block.Difficulty
-}
+type Blocks []*Block
-func (self *Block) Receipts() []*Receipt {
- return self.receipts
-}
+type BlockBy func(b1, b2 *Block) bool
-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 (self BlockBy) Sort(blocks Blocks) {
+ bs := blockSorter{
+ blocks: blocks,
+ by: self,
}
+ sort.Sort(bs)
}
-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()))
+type blockSorter struct {
+ blocks Blocks
+ by func(b1, b2 *Block) bool
}
-// Implement RlpEncodable
-func (self *Block) RlpData() interface{} {
- return []interface{}{self.header(), self.transactions, self.rlpUncles()}
+func (self blockSorter) Len() int { return len(self.blocks) }
+func (self blockSorter) Swap(i, j int) {
+ self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
}
+func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
-// Implement pow.Block
-func (self *Block) N() []byte { return self.Nonce }
+func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 }
diff --git a/core/types/block_test.go b/core/types/block_test.go
new file mode 100644
index 000000000..c85708975
--- /dev/null
+++ b/core/types/block_test.go
@@ -0,0 +1,23 @@
+package types
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func init() {
+ ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
+ ethutil.Config.Db, _ = ethdb.NewMemDatabase()
+}
+
+func TestNewBlock(t *testing.T) {
+ block := GenesisBlock()
+ data := ethutil.Encode(block)
+
+ var genesis Block
+ err := rlp.Decode(bytes.NewReader(data), &genesis)
+}
diff --git a/core/types/common.go b/core/types/common.go
index 89cb5f498..ba88b77e1 100644
--- a/core/types/common.go
+++ b/core/types/common.go
@@ -4,13 +4,8 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/wire"
)
type BlockProcessor interface {
Process(*Block) (*big.Int, state.Messages, error)
}
-
-type Broadcaster interface {
- Broadcast(wire.MsgType, []interface{})
-}
diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go
index 1897ff198..0beb19670 100644
--- a/core/types/derive_sha.go
+++ b/core/types/derive_sha.go
@@ -2,7 +2,7 @@ package types
import (
"github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/ptrie"
)
type DerivableList interface {
@@ -11,10 +11,10 @@ type DerivableList interface {
}
func DeriveSha(list DerivableList) []byte {
- trie := trie.New(ethutil.Config.Db, "")
+ trie := ptrie.New(nil, ethutil.Config.Db)
for i := 0; i < list.Len(); i++ {
- 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()
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index f6ad0774b..59244adc3 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -1,11 +1,13 @@
package types
import (
+ "bytes"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/obscuren/secp256k1-go"
)
@@ -14,22 +16,22 @@ func IsContractAddr(addr []byte) bool {
}
type Transaction struct {
- nonce uint64
- recipient []byte
- value *big.Int
- gas *big.Int
- gasPrice *big.Int
- data []byte
- v byte
- r, s []byte
+ AccountNonce uint64
+ Price *big.Int
+ GasLimit *big.Int
+ Recipient []byte
+ Amount *big.Int
+ Payload []byte
+ V uint64
+ R, S []byte
}
-func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
- return &Transaction{recipient: nil, value: value, gas: gas, gasPrice: gasPrice, data: script}
+func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction {
+ return NewTransactionMessage(nil, Amount, gasAmount, price, data)
}
-func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
- return &Transaction{recipient: to, value: value, gasPrice: gasPrice, gas: gas, data: data}
+func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction {
+ return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
}
func NewTransactionFromBytes(data []byte) *Transaction {
@@ -39,7 +41,7 @@ func NewTransactionFromBytes(data []byte) *Transaction {
return tx
}
-func NewTransactionFromValue(val *ethutil.Value) *Transaction {
+func NewTransactionFromAmount(val *ethutil.Value) *Transaction {
tx := &Transaction{}
tx.RlpValueDecode(val)
@@ -47,47 +49,47 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
}
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 {
- return self.data
+ return self.Payload
}
func (self *Transaction) Gas() *big.Int {
- return self.gas
+ return self.GasLimit
}
func (self *Transaction) GasPrice() *big.Int {
- return self.gasPrice
+ return self.Price
}
func (self *Transaction) Value() *big.Int {
- return self.value
+ return self.Amount
}
func (self *Transaction) Nonce() uint64 {
- return self.nonce
+ return self.AccountNonce
}
-func (self *Transaction) SetNonce(nonce uint64) {
- self.nonce = nonce
+func (self *Transaction) SetNonce(AccountNonce uint64) {
+ self.AccountNonce = AccountNonce
}
func (self *Transaction) From() []byte {
- return self.Sender()
+ return self.sender()
}
func (self *Transaction) To() []byte {
- return self.recipient
+ return self.Recipient
}
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
- v = tx.v
- r = ethutil.LeftPadBytes(tx.r, 32)
- s = ethutil.LeftPadBytes(tx.s, 32)
+ v = byte(tx.V)
+ r = ethutil.LeftPadBytes(tx.R, 32)
+ s = ethutil.LeftPadBytes(tx.S, 32)
return
}
@@ -114,12 +116,12 @@ func (tx *Transaction) PublicKey() []byte {
return pubkey
}
-func (tx *Transaction) Sender() []byte {
+func (tx *Transaction) sender() []byte {
pubkey := tx.PublicKey()
// Validate the returned key.
// Return nil if public key isn't in full format
- if len(pubkey) != 0 && pubkey[0] != 4 {
+ if len(pubkey) == 0 || pubkey[0] != 4 {
return nil
}
@@ -130,42 +132,37 @@ func (tx *Transaction) Sign(privk []byte) error {
sig := tx.Signature(privk)
- tx.r = sig[:32]
- tx.s = sig[32:64]
- tx.v = sig[64] + 27
+ tx.R = sig[:32]
+ tx.S = sig[32:64]
+ tx.V = uint64(sig[64] + 27)
return nil
}
func (tx *Transaction) RlpData() interface{} {
- data := []interface{}{tx.nonce, tx.gasPrice, tx.gas, tx.recipient, tx.value, tx.data}
+ data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
- return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
-}
-
-func (tx *Transaction) RlpValue() *ethutil.Value {
- return ethutil.NewValue(tx.RlpData())
+ return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
}
func (tx *Transaction) RlpEncode() []byte {
- return tx.RlpValue().Encode()
+ return ethutil.Encode(tx)
}
func (tx *Transaction) RlpDecode(data []byte) {
- tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
+ rlp.Decode(bytes.NewReader(data), tx)
}
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
- tx.nonce = decoder.Get(0).Uint()
- tx.gasPrice = decoder.Get(1).BigInt()
- tx.gas = decoder.Get(2).BigInt()
- tx.recipient = decoder.Get(3).Bytes()
- tx.value = decoder.Get(4).BigInt()
- tx.data = decoder.Get(5).Bytes()
- tx.v = byte(decoder.Get(6).Uint())
-
- tx.r = decoder.Get(7).Bytes()
- tx.s = decoder.Get(8).Bytes()
+ tx.AccountNonce = decoder.Get(0).Uint()
+ tx.Price = decoder.Get(1).BigInt()
+ tx.GasLimit = decoder.Get(2).BigInt()
+ tx.Recipient = decoder.Get(3).Bytes()
+ tx.Amount = decoder.Get(4).BigInt()
+ tx.Payload = decoder.Get(5).Bytes()
+ tx.V = decoder.Get(6).Uint()
+ tx.R = decoder.Get(7).Bytes()
+ tx.S = decoder.Get(8).Bytes()
}
func (tx *Transaction) String() string {
@@ -176,26 +173,26 @@ func (tx *Transaction) String() string {
To: %x
Nonce: %v
GasPrice: %v
- Gas: %v
+ GasLimit %v
Value: %v
Data: 0x%x
V: 0x%x
R: 0x%x
S: 0x%x
Hex: %x
- `,
+`,
tx.Hash(),
- len(tx.recipient) == 0,
- tx.Sender(),
- tx.recipient,
- tx.nonce,
- tx.gasPrice,
- tx.gas,
- tx.value,
- tx.data,
- tx.v,
- tx.r,
- tx.s,
+ len(tx.Recipient) == 0,
+ tx.From(),
+ tx.To(),
+ tx.AccountNonce,
+ tx.Price,
+ tx.GasLimit,
+ tx.Amount,
+ tx.Payload,
+ tx.V,
+ tx.R,
+ tx.S,
ethutil.Encode(tx),
)
}
@@ -220,5 +217,5 @@ func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) }
type TxByNonce struct{ Transactions }
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
}
diff --git a/core/vm_env.go b/core/vm_env.go
index ad63ecf9c..209115eab 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -24,15 +24,15 @@ func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv {
}
func (self *VMEnv) Origin() []byte { return self.msg.From() }
-func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
-func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
-func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
-func (self *VMEnv) Time() int64 { return self.block.Time }
-func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
+func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
+func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
+func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
+func (self *VMEnv) Time() int64 { return self.block.Time() }
+func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
+func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) State() *state.StateDB { return self.state }
-func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) AddLog(log state.Log) {
diff --git a/eth/backend.go b/eth/backend.go
new file mode 100644
index 000000000..383cda46f
--- /dev/null
+++ b/eth/backend.go
@@ -0,0 +1,249 @@
+package eth
+
+import (
+ "net"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/event"
+ ethlogger "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/pow/ezp"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/ethereum/go-ethereum/whisper"
+)
+
+const (
+ seedNodeAddress = "poc-7.ethdev.com:30300"
+)
+
+var logger = ethlogger.NewLogger("SERV")
+
+type Ethereum struct {
+ // Channel for shutting down the ethereum
+ shutdownChan chan bool
+ quit chan bool
+
+ // DB interface
+ db ethutil.Database
+ blacklist p2p.Blacklist
+
+ //*** SERVICES ***
+ // State manager for processing new blocks and managing the over all states
+ blockManager *core.BlockManager
+ txPool *core.TxPool
+ chainManager *core.ChainManager
+ blockPool *BlockPool
+ whisper *whisper.Whisper
+
+ server *p2p.Server
+ eventMux *event.TypeMux
+ txSub event.Subscription
+ blockSub event.Subscription
+
+ RpcServer *rpc.JsonRpcServer
+ keyManager *crypto.KeyManager
+
+ clientIdentity p2p.ClientIdentity
+
+ synclock sync.Mutex
+ syncGroup sync.WaitGroup
+
+ Mining bool
+}
+
+func New(db ethutil.Database, identity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, port string, maxPeers int) (*Ethereum, error) {
+
+ saveProtocolVersion(db)
+ ethutil.Config.Db = db
+
+ eth := &Ethereum{
+ shutdownChan: make(chan bool),
+ quit: make(chan bool),
+ db: db,
+ keyManager: keyManager,
+ clientIdentity: identity,
+ blacklist: p2p.NewBlacklist(),
+ eventMux: &event.TypeMux{},
+ }
+
+ eth.chainManager = core.NewChainManager(eth.EventMux())
+ eth.txPool = core.NewTxPool(eth.chainManager, eth.EventMux())
+ eth.blockManager = core.NewBlockManager(eth.txPool, eth.chainManager, eth.EventMux())
+ eth.chainManager.SetProcessor(eth.blockManager)
+ eth.whisper = whisper.New()
+
+ hasBlock := eth.chainManager.HasBlock
+ insertChain := eth.chainManager.InsertChain
+ eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
+
+ // Start services
+ eth.txPool.Start()
+
+ ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
+ protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
+
+ server := &p2p.Server{
+ Identity: identity,
+ MaxPeers: maxPeers,
+ Protocols: protocols,
+ ListenAddr: ":" + port,
+ Blacklist: eth.blacklist,
+ NAT: nat,
+ }
+
+ eth.server = server
+
+ return eth, nil
+}
+
+func (s *Ethereum) KeyManager() *crypto.KeyManager {
+ return s.keyManager
+}
+
+func (s *Ethereum) ClientIdentity() p2p.ClientIdentity {
+ return s.clientIdentity
+}
+
+func (s *Ethereum) ChainManager() *core.ChainManager {
+ return s.chainManager
+}
+
+func (s *Ethereum) BlockManager() *core.BlockManager {
+ return s.blockManager
+}
+
+func (s *Ethereum) TxPool() *core.TxPool {
+ return s.txPool
+}
+
+func (s *Ethereum) BlockPool() *BlockPool {
+ return s.blockPool
+}
+
+func (s *Ethereum) Whisper() *whisper.Whisper {
+ return s.whisper
+}
+
+func (s *Ethereum) EventMux() *event.TypeMux {
+ return s.eventMux
+}
+func (self *Ethereum) Db() ethutil.Database {
+ return self.db
+}
+
+func (s *Ethereum) IsMining() bool {
+ return s.Mining
+}
+
+func (s *Ethereum) IsListening() bool {
+ // XXX TODO
+ return false
+}
+
+func (s *Ethereum) PeerCount() int {
+ return s.server.PeerCount()
+}
+
+func (s *Ethereum) Peers() []*p2p.Peer {
+ return s.server.Peers()
+}
+
+func (s *Ethereum) MaxPeers() int {
+ return s.server.MaxPeers
+}
+
+// Start the ethereum
+func (s *Ethereum) Start(seed bool) error {
+ err := s.server.Start()
+ if err != nil {
+ return err
+ }
+ s.blockPool.Start()
+ s.whisper.Start()
+
+ // broadcast transactions
+ s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
+ go s.txBroadcastLoop()
+
+ // broadcast mined blocks
+ s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{})
+ go s.blockBroadcastLoop()
+
+ // TODO: read peers here
+ if seed {
+ logger.Infof("Connect to seed node %v", seedNodeAddress)
+ if err := s.SuggestPeer(seedNodeAddress); err != nil {
+ return err
+ }
+ }
+
+ logger.Infoln("Server started")
+ return nil
+}
+
+func (self *Ethereum) SuggestPeer(addr string) error {
+ netaddr, err := net.ResolveTCPAddr("tcp", addr)
+ if err != nil {
+ logger.Errorf("couldn't resolve %s:", addr, err)
+ return err
+ }
+
+ self.server.SuggestPeer(netaddr.IP, netaddr.Port, nil)
+ return nil
+}
+
+func (s *Ethereum) Stop() {
+ // Close the database
+ defer s.db.Close()
+
+ close(s.quit)
+
+ s.txSub.Unsubscribe() // quits txBroadcastLoop
+ s.blockSub.Unsubscribe() // quits blockBroadcastLoop
+
+ if s.RpcServer != nil {
+ s.RpcServer.Stop()
+ }
+ s.txPool.Stop()
+ s.eventMux.Stop()
+ s.blockPool.Stop()
+ s.whisper.Stop()
+
+ logger.Infoln("Server stopped")
+ close(s.shutdownChan)
+}
+
+// This function will wait for a shutdown and resumes main thread execution
+func (s *Ethereum) WaitForShutdown() {
+ <-s.shutdownChan
+}
+
+// now tx broadcasting is taken out of txPool
+// handled here via subscription, efficiency?
+func (self *Ethereum) txBroadcastLoop() {
+ // automatically stops if unsubscribe
+ for obj := range self.txSub.Chan() {
+ event := obj.(core.TxPreEvent)
+ self.server.Broadcast("eth", TxMsg, []interface{}{event.Tx.RlpData()})
+ }
+}
+
+func (self *Ethereum) blockBroadcastLoop() {
+ // automatically stops if unsubscribe
+ for obj := range self.txSub.Chan() {
+ event := obj.(core.NewMinedBlockEvent)
+ self.server.Broadcast("eth", NewBlockMsg, event.Block.RlpData())
+ }
+}
+
+func saveProtocolVersion(db ethutil.Database) {
+ d, _ := db.Get([]byte("ProtocolVersion"))
+ protocolVersion := ethutil.NewValue(d).Uint()
+
+ if protocolVersion == 0 {
+ db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
+ }
+}
diff --git a/eth/block_pool.go b/eth/block_pool.go
new file mode 100644
index 000000000..7cfbc63f8
--- /dev/null
+++ b/eth/block_pool.go
@@ -0,0 +1,1015 @@
+package eth
+
+import (
+ "math"
+ "math/big"
+ "math/rand"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethutil"
+ ethlogger "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/pow"
+)
+
+var poolLogger = ethlogger.NewLogger("Blockpool")
+
+const (
+ blockHashesBatchSize = 256
+ blockBatchSize = 64
+ blocksRequestInterval = 10 // seconds
+ blocksRequestRepetition = 1
+ blockHashesRequestInterval = 10 // seconds
+ blocksRequestMaxIdleRounds = 10
+ cacheTimeout = 3 // minutes
+ blockTimeout = 5 // minutes
+)
+
+type poolNode struct {
+ lock sync.RWMutex
+ hash []byte
+ block *types.Block
+ child *poolNode
+ parent *poolNode
+ section *section
+ knownParent bool
+ peer string
+ source string
+ complete bool
+}
+
+type BlockPool struct {
+ lock sync.RWMutex
+ pool map[string]*poolNode
+
+ peersLock sync.RWMutex
+ peers map[string]*peerInfo
+ peer *peerInfo
+
+ quit chan bool
+ wg sync.WaitGroup
+ running bool
+
+ // the minimal interface with blockchain
+ hasBlock func(hash []byte) bool
+ insertChain func(types.Blocks) error
+ verifyPoW func(pow.Block) bool
+}
+
+type peerInfo struct {
+ lock sync.RWMutex
+
+ td *big.Int
+ currentBlock []byte
+ id string
+
+ requestBlockHashes func([]byte) error
+ requestBlocks func([][]byte) error
+ peerError func(int, string, ...interface{})
+
+ sections map[string]*section
+ roots []*poolNode
+ quitC chan bool
+}
+
+func NewBlockPool(hasBlock func(hash []byte) bool, insertChain func(types.Blocks) error, verifyPoW func(pow.Block) bool,
+) *BlockPool {
+ return &BlockPool{
+ hasBlock: hasBlock,
+ insertChain: insertChain,
+ verifyPoW: verifyPoW,
+ }
+}
+
+// allows restart
+func (self *BlockPool) Start() {
+ self.lock.Lock()
+ if self.running {
+ self.lock.Unlock()
+ return
+ }
+ self.running = true
+ self.quit = make(chan bool)
+ self.pool = make(map[string]*poolNode)
+ self.lock.Unlock()
+
+ self.peersLock.Lock()
+ self.peers = make(map[string]*peerInfo)
+ self.peersLock.Unlock()
+
+ poolLogger.Infoln("Started")
+
+}
+
+func (self *BlockPool) Stop() {
+ self.lock.Lock()
+ if !self.running {
+ self.lock.Unlock()
+ return
+ }
+ self.running = false
+ self.lock.Unlock()
+
+ poolLogger.Infoln("Stopping")
+
+ close(self.quit)
+ self.lock.Lock()
+ self.peersLock.Lock()
+ self.peers = nil
+ self.pool = nil
+ self.peer = nil
+ self.wg.Wait()
+ self.lock.Unlock()
+ self.peersLock.Unlock()
+ poolLogger.Infoln("Stopped")
+
+}
+
+// AddPeer is called by the eth protocol instance running on the peer after
+// the status message has been received with total difficulty and current block hash
+// AddPeer can only be used once, RemovePeer needs to be called when the peer disconnects
+func (self *BlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) bool {
+ self.peersLock.Lock()
+ defer self.peersLock.Unlock()
+ if self.peers[peerId] != nil {
+ panic("peer already added")
+ }
+ peer := &peerInfo{
+ td: td,
+ currentBlock: currentBlock,
+ id: peerId, //peer.Identity().Pubkey()
+ requestBlockHashes: requestBlockHashes,
+ requestBlocks: requestBlocks,
+ peerError: peerError,
+ }
+ self.peers[peerId] = peer
+ poolLogger.Debugf("add new peer %v with td %v", peerId, td)
+ currentTD := ethutil.Big0
+ if self.peer != nil {
+ currentTD = self.peer.td
+ }
+ if td.Cmp(currentTD) > 0 {
+ self.peer.stop(peer)
+ peer.start(self.peer)
+ poolLogger.Debugf("peer %v promoted to best peer", peerId)
+ self.peer = peer
+ return true
+ }
+ return false
+}
+
+// RemovePeer is called by the eth protocol when the peer disconnects
+func (self *BlockPool) RemovePeer(peerId string) {
+ self.peersLock.Lock()
+ defer self.peersLock.Unlock()
+ peer := self.peers[peerId]
+ if peer == nil {
+ return
+ }
+ self.peers[peerId] = nil
+ poolLogger.Debugf("remove peer %v", peerId[0:4])
+
+ // if current best peer is removed, need find a better one
+ if self.peer != nil && peerId == self.peer.id {
+ var newPeer *peerInfo
+ max := ethutil.Big0
+ // peer with the highest self-acclaimed TD is chosen
+ for _, info := range self.peers {
+ if info.td.Cmp(max) > 0 {
+ max = info.td
+ newPeer = info
+ }
+ }
+ self.peer.stop(peer)
+ peer.start(self.peer)
+ if newPeer != nil {
+ poolLogger.Debugf("peer %v with td %v promoted to best peer", newPeer.id[0:4], newPeer.td)
+ } else {
+ poolLogger.Warnln("no peers left")
+ }
+ }
+}
+
+// Entry point for eth protocol to add block hashes received via BlockHashesMsg
+// only hashes from the best peer is handled
+// this method is always responsible to initiate further hash requests until
+// a known parent is reached unless cancelled by a peerChange event
+// this process also launches all request processes on each chain section
+// this function needs to run asynchronously for one peer since the message is discarded???
+func (self *BlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string) {
+
+ // check if this peer is the best
+ peer, best := self.getPeer(peerId)
+ if !best {
+ return
+ }
+ // peer is still the best
+
+ var child *poolNode
+ var depth int
+
+ // iterate using next (rlp stream lazy decoder) feeding hashesC
+ self.wg.Add(1)
+ go func() {
+ for {
+ select {
+ case <-self.quit:
+ return
+ case <-peer.quitC:
+ // if the peer is demoted, no more hashes taken
+ break
+ default:
+ hash, ok := next()
+ if !ok {
+ // message consumed chain skeleton built
+ break
+ }
+ // check if known block connecting the downloaded chain to our blockchain
+ if self.hasBlock(hash) {
+ poolLogger.Infof("known block (%x...)\n", hash[0:4])
+ if child != nil {
+ child.Lock()
+ // mark child as absolute pool root with parent known to blockchain
+ child.knownParent = true
+ child.Unlock()
+ }
+ break
+ }
+ //
+ var parent *poolNode
+ // look up node in pool
+ parent = self.get(hash)
+ if parent != nil {
+ // reached a known chain in the pool
+ // request blocks on the newly added part of the chain
+ if child != nil {
+ self.link(parent, child)
+
+ // activate the current chain
+ self.activateChain(parent, peer, true)
+ poolLogger.Debugf("potential chain of %v blocks added, reached blockpool, activate chain", depth)
+ break
+ }
+ // if this is the first hash, we expect to find it
+ parent.RLock()
+ grandParent := parent.parent
+ parent.RUnlock()
+ if grandParent != nil {
+ // activate the current chain
+ self.activateChain(parent, peer, true)
+ poolLogger.Debugf("block hash found, activate chain")
+ break
+ }
+ // the first node is the root of a chain in the pool, rejoice and continue
+ }
+ // if node does not exist, create it and index in the pool
+ section := &section{}
+ if child == nil {
+ section.top = parent
+ }
+ parent = &poolNode{
+ hash: hash,
+ child: child,
+ section: section,
+ peer: peerId,
+ }
+ self.set(hash, parent)
+ poolLogger.Debugf("create potential block for %x...", hash[0:4])
+
+ depth++
+ child = parent
+ }
+ }
+ if child != nil {
+ poolLogger.Debugf("chain of %v hashes added", depth)
+ // start a processSection on the last node, but switch off asking
+ // hashes and blocks until next peer confirms this chain
+ section := self.processSection(child)
+ peer.addSection(child.hash, section)
+ section.start()
+ }
+ }()
+}
+
+// AddBlock is the entry point for the eth protocol when blockmsg is received upon requests
+// It has a strict interpretation of the protocol in that if the block received has not been requested, it results in an error (which can be ignored)
+// block is checked for PoW
+// only the first PoW-valid block for a hash is considered legit
+func (self *BlockPool) AddBlock(block *types.Block, peerId string) {
+ hash := block.Hash()
+ node := self.get(hash)
+ node.RLock()
+ b := node.block
+ node.RUnlock()
+ if b != nil {
+ return
+ }
+ if node == nil && !self.hasBlock(hash) {
+ self.peerError(peerId, ErrUnrequestedBlock, "%x", hash)
+ return
+ }
+ // validate block for PoW
+ if !self.verifyPoW(block) {
+ self.peerError(peerId, ErrInvalidPoW, "%x", hash)
+ }
+ node.Lock()
+ node.block = block
+ node.source = peerId
+ node.Unlock()
+}
+
+// iterates down a known poolchain and activates fetching processes
+// on each chain section for the peer
+// stops if the peer is demoted
+// registers last section root as root for the peer (in case peer is promoted a second time, to remember)
+func (self *BlockPool) activateChain(node *poolNode, peer *peerInfo, on bool) {
+ self.wg.Add(1)
+ go func() {
+ for {
+ node.sectionRLock()
+ bottom := node.section.bottom
+ if bottom == nil { // the chain section is being created or killed
+ break
+ }
+ // register this section with the peer
+ if peer != nil {
+ peer.addSection(bottom.hash, bottom.section)
+ if on {
+ bottom.section.start()
+ } else {
+ bottom.section.start()
+ }
+ }
+ if bottom.parent == nil {
+ node = bottom
+ break
+ }
+ // if peer demoted stop activation
+ select {
+ case <-peer.quitC:
+ break
+ default:
+ }
+
+ node = bottom.parent
+ bottom.sectionRUnlock()
+ }
+ // remember root for this peer
+ peer.addRoot(node)
+ self.wg.Done()
+ }()
+}
+
+// main worker thread on each section in the poolchain
+// - kills the section if there are blocks missing after an absolute time
+// - kills the section if there are maxIdleRounds of idle rounds of block requests with no response
+// - periodically polls the chain section for missing blocks which are then requested from peers
+// - registers the process controller on the peer so that if the peer is promoted as best peer the second time (after a disconnect of a better one), all active processes are switched back on unless they expire and killed ()
+// - when turned off (if peer disconnects and new peer connects with alternative chain), no blockrequests are made but absolute expiry timer is ticking
+// - when turned back on it recursively calls itself on the root of the next chain section
+// - when exits, signals to
+func (self *BlockPool) processSection(node *poolNode) *section {
+ // absolute time after which sub-chain is killed if not complete (some blocks are missing)
+ suicideTimer := time.After(blockTimeout * time.Minute)
+ var blocksRequestTimer, blockHashesRequestTimer <-chan time.Time
+ var nodeC, missingC, processC chan *poolNode
+ controlC := make(chan bool)
+ resetC := make(chan bool)
+ var hashes [][]byte
+ var i, total, missing, lastMissing, depth int
+ var blockHashesRequests, blocksRequests int
+ var idle int
+ var init, alarm, done, same, running, once bool
+ orignode := node
+ hash := node.hash
+
+ node.sectionLock()
+ defer node.sectionUnlock()
+ section := &section{controlC: controlC, resetC: resetC}
+ node.section = section
+
+ go func() {
+ self.wg.Add(1)
+ for {
+ node.sectionRLock()
+ controlC = node.section.controlC
+ node.sectionRUnlock()
+
+ if init {
+ // missing blocks read from nodeC
+ // initialized section
+ if depth == 0 {
+ break
+ }
+ // enable select case to read missing block when ready
+ processC = missingC
+ missingC = make(chan *poolNode, lastMissing)
+ nodeC = nil
+ // only do once
+ init = false
+ } else {
+ if !once {
+ missingC = nil
+ processC = nil
+ i = 0
+ total = 0
+ lastMissing = 0
+ }
+ }
+
+ // went through all blocks in section
+ if i != 0 && i == lastMissing {
+ if len(hashes) > 0 {
+ // send block requests to peers
+ self.requestBlocks(blocksRequests, hashes)
+ }
+ blocksRequests++
+ poolLogger.Debugf("[%x] block request attempt %v: missing %v/%v/%v", hash[0:4], blocksRequests, missing, total, depth)
+ if missing == lastMissing {
+ // idle round
+ if same {
+ // more than once
+ idle++
+ // too many idle rounds
+ if idle > blocksRequestMaxIdleRounds {
+ poolLogger.Debugf("[%x] block requests had %v idle rounds (%v total attempts): missing %v/%v/%v\ngiving up...", hash[0:4], idle, blocksRequests, missing, total, depth)
+ self.killChain(node, nil)
+ break
+ }
+ } else {
+ idle = 0
+ }
+ same = true
+ } else {
+ if missing == 0 {
+ // no missing nodes
+ poolLogger.Debugf("block request process complete on section %x... (%v total blocksRequests): missing %v/%v/%v", hash[0:4], blockHashesRequests, blocksRequests, missing, total, depth)
+ node.Lock()
+ orignode.complete = true
+ node.Unlock()
+ blocksRequestTimer = nil
+ if blockHashesRequestTimer == nil {
+ // not waiting for hashes any more
+ poolLogger.Debugf("hash request on root %x... successful (%v total attempts)\nquitting...", hash[0:4], blockHashesRequests)
+ break
+ } // otherwise suicide if no hashes coming
+ }
+ same = false
+ }
+ lastMissing = missing
+ i = 0
+ missing = 0
+ // ready for next round
+ done = true
+ }
+ if done && alarm {
+ poolLogger.Debugf("start checking if new blocks arrived (attempt %v): missing %v/%v/%v", blocksRequests, missing, total, depth)
+ blocksRequestTimer = time.After(blocksRequestInterval * time.Second)
+ alarm = false
+ done = false
+ // processC supposed to be empty and never closed so just swap, no need to allocate
+ tempC := processC
+ processC = missingC
+ missingC = tempC
+ }
+ select {
+ case <-self.quit:
+ break
+ case <-suicideTimer:
+ self.killChain(node, nil)
+ poolLogger.Warnf("[%x] timeout. (%v total attempts): missing %v/%v/%v", hash[0:4], blocksRequests, missing, total, depth)
+ break
+ case <-blocksRequestTimer:
+ alarm = true
+ case <-blockHashesRequestTimer:
+ orignode.RLock()
+ parent := orignode.parent
+ orignode.RUnlock()
+ if parent != nil {
+ // if not root of chain, switch off
+ poolLogger.Debugf("[%x] parent found, hash requests deactivated (after %v total attempts)\n", hash[0:4], blockHashesRequests)
+ blockHashesRequestTimer = nil
+ } else {
+ blockHashesRequests++
+ poolLogger.Debugf("[%x] hash request on root (%v total attempts)\n", hash[0:4], blockHashesRequests)
+ self.requestBlockHashes(parent.hash)
+ blockHashesRequestTimer = time.After(blockHashesRequestInterval * time.Second)
+ }
+ case r, ok := <-controlC:
+ if !ok {
+ break
+ }
+ if running && !r {
+ poolLogger.Debugf("process on section %x... (%v total attempts): missing %v/%v/%v", hash[0:4], blocksRequests, missing, total, depth)
+
+ alarm = false
+ blocksRequestTimer = nil
+ blockHashesRequestTimer = nil
+ processC = nil
+ }
+ if !running && r {
+ poolLogger.Debugf("[%x] on", hash[0:4])
+
+ orignode.RLock()
+ parent := orignode.parent
+ complete := orignode.complete
+ knownParent := orignode.knownParent
+ orignode.RUnlock()
+ if !complete {
+ poolLogger.Debugf("[%x] activate block requests", hash[0:4])
+ blocksRequestTimer = time.After(0)
+ }
+ if parent == nil && !knownParent {
+ // if no parent but not connected to blockchain
+ poolLogger.Debugf("[%x] activate block hashes requests", hash[0:4])
+ blockHashesRequestTimer = time.After(0)
+ } else {
+ blockHashesRequestTimer = nil
+ }
+ alarm = true
+ processC = missingC
+ if !once {
+ // if not run at least once fully, launch iterator
+ processC = make(chan *poolNode)
+ missingC = make(chan *poolNode)
+ self.foldUp(orignode, processC)
+ once = true
+ }
+ }
+ total = lastMissing
+ case <-resetC:
+ once = false
+ init = false
+ done = false
+ case node, ok := <-processC:
+ if !ok {
+ // channel closed, first iteration finished
+ init = true
+ once = true
+ continue
+ }
+ i++
+ // if node has no block
+ node.RLock()
+ block := node.block
+ nhash := node.hash
+ knownParent := node.knownParent
+ node.RUnlock()
+ if !init {
+ depth++
+ }
+ if block == nil {
+ missing++
+ if !init {
+ total++
+ }
+ hashes = append(hashes, nhash)
+ if len(hashes) == blockBatchSize {
+ self.requestBlocks(blocksRequests, hashes)
+ hashes = nil
+ }
+ missingC <- node
+ } else {
+ // block is found
+ if knownParent {
+ // connected to the blockchain, insert the longest chain of blocks
+ var blocks types.Blocks
+ child := node
+ parent := node
+ node.sectionRLock()
+ for child != nil && child.block != nil {
+ parent = child
+ blocks = append(blocks, parent.block)
+ child = parent.child
+ }
+ node.sectionRUnlock()
+ poolLogger.Debugf("[%x] insert %v blocks into blockchain", hash[0:4], len(blocks))
+ if err := self.insertChain(blocks); err != nil {
+ // TODO: not clear which peer we need to address
+ // peerError should dispatch to peer if still connected and disconnect
+ self.peerError(node.source, ErrInvalidBlock, "%v", err)
+ poolLogger.Debugf("invalid block %v", node.hash)
+ poolLogger.Debugf("penalise peers %v (hash), %v (block)", node.peer, node.source)
+ // penalise peer in node.source
+ self.killChain(node, nil)
+ // self.disconnect()
+ break
+ }
+ // if suceeded mark the next one (no block yet) as connected to blockchain
+ if child != nil {
+ child.Lock()
+ child.knownParent = true
+ child.Unlock()
+ }
+ // reset starting node to first node with missing block
+ orignode = child
+ // pop the inserted ancestors off the channel
+ for i := 1; i < len(blocks); i++ {
+ <-processC
+ }
+ // delink inserted chain section
+ self.killChain(node, parent)
+ }
+ }
+ }
+ }
+ poolLogger.Debugf("[%x] quit after\n%v block hashes requests\n%v block requests: missing %v/%v/%v", hash[0:4], blockHashesRequests, blocksRequests, missing, total, depth)
+
+ self.wg.Done()
+ node.sectionLock()
+ node.section.controlC = nil
+ node.sectionUnlock()
+ // this signals that controller not available
+ }()
+ return section
+
+}
+
+func (self *BlockPool) peerError(peerId string, code int, format string, params ...interface{}) {
+ self.peersLock.RLock()
+ defer self.peersLock.RUnlock()
+ peer, ok := self.peers[peerId]
+ if ok {
+ peer.peerError(code, format, params...)
+ }
+}
+
+func (self *BlockPool) requestBlockHashes(hash []byte) {
+ self.peersLock.Lock()
+ defer self.peersLock.Unlock()
+ if self.peer != nil {
+ self.peer.requestBlockHashes(hash)
+ }
+}
+
+func (self *BlockPool) requestBlocks(attempts int, hashes [][]byte) {
+ // distribute block request among known peers
+ self.peersLock.Lock()
+ defer self.peersLock.Unlock()
+ peerCount := len(self.peers)
+ // on first attempt use the best peer
+ if attempts == 0 {
+ self.peer.requestBlocks(hashes)
+ return
+ }
+ repetitions := int(math.Min(float64(peerCount), float64(blocksRequestRepetition)))
+ poolLogger.Debugf("request %v missing blocks from %v/%v peers", len(hashes), repetitions, peerCount)
+ i := 0
+ indexes := rand.Perm(peerCount)[0:(repetitions - 1)]
+ sort.Ints(indexes)
+ for _, peer := range self.peers {
+ if i == indexes[0] {
+ peer.requestBlocks(hashes)
+ indexes = indexes[1:]
+ if len(indexes) == 0 {
+ break
+ }
+ }
+ i++
+ }
+}
+
+func (self *BlockPool) getPeer(peerId string) (*peerInfo, bool) {
+ self.peersLock.RLock()
+ defer self.peersLock.RUnlock()
+ if self.peer != nil && self.peer.id == peerId {
+ return self.peer, true
+ }
+ info, ok := self.peers[peerId]
+ if !ok {
+ panic("unknown peer")
+ }
+ return info, false
+}
+
+func (self *peerInfo) addSection(hash []byte, section *section) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.sections[string(hash)] = section
+}
+
+func (self *peerInfo) addRoot(node *poolNode) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.roots = append(self.roots, node)
+}
+
+// (re)starts processes registered for this peer (self)
+func (self *peerInfo) start(peer *peerInfo) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.quitC = make(chan bool)
+ for _, root := range self.roots {
+ root.sectionRLock()
+ if root.section.bottom != nil {
+ if root.parent == nil {
+ self.requestBlockHashes(root.hash)
+ }
+ }
+ root.sectionRUnlock()
+ }
+ self.roots = nil
+ self.controlSections(peer, true)
+}
+
+// (re)starts process without requests, only suicide timer
+func (self *peerInfo) stop(peer *peerInfo) {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ close(self.quitC)
+ self.controlSections(peer, false)
+}
+
+func (self *peerInfo) controlSections(peer *peerInfo, on bool) {
+ if peer != nil {
+ peer.lock.RLock()
+ defer peer.lock.RUnlock()
+ }
+ for hash, section := range peer.sections {
+ if section.done() {
+ delete(self.sections, hash)
+ }
+ _, exists := peer.sections[hash]
+ if on || peer == nil || exists {
+ if on {
+ // self is best peer
+ section.start()
+ } else {
+ // (re)starts process without requests, only suicide timer
+ section.stop()
+ }
+ }
+ }
+}
+
+// called when parent is found in pool
+// parent and child are guaranteed to be on different sections
+func (self *BlockPool) link(parent, child *poolNode) {
+ var top bool
+ parent.sectionLock()
+ if child != nil {
+ child.sectionLock()
+ }
+ if parent == parent.section.top && parent.section.top != nil {
+ top = true
+ }
+ var bottom bool
+
+ if child == child.section.bottom {
+ bottom = true
+ }
+ if parent.child != child {
+ orphan := parent.child
+ if orphan != nil {
+ // got a fork in the chain
+ if top {
+ orphan.lock.Lock()
+ // make old child orphan
+ orphan.parent = nil
+ orphan.lock.Unlock()
+ } else { // we are under section lock
+ // make old child orphan
+ orphan.parent = nil
+ // reset section objects above the fork
+ nchild := orphan.child
+ node := orphan
+ section := &section{bottom: orphan}
+ for node.section == nchild.section {
+ node = nchild
+ node.section = section
+ nchild = node.child
+ }
+ section.top = node
+ // set up a suicide
+ self.processSection(orphan).stop()
+ }
+ } else {
+ // child is on top of a chain need to close section
+ child.section.bottom = child
+ }
+ // adopt new child
+ parent.child = child
+ if !top {
+ parent.section.top = parent
+ // restart section process so that shorter section is scanned for blocks
+ parent.section.reset()
+ }
+ }
+
+ if child != nil {
+ if child.parent != parent {
+ stepParent := child.parent
+ if stepParent != nil {
+ if bottom {
+ stepParent.Lock()
+ stepParent.child = nil
+ stepParent.Unlock()
+ } else {
+ // we are on the same section
+ // if it is a aberrant reverse fork,
+ stepParent.child = nil
+ node := stepParent
+ nparent := stepParent.child
+ section := &section{top: stepParent}
+ for node.section == nparent.section {
+ node = nparent
+ node.section = section
+ node = node.parent
+ }
+ }
+ } else {
+ // linking to a root node, ie. parent is under the root of a chain
+ parent.section.top = parent
+ }
+ }
+ child.parent = parent
+ child.section.bottom = child
+ }
+ // this needed if someone lied about the parent before
+ child.knownParent = false
+
+ parent.sectionUnlock()
+ if child != nil {
+ child.sectionUnlock()
+ }
+}
+
+// this immediately kills the chain from node to end (inclusive) section by section
+func (self *BlockPool) killChain(node *poolNode, end *poolNode) {
+ poolLogger.Debugf("kill chain section with root node %v", node)
+
+ node.sectionLock()
+ node.section.abort()
+ self.set(node.hash, nil)
+ child := node.child
+ top := node.section.top
+ i := 1
+ self.wg.Add(1)
+ go func() {
+ var quit bool
+ for node != top && node != end && child != nil {
+ node = child
+ select {
+ case <-self.quit:
+ quit = true
+ break
+ default:
+ }
+ self.set(node.hash, nil)
+ child = node.child
+ }
+ poolLogger.Debugf("killed chain section of %v blocks with root node %v", i, node)
+ if !quit {
+ if node == top {
+ if node != end && child != nil && end != nil {
+ //
+ self.killChain(child, end)
+ }
+ } else {
+ if child != nil {
+ // delink rest of this section if ended midsection
+ child.section.bottom = child
+ child.parent = nil
+ }
+ }
+ }
+ node.section.bottom = nil
+ node.sectionUnlock()
+ self.wg.Done()
+ }()
+}
+
+// structure to store long range links on chain to skip along
+type section struct {
+ lock sync.RWMutex
+ bottom *poolNode
+ top *poolNode
+ controlC chan bool
+ resetC chan bool
+}
+
+func (self *section) start() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ if self.controlC != nil {
+ self.controlC <- true
+ }
+}
+
+func (self *section) stop() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ if self.controlC != nil {
+ self.controlC <- false
+ }
+}
+
+func (self *section) reset() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ if self.controlC != nil {
+ self.resetC <- true
+ self.controlC <- false
+ }
+}
+
+func (self *section) abort() {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ if self.controlC != nil {
+ close(self.controlC)
+ self.controlC = nil
+ }
+}
+
+func (self *section) done() bool {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ if self.controlC != nil {
+ return true
+ }
+ return false
+}
+
+func (self *BlockPool) get(hash []byte) (node *poolNode) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ return self.pool[string(hash)]
+}
+
+func (self *BlockPool) set(hash []byte, node *poolNode) {
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.pool[string(hash)] = node
+}
+
+// first time for block request, this iteration retrieves nodes of the chain
+// from node up to top (all the way if nil) via child links
+// copies the controller
+// and feeds nodeC channel
+// this is performed under section readlock to prevent top from going away
+// when
+func (self *BlockPool) foldUp(node *poolNode, nodeC chan *poolNode) {
+ self.wg.Add(1)
+ go func() {
+ node.sectionRLock()
+ defer node.sectionRUnlock()
+ for node != nil {
+ select {
+ case <-self.quit:
+ break
+ case nodeC <- node:
+ if node == node.section.top {
+ break
+ }
+ node = node.child
+ }
+ }
+ close(nodeC)
+ self.wg.Done()
+ }()
+}
+
+func (self *poolNode) Lock() {
+ self.sectionLock()
+ self.lock.Lock()
+}
+
+func (self *poolNode) Unlock() {
+ self.lock.Unlock()
+ self.sectionUnlock()
+}
+
+func (self *poolNode) RLock() {
+ self.lock.RLock()
+}
+
+func (self *poolNode) RUnlock() {
+ self.lock.RUnlock()
+}
+
+func (self *poolNode) sectionLock() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ self.section.lock.Lock()
+}
+
+func (self *poolNode) sectionUnlock() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ self.section.lock.Unlock()
+}
+
+func (self *poolNode) sectionRLock() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ self.section.lock.RLock()
+}
+
+func (self *poolNode) sectionRUnlock() {
+ self.lock.RLock()
+ defer self.lock.RUnlock()
+ self.section.lock.RUnlock()
+}
diff --git a/eth/block_pool_test.go b/eth/block_pool_test.go
new file mode 100644
index 000000000..315cc748d
--- /dev/null
+++ b/eth/block_pool_test.go
@@ -0,0 +1,198 @@
+package eth
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "sync"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethutil"
+ ethlogger "github.com/ethereum/go-ethereum/logger"
+)
+
+var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
+
+type testChainManager struct {
+ knownBlock func(hash []byte) bool
+ addBlock func(*types.Block) error
+ checkPoW func(*types.Block) bool
+}
+
+func (self *testChainManager) KnownBlock(hash []byte) bool {
+ if self.knownBlock != nil {
+ return self.knownBlock(hash)
+ }
+ return false
+}
+
+func (self *testChainManager) AddBlock(block *types.Block) error {
+ if self.addBlock != nil {
+ return self.addBlock(block)
+ }
+ return nil
+}
+
+func (self *testChainManager) CheckPoW(block *types.Block) bool {
+ if self.checkPoW != nil {
+ return self.checkPoW(block)
+ }
+ return false
+}
+
+func knownBlock(hashes ...[]byte) (f func([]byte) bool) {
+ f = func(block []byte) bool {
+ for _, hash := range hashes {
+ if bytes.Compare(block, hash) == 0 {
+ return true
+ }
+ }
+ return false
+ }
+ return
+}
+
+func addBlock(hashes ...[]byte) (f func(*types.Block) error) {
+ f = func(block *types.Block) error {
+ for _, hash := range hashes {
+ if bytes.Compare(block.Hash(), hash) == 0 {
+ return fmt.Errorf("invalid by test")
+ }
+ }
+ return nil
+ }
+ return
+}
+
+func checkPoW(hashes ...[]byte) (f func(*types.Block) bool) {
+ f = func(block *types.Block) bool {
+ for _, hash := range hashes {
+ if bytes.Compare(block.Hash(), hash) == 0 {
+ return false
+ }
+ }
+ return true
+ }
+ return
+}
+
+func newTestChainManager(knownBlocks [][]byte, invalidBlocks [][]byte, invalidPoW [][]byte) *testChainManager {
+ return &testChainManager{
+ knownBlock: knownBlock(knownBlocks...),
+ addBlock: addBlock(invalidBlocks...),
+ checkPoW: checkPoW(invalidPoW...),
+ }
+}
+
+type intToHash map[int][]byte
+
+type hashToInt map[string]int
+
+type testHashPool struct {
+ intToHash
+ hashToInt
+}
+
+func newHash(i int) []byte {
+ return crypto.Sha3([]byte(string(i)))
+}
+
+func newTestBlockPool(knownBlockIndexes []int, invalidBlockIndexes []int, invalidPoWIndexes []int) (hashPool *testHashPool, blockPool *BlockPool) {
+ hashPool = &testHashPool{make(intToHash), make(hashToInt)}
+ knownBlocks := hashPool.indexesToHashes(knownBlockIndexes)
+ invalidBlocks := hashPool.indexesToHashes(invalidBlockIndexes)
+ invalidPoW := hashPool.indexesToHashes(invalidPoWIndexes)
+ blockPool = NewBlockPool(newTestChainManager(knownBlocks, invalidBlocks, invalidPoW))
+ return
+}
+
+func (self *testHashPool) indexesToHashes(indexes []int) (hashes [][]byte) {
+ for _, i := range indexes {
+ hash, found := self.intToHash[i]
+ if !found {
+ hash = newHash(i)
+ self.intToHash[i] = hash
+ self.hashToInt[string(hash)] = i
+ }
+ hashes = append(hashes, hash)
+ }
+ return
+}
+
+func (self *testHashPool) hashesToIndexes(hashes [][]byte) (indexes []int) {
+ for _, hash := range hashes {
+ i, found := self.hashToInt[string(hash)]
+ if !found {
+ i = -1
+ }
+ indexes = append(indexes, i)
+ }
+ return
+}
+
+type protocolChecker struct {
+ blockHashesRequests []int
+ blocksRequests [][]int
+ invalidBlocks []error
+ hashPool *testHashPool
+ lock sync.Mutex
+}
+
+// -1 is special: not found (a hash never seen)
+func (self *protocolChecker) requestBlockHashesCallBack() (requestBlockHashesCallBack func([]byte) error) {
+ requestBlockHashesCallBack = func(hash []byte) error {
+ indexes := self.hashPool.hashesToIndexes([][]byte{hash})
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.blockHashesRequests = append(self.blockHashesRequests, indexes[0])
+ return nil
+ }
+ return
+}
+
+func (self *protocolChecker) requestBlocksCallBack() (requestBlocksCallBack func([][]byte) error) {
+ requestBlocksCallBack = func(hashes [][]byte) error {
+ indexes := self.hashPool.hashesToIndexes(hashes)
+ self.lock.Lock()
+ defer self.lock.Unlock()
+ self.blocksRequests = append(self.blocksRequests, indexes)
+ return nil
+ }
+ return
+}
+
+func (self *protocolChecker) invalidBlockCallBack() (invalidBlockCallBack func(error)) {
+ invalidBlockCallBack = func(err error) {
+ self.invalidBlocks = append(self.invalidBlocks, err)
+ }
+ return
+}
+
+func TestAddPeer(t *testing.T) {
+ ethlogger.AddLogSystem(sys)
+ knownBlockIndexes := []int{0, 1}
+ invalidBlockIndexes := []int{2, 3}
+ invalidPoWIndexes := []int{4, 5}
+ hashPool, blockPool := newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
+ // TODO:
+ // hashPool, blockPool, blockChainChecker = newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
+ peer0 := &protocolChecker{
+ // blockHashesRequests: make([]int),
+ // blocksRequests: make([][]int),
+ // invalidBlocks: make([]error),
+ hashPool: hashPool,
+ }
+ best := blockPool.AddPeer(ethutil.Big1, newHash(100), "0",
+ peer0.requestBlockHashesCallBack(),
+ peer0.requestBlocksCallBack(),
+ peer0.invalidBlockCallBack(),
+ )
+ if !best {
+ t.Errorf("peer not accepted as best")
+ }
+ blockPool.Stop()
+
+}
diff --git a/eth/error.go b/eth/error.go
new file mode 100644
index 000000000..d1daad575
--- /dev/null
+++ b/eth/error.go
@@ -0,0 +1,71 @@
+package eth
+
+import (
+ "fmt"
+)
+
+const (
+ ErrMsgTooLarge = iota
+ ErrDecode
+ ErrInvalidMsgCode
+ ErrProtocolVersionMismatch
+ ErrNetworkIdMismatch
+ ErrGenesisBlockMismatch
+ ErrNoStatusMsg
+ ErrExtraStatusMsg
+ ErrInvalidBlock
+ ErrInvalidPoW
+ ErrUnrequestedBlock
+)
+
+var errorToString = map[int]string{
+ ErrMsgTooLarge: "Message too long",
+ ErrDecode: "Invalid message",
+ ErrInvalidMsgCode: "Invalid message code",
+ ErrProtocolVersionMismatch: "Protocol version mismatch",
+ ErrNetworkIdMismatch: "NetworkId mismatch",
+ ErrGenesisBlockMismatch: "Genesis block mismatch",
+ ErrNoStatusMsg: "No status message",
+ ErrExtraStatusMsg: "Extra status message",
+ ErrInvalidBlock: "Invalid block",
+ ErrInvalidPoW: "Invalid PoW",
+ ErrUnrequestedBlock: "Unrequested block",
+}
+
+type protocolError struct {
+ Code int
+ fatal bool
+ message string
+ format string
+ params []interface{}
+ // size int
+}
+
+func newProtocolError(code int, format string, params ...interface{}) *protocolError {
+ return &protocolError{Code: code, format: format, params: params}
+}
+
+func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
+ err = newProtocolError(code, format, params...)
+ // report(err)
+ return
+}
+
+func (self protocolError) Error() (message string) {
+ message = self.message
+ if message == "" {
+ message, ok := errorToString[self.Code]
+ if !ok {
+ panic("invalid error code")
+ }
+ if self.format != "" {
+ message += ": " + fmt.Sprintf(self.format, self.params...)
+ }
+ self.message = message
+ }
+ return
+}
+
+func (self *protocolError) Fatal() bool {
+ return self.fatal
+}
diff --git a/eth/peer_util.go b/eth/peer_util.go
new file mode 100644
index 000000000..6cf80cde2
--- /dev/null
+++ b/eth/peer_util.go
@@ -0,0 +1,23 @@
+package eth
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+)
+
+func WritePeers(path string, addresses []string) {
+ if len(addresses) > 0 {
+ data, _ := json.MarshalIndent(addresses, "", " ")
+ ethutil.WriteFile(path, data)
+ }
+}
+
+func ReadPeers(path string) (ips []string, err error) {
+ var data string
+ data, err = ethutil.ReadAllFile(path)
+ if err != nil {
+ json.Unmarshal([]byte(data), &ips)
+ }
+ return
+}
diff --git a/eth/protocol.go b/eth/protocol.go
new file mode 100644
index 000000000..963d41794
--- /dev/null
+++ b/eth/protocol.go
@@ -0,0 +1,319 @@
+package eth
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+const (
+ ProtocolVersion = 49
+ NetworkId = 0
+ ProtocolLength = uint64(8)
+ ProtocolMaxMsgSize = 10 * 1024 * 1024
+)
+
+// eth protocol message codes
+const (
+ StatusMsg = iota
+ GetTxMsg // unused
+ TxMsg
+ GetBlockHashesMsg
+ BlockHashesMsg
+ GetBlocksMsg
+ BlocksMsg
+ NewBlockMsg
+)
+
+// ethProtocol represents the ethereum wire protocol
+// instance is running on each peer
+type ethProtocol struct {
+ txPool txPool
+ chainManager chainManager
+ blockPool blockPool
+ peer *p2p.Peer
+ id string
+ rw p2p.MsgReadWriter
+}
+
+// backend is the interface the ethereum protocol backend should implement
+// used as an argument to EthProtocol
+type txPool interface {
+ AddTransactions([]*types.Transaction)
+}
+
+type chainManager interface {
+ GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte)
+ GetBlock(hash []byte) (block *types.Block)
+ Status() (td *big.Int, currentBlock []byte, genesisBlock []byte)
+}
+
+type blockPool interface {
+ AddBlockHashes(next func() ([]byte, bool), peerId string)
+ AddBlock(block *types.Block, peerId string)
+ AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
+ RemovePeer(peerId string)
+}
+
+// message structs used for rlp decoding
+type newBlockMsgData struct {
+ Block *types.Block
+ TD *big.Int
+}
+
+type getBlockHashesMsgData struct {
+ Hash []byte
+ Amount uint64
+}
+
+// main entrypoint, wrappers starting a server running the eth protocol
+// use this constructor to attach the protocol ("class") to server caps
+// the Dev p2p layer then runs the protocol instance on each peer
+func EthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool) p2p.Protocol {
+ return p2p.Protocol{
+ Name: "eth",
+ Version: ProtocolVersion,
+ Length: ProtocolLength,
+ Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
+ return runEthProtocol(txPool, chainManager, blockPool, peer, rw)
+ },
+ }
+}
+
+// the main loop that handles incoming messages
+// note RemovePeer in the post-disconnect hook
+func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool, peer *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
+ self := &ethProtocol{
+ txPool: txPool,
+ chainManager: chainManager,
+ blockPool: blockPool,
+ rw: rw,
+ peer: peer,
+ id: (string)(peer.Identity().Pubkey()),
+ }
+ err = self.handleStatus()
+ if err == nil {
+ for {
+ err = self.handle()
+ if err != nil {
+ fmt.Println(err)
+ self.blockPool.RemovePeer(self.id)
+ break
+ }
+ }
+ }
+ return
+}
+
+func (self *ethProtocol) handle() error {
+ msg, err := self.rw.ReadMsg()
+ if err != nil {
+ return err
+ }
+ if msg.Size > ProtocolMaxMsgSize {
+ return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
+ }
+ // make sure that the payload has been fully consumed
+ defer msg.Discard()
+
+ switch msg.Code {
+
+ case StatusMsg:
+ return ProtocolError(ErrExtraStatusMsg, "")
+
+ case TxMsg:
+ // TODO: rework using lazy RLP stream
+ var txs []*types.Transaction
+ if err := msg.Decode(&txs); err != nil {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+ self.txPool.AddTransactions(txs)
+
+ case GetBlockHashesMsg:
+ var request getBlockHashesMsgData
+ if err := msg.Decode(&request); err != nil {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+ hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
+ return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
+
+ case BlockHashesMsg:
+ // TODO: redo using lazy decode , this way very inefficient on known chains
+ msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
+ var err error
+ iter := func() (hash []byte, ok bool) {
+ hash, err = msgStream.Bytes()
+ if err == nil {
+ ok = true
+ }
+ return
+ }
+ self.blockPool.AddBlockHashes(iter, self.id)
+ if err != nil && err != rlp.EOL {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+
+ case GetBlocksMsg:
+ var blockHashes [][]byte
+ if err := msg.Decode(&blockHashes); err != nil {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+ max := int(math.Min(float64(len(blockHashes)), blockHashesBatchSize))
+ var blocks []interface{}
+ for i, hash := range blockHashes {
+ if i >= max {
+ break
+ }
+ block := self.chainManager.GetBlock(hash)
+ if block != nil {
+ blocks = append(blocks, block.RlpData())
+ }
+ }
+ return self.rw.EncodeMsg(BlocksMsg, blocks...)
+
+ case BlocksMsg:
+ msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
+ for {
+ var block *types.Block
+ if err := msgStream.Decode(&block); err != nil {
+ if err == rlp.EOL {
+ break
+ } else {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+ }
+ self.blockPool.AddBlock(block, self.id)
+ }
+
+ case NewBlockMsg:
+ var request newBlockMsgData
+ if err := msg.Decode(&request); err != nil {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+ hash := request.Block.Hash()
+ // to simplify backend interface adding a new block
+ // uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
+ // (or selected as new best peer)
+ if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
+ called := true
+ iter := func() (hash []byte, ok bool) {
+ if called {
+ called = false
+ return hash, true
+ } else {
+ return
+ }
+ }
+ self.blockPool.AddBlockHashes(iter, self.id)
+ self.blockPool.AddBlock(request.Block, self.id)
+ }
+
+ default:
+ return ProtocolError(ErrInvalidMsgCode, "%v", msg.Code)
+ }
+ return nil
+}
+
+type statusMsgData struct {
+ ProtocolVersion uint
+ NetworkId uint
+ TD *big.Int
+ CurrentBlock []byte
+ GenesisBlock []byte
+}
+
+func (self *ethProtocol) statusMsg() p2p.Msg {
+ td, currentBlock, genesisBlock := self.chainManager.Status()
+
+ return p2p.NewMsg(StatusMsg,
+ uint32(ProtocolVersion),
+ uint32(NetworkId),
+ td,
+ currentBlock,
+ genesisBlock,
+ )
+}
+
+func (self *ethProtocol) handleStatus() error {
+ // send precanned status message
+ if err := self.rw.WriteMsg(self.statusMsg()); err != nil {
+ return err
+ }
+
+ // read and handle remote status
+ msg, err := self.rw.ReadMsg()
+ if err != nil {
+ return err
+ }
+
+ if msg.Code != StatusMsg {
+ return ProtocolError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
+ }
+
+ if msg.Size > ProtocolMaxMsgSize {
+ return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
+ }
+
+ var status statusMsgData
+ if err := msg.Decode(&status); err != nil {
+ return ProtocolError(ErrDecode, "%v", err)
+ }
+
+ _, _, genesisBlock := self.chainManager.Status()
+
+ if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
+ return ProtocolError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
+ }
+
+ if status.NetworkId != NetworkId {
+ return ProtocolError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
+ }
+
+ if ProtocolVersion != status.ProtocolVersion {
+ return ProtocolError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
+ }
+
+ self.peer.Infof("Peer is [eth] capable (%d/%d). TD=%v H=%x\n", status.ProtocolVersion, status.NetworkId, status.TD, status.CurrentBlock[:4])
+
+ //self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
+ self.peer.Infoln("AddPeer(IGNORED)")
+
+ return nil
+}
+
+func (self *ethProtocol) requestBlockHashes(from []byte) error {
+ self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
+ return self.rw.EncodeMsg(GetBlockHashesMsg, from, blockHashesBatchSize)
+}
+
+func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
+ self.peer.Debugf("fetching %v blocks", len(hashes))
+ return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes))
+}
+
+func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
+ err = ProtocolError(code, format, params...)
+ if err.Fatal() {
+ self.peer.Errorln(err)
+ } else {
+ self.peer.Debugln(err)
+ }
+ return
+}
+
+func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
+ err := ProtocolError(code, format, params...)
+ if err.Fatal() {
+ self.peer.Errorln(err)
+ // disconnect
+ } else {
+ self.peer.Debugln(err)
+ }
+
+}
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
new file mode 100644
index 000000000..322aec7b7
--- /dev/null
+++ b/eth/protocol_test.go
@@ -0,0 +1,232 @@
+package eth
+
+import (
+ "io"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/p2p"
+)
+
+type testMsgReadWriter struct {
+ in chan p2p.Msg
+ out chan p2p.Msg
+}
+
+func (self *testMsgReadWriter) In(msg p2p.Msg) {
+ self.in <- msg
+}
+
+func (self *testMsgReadWriter) Out(msg p2p.Msg) {
+ self.in <- msg
+}
+
+func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
+ self.out <- msg
+ return nil
+}
+
+func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
+ return self.WriteMsg(p2p.NewMsg(code, data))
+}
+
+func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
+ msg, ok := <-self.in
+ if !ok {
+ return msg, io.EOF
+ }
+ return msg, nil
+}
+
+func errorCheck(t *testing.T, expCode int, err error) {
+ perr, ok := err.(*protocolError)
+ if ok && perr != nil {
+ if code := perr.Code; code != expCode {
+ ok = false
+ }
+ }
+ if !ok {
+ t.Errorf("expected error code %v, got %v", ErrNoStatusMsg, err)
+ }
+}
+
+type TestBackend struct {
+ getTransactions func() []*types.Transaction
+ addTransactions func(txs []*types.Transaction)
+ getBlockHashes func(hash []byte, amount uint32) (hashes [][]byte)
+ addBlockHashes func(next func() ([]byte, bool), peerId string)
+ getBlock func(hash []byte) *types.Block
+ addBlock func(block *types.Block, peerId string) (err error)
+ addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool)
+ removePeer func(peerId string)
+ status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
+}
+
+func (self *TestBackend) GetTransactions() (txs []*types.Transaction) {
+ if self.getTransactions != nil {
+ txs = self.getTransactions()
+ }
+ return
+}
+
+func (self *TestBackend) AddTransactions(txs []*types.Transaction) {
+ if self.addTransactions != nil {
+ self.addTransactions(txs)
+ }
+}
+
+func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) {
+ if self.getBlockHashes != nil {
+ hashes = self.getBlockHashes(hash, amount)
+ }
+ return
+}
+
+<<<<<<< HEAD
+<<<<<<< HEAD
+func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
+ if self.addBlockHashes != nil {
+ self.addBlockHashes(next, peerId)
+ }
+}
+
+=======
+func (self *TestBackend) AddHash(hash []byte, peer *p2p.Peer) (more bool) {
+ if self.addHash != nil {
+ more = self.addHash(hash, peer)
+=======
+func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
+ if self.addBlockHashes != nil {
+ self.addBlockHashes(next, peerId)
+>>>>>>> eth protocol changes
+ }
+}
+<<<<<<< HEAD
+>>>>>>> initial commit for eth-p2p integration
+=======
+
+>>>>>>> eth protocol changes
+func (self *TestBackend) GetBlock(hash []byte) (block *types.Block) {
+ if self.getBlock != nil {
+ block = self.getBlock(hash)
+ }
+ return
+}
+
+<<<<<<< HEAD
+<<<<<<< HEAD
+func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
+ if self.addBlock != nil {
+ err = self.addBlock(block, peerId)
+=======
+func (self *TestBackend) AddBlock(td *big.Int, block *types.Block, peer *p2p.Peer) (fetchHashes bool, err error) {
+ if self.addBlock != nil {
+ fetchHashes, err = self.addBlock(td, block, peer)
+>>>>>>> initial commit for eth-p2p integration
+=======
+func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
+ if self.addBlock != nil {
+ err = self.addBlock(block, peerId)
+>>>>>>> eth protocol changes
+ }
+ return
+}
+
+<<<<<<< HEAD
+<<<<<<< HEAD
+func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
+ if self.addPeer != nil {
+ best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
+=======
+func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peer *p2p.Peer) (fetchHashes bool) {
+ if self.addPeer != nil {
+ fetchHashes = self.addPeer(td, currentBlock, peer)
+>>>>>>> initial commit for eth-p2p integration
+=======
+func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
+ if self.addPeer != nil {
+ best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
+>>>>>>> eth protocol changes
+ }
+ return
+}
+
+<<<<<<< HEAD
+<<<<<<< HEAD
+=======
+>>>>>>> eth protocol changes
+func (self *TestBackend) RemovePeer(peerId string) {
+ if self.removePeer != nil {
+ self.removePeer(peerId)
+ }
+}
+
+<<<<<<< HEAD
+=======
+>>>>>>> initial commit for eth-p2p integration
+=======
+>>>>>>> eth protocol changes
+func (self *TestBackend) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
+ if self.status != nil {
+ td, currentBlock, genesisBlock = self.status()
+ }
+ return
+}
+
+<<<<<<< HEAD
+<<<<<<< HEAD
+=======
+>>>>>>> eth protocol changes
+// TODO: refactor this into p2p/client_identity
+type peerId struct {
+ pubkey []byte
+}
+
+func (self *peerId) String() string {
+ return "test peer"
+}
+
+func (self *peerId) Pubkey() (pubkey []byte) {
+ pubkey = self.pubkey
+ if len(pubkey) == 0 {
+ pubkey = crypto.GenerateNewKeyPair().PublicKey
+ self.pubkey = pubkey
+ }
+ return
+}
+
+func testPeer() *p2p.Peer {
+ return p2p.NewPeer(&peerId{}, []p2p.Cap{})
+}
+
+func TestErrNoStatusMsg(t *testing.T) {
+<<<<<<< HEAD
+=======
+func TestEth(t *testing.T) {
+>>>>>>> initial commit for eth-p2p integration
+=======
+>>>>>>> eth protocol changes
+ quit := make(chan bool)
+ rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
+ testBackend := &TestBackend{}
+ var err error
+ go func() {
+<<<<<<< HEAD
+<<<<<<< HEAD
+ err = runEthProtocol(testBackend, testPeer(), rw)
+=======
+ err = runEthProtocol(testBackend, nil, rw)
+>>>>>>> initial commit for eth-p2p integration
+=======
+ err = runEthProtocol(testBackend, testPeer(), rw)
+>>>>>>> eth protocol changes
+ close(quit)
+ }()
+ statusMsg := p2p.NewMsg(4)
+ rw.In(statusMsg)
+ <-quit
+ errorCheck(t, ErrNoStatusMsg, err)
+ // read(t, remote, []byte("hello, world"), nil)
+}
diff --git a/ethdb/database.go b/ethdb/database.go
index 19aa83466..47ddec9c0 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -71,6 +71,10 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
return self.db.NewIterator(nil, nil)
}
+func (self *LDBDatabase) Write(batch *leveldb.Batch) error {
+ return self.db.Write(batch, nil)
+}
+
func (self *LDBDatabase) Close() {
// Close the leveldb database
self.db.Close()
diff --git a/ethereum.go b/ethereum.go
deleted file mode 100644
index 5d74e28e9..000000000
--- a/ethereum.go
+++ /dev/null
@@ -1,659 +0,0 @@
-package eth
-
-import (
- "container/list"
- "encoding/json"
- "fmt"
- "math/big"
- "math/rand"
- "net"
- "path"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/ethereum/go-ethereum/state"
- "github.com/ethereum/go-ethereum/wire"
-)
-
-const (
- seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
- seedNodeAddress = "poc-7.ethdev.com:30303"
-)
-
-var loggerger = logger.NewLogger("SERV")
-
-func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
- // Loop thru the peers and close them (if we had them)
- for e := peers.Front(); e != nil; e = e.Next() {
- callback(e.Value.(*Peer), e)
- }
-}
-
-const (
- processReapingTimeout = 60 // TODO increase
-)
-
-type Ethereum struct {
- // Channel for shutting down the ethereum
- shutdownChan chan bool
- quit chan bool
-
- // DB interface
- db ethutil.Database
- // State manager for processing new blocks and managing the over all states
- blockManager *core.BlockManager
- // The transaction pool. Transaction can be pushed on this pool
- // for later including in the blocks
- txPool *core.TxPool
- // The canonical chain
- blockChain *core.ChainManager
- // The block pool
- blockPool *BlockPool
- // Eventer
- eventMux event.TypeMux
- // Peers
- peers *list.List
- // Nonce
- Nonce uint64
-
- Addr net.Addr
- Port string
-
- blacklist [][]byte
-
- peerMut sync.Mutex
-
- // Capabilities for outgoing peers
- serverCaps Caps
-
- nat NAT
-
- // Specifies the desired amount of maximum peers
- MaxPeers int
-
- Mining bool
-
- listening bool
-
- RpcServer *rpc.JsonRpcServer
-
- keyManager *crypto.KeyManager
-
- clientIdentity wire.ClientIdentity
-
- isUpToDate bool
-
- filterMu sync.RWMutex
- filterId int
- filters map[int]*core.Filter
-}
-
-func New(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) {
- var err error
- var nat NAT
-
- if usePnp {
- nat, err = Discover()
- if err != nil {
- loggerger.Debugln("UPnP failed", err)
- }
- }
-
- bootstrapDb(db)
-
- ethutil.Config.Db = db
-
- nonce, _ := ethutil.RandomUint64()
- ethereum := &Ethereum{
- shutdownChan: make(chan bool),
- quit: make(chan bool),
- db: db,
- peers: list.New(),
- Nonce: nonce,
- serverCaps: caps,
- nat: nat,
- keyManager: keyManager,
- clientIdentity: clientIdentity,
- isUpToDate: true,
- filters: make(map[int]*core.Filter),
- }
-
- ethereum.blockPool = NewBlockPool(ethereum)
- ethereum.blockChain = core.NewChainManager(ethereum.EventMux())
- ethereum.txPool = core.NewTxPool(ethereum.blockChain, ethereum, ethereum.EventMux())
- ethereum.blockManager = core.NewBlockManager(ethereum.txPool, ethereum.blockChain, ethereum.EventMux())
- ethereum.blockChain.SetProcessor(ethereum.blockManager)
-
- // Start the tx pool
- ethereum.txPool.Start()
-
- return ethereum, nil
-}
-
-func (s *Ethereum) KeyManager() *crypto.KeyManager {
- return s.keyManager
-}
-
-func (s *Ethereum) ClientIdentity() wire.ClientIdentity {
- return s.clientIdentity
-}
-
-func (s *Ethereum) ChainManager() *core.ChainManager {
- return s.blockChain
-}
-
-func (s *Ethereum) BlockManager() *core.BlockManager {
- return s.blockManager
-}
-
-func (s *Ethereum) TxPool() *core.TxPool {
- return s.txPool
-}
-func (s *Ethereum) BlockPool() *BlockPool {
- return s.blockPool
-}
-func (s *Ethereum) EventMux() *event.TypeMux {
- return &s.eventMux
-}
-func (self *Ethereum) Db() ethutil.Database {
- return self.db
-}
-
-func (s *Ethereum) ServerCaps() Caps {
- return s.serverCaps
-}
-func (s *Ethereum) IsMining() bool {
- return s.Mining
-}
-func (s *Ethereum) PeerCount() int {
- return s.peers.Len()
-}
-func (s *Ethereum) IsUpToDate() bool {
- upToDate := true
- eachPeer(s.peers, func(peer *Peer, e *list.Element) {
- if atomic.LoadInt32(&peer.connected) == 1 {
- if peer.catchingUp == true && peer.versionKnown {
- upToDate = false
- }
- }
- })
- return upToDate
-}
-func (s *Ethereum) PushPeer(peer *Peer) {
- s.peers.PushBack(peer)
-}
-func (s *Ethereum) IsListening() bool {
- return s.listening
-}
-
-func (s *Ethereum) HighestTDPeer() (td *big.Int) {
- td = big.NewInt(0)
-
- eachPeer(s.peers, func(p *Peer, v *list.Element) {
- if p.td.Cmp(td) > 0 {
- td = p.td
- }
- })
-
- return
-}
-
-func (self *Ethereum) BlacklistPeer(peer *Peer) {
- self.blacklist = append(self.blacklist, peer.pubkey)
-}
-
-func (s *Ethereum) AddPeer(conn net.Conn) {
- peer := NewPeer(conn, s, true)
-
- if peer != nil {
- if s.peers.Len() < s.MaxPeers {
- peer.Start()
- } else {
- loggerger.Debugf("Max connected peers reached. Not adding incoming peer.")
- }
- }
-}
-
-func (s *Ethereum) ProcessPeerList(addrs []string) {
- for _, addr := range addrs {
- // TODO Probably requires some sanity checks
- s.ConnectToPeer(addr)
- }
-}
-
-func (s *Ethereum) ConnectToPeer(addr string) error {
- if s.peers.Len() < s.MaxPeers {
- var alreadyConnected bool
-
- ahost, aport, _ := net.SplitHostPort(addr)
- var chost string
-
- ips, err := net.LookupIP(ahost)
-
- if err != nil {
- return err
- } else {
- // If more then one ip is available try stripping away the ipv6 ones
- if len(ips) > 1 {
- var ipsv4 []net.IP
- // For now remove the ipv6 addresses
- for _, ip := range ips {
- if strings.Contains(ip.String(), "::") {
- continue
- } else {
- ipsv4 = append(ipsv4, ip)
- }
- }
- if len(ipsv4) == 0 {
- return fmt.Errorf("[SERV] No IPV4 addresses available for hostname")
- }
-
- // Pick a random ipv4 address, simulating round-robin DNS.
- rand.Seed(time.Now().UTC().UnixNano())
- i := rand.Intn(len(ipsv4))
- chost = ipsv4[i].String()
- } else {
- if len(ips) == 0 {
- return fmt.Errorf("[SERV] No IPs resolved for the given hostname")
- return nil
- }
- chost = ips[0].String()
- }
- }
-
- eachPeer(s.peers, func(p *Peer, v *list.Element) {
- if p.conn == nil {
- return
- }
- phost, pport, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
-
- if phost == chost && pport == aport {
- alreadyConnected = true
- //loggerger.Debugf("Peer %s already added.\n", chost)
- return
- }
- })
-
- if alreadyConnected {
- return nil
- }
-
- NewOutboundPeer(addr, s, s.serverCaps)
- }
-
- return nil
-}
-
-func (s *Ethereum) OutboundPeers() []*Peer {
- // Create a new peer slice with at least the length of the total peers
- outboundPeers := make([]*Peer, s.peers.Len())
- length := 0
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- if !p.inbound && p.conn != nil {
- outboundPeers[length] = p
- length++
- }
- })
-
- return outboundPeers[:length]
-}
-
-func (s *Ethereum) InboundPeers() []*Peer {
- // Create a new peer slice with at least the length of the total peers
- inboundPeers := make([]*Peer, s.peers.Len())
- length := 0
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- if p.inbound {
- inboundPeers[length] = p
- length++
- }
- })
-
- return inboundPeers[:length]
-}
-
-func (s *Ethereum) InOutPeers() []*Peer {
- // Reap the dead peers first
- s.reapPeers()
-
- // Create a new peer slice with at least the length of the total peers
- inboundPeers := make([]*Peer, s.peers.Len())
- length := 0
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- // Only return peers with an actual ip
- if len(p.host) > 0 {
- inboundPeers[length] = p
- length++
- }
- })
-
- return inboundPeers[:length]
-}
-
-func (s *Ethereum) Broadcast(msgType wire.MsgType, data []interface{}) {
- msg := wire.NewMessage(msgType, data)
- s.BroadcastMsg(msg)
-}
-
-func (s *Ethereum) BroadcastMsg(msg *wire.Msg) {
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- p.QueueMessage(msg)
- })
-}
-
-func (s *Ethereum) Peers() *list.List {
- return s.peers
-}
-
-func (s *Ethereum) reapPeers() {
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
- s.removePeerElement(e)
- }
- })
-}
-
-func (s *Ethereum) removePeerElement(e *list.Element) {
- s.peerMut.Lock()
- defer s.peerMut.Unlock()
-
- s.peers.Remove(e)
-
- s.eventMux.Post(PeerListEvent{s.peers})
-}
-
-func (s *Ethereum) RemovePeer(p *Peer) {
- eachPeer(s.peers, func(peer *Peer, e *list.Element) {
- if peer == p {
- s.removePeerElement(e)
- }
- })
-}
-
-func (s *Ethereum) reapDeadPeerHandler() {
- reapTimer := time.NewTicker(processReapingTimeout * time.Second)
-
- for {
- select {
- case <-reapTimer.C:
- s.reapPeers()
- }
- }
-}
-
-// Start the ethereum
-func (s *Ethereum) Start(seed bool) {
- s.blockPool.Start()
-
- // Bind to addr and port
- ln, err := net.Listen("tcp", ":"+s.Port)
- if err != nil {
- loggerger.Warnf("Port %s in use. Connection listening disabled. Acting as client", s.Port)
- s.listening = false
- } else {
- s.listening = true
- // Starting accepting connections
- loggerger.Infoln("Ready and accepting connections")
- // Start the peer handler
- go s.peerHandler(ln)
- }
-
- if s.nat != nil {
- go s.upnpUpdateThread()
- }
-
- // Start the reaping processes
- go s.reapDeadPeerHandler()
- go s.update()
- go s.filterLoop()
-
- if seed {
- s.Seed()
- }
- s.ConnectToPeer("localhost:40404")
- loggerger.Infoln("Server started")
-}
-
-func (s *Ethereum) Seed() {
- // Sorry Py person. I must blacklist. you perform badly
- s.blacklist = append(s.blacklist, ethutil.Hex2Bytes("64656330303561383532336435376331616537643864663236623336313863373537353163636634333530626263396330346237336262623931383064393031"))
- ips := PastPeers()
- if len(ips) > 0 {
- for _, ip := range ips {
- loggerger.Infoln("Connecting to previous peer ", ip)
- s.ConnectToPeer(ip)
- }
- } else {
- loggerger.Debugln("Retrieving seed nodes")
-
- // Eth-Go Bootstrapping
- ips, er := net.LookupIP("seed.bysh.me")
- if er == nil {
- peers := []string{}
- for _, ip := range ips {
- node := fmt.Sprintf("%s:%d", ip.String(), 30303)
- loggerger.Debugln("Found DNS Go Peer:", node)
- peers = append(peers, node)
- }
- s.ProcessPeerList(peers)
- }
-
- // Official DNS Bootstrapping
- _, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
- if err == nil {
- peers := []string{}
- // Iterate SRV nodes
- for _, n := range nodes {
- target := n.Target
- port := strconv.Itoa(int(n.Port))
- // Resolve target to ip (Go returns list, so may resolve to multiple ips?)
- addr, err := net.LookupHost(target)
- if err == nil {
- for _, a := range addr {
- // Build string out of SRV port and Resolved IP
- peer := net.JoinHostPort(a, port)
- loggerger.Debugln("Found DNS Bootstrap Peer:", peer)
- peers = append(peers, peer)
- }
- } else {
- loggerger.Debugln("Couldn't resolve :", target)
- }
- }
- // Connect to Peer list
- s.ProcessPeerList(peers)
- }
-
- s.ConnectToPeer(seedNodeAddress)
- }
-}
-
-func (s *Ethereum) peerHandler(listener net.Listener) {
- for {
- conn, err := listener.Accept()
- if err != nil {
- loggerger.Debugln(err)
-
- continue
- }
-
- go s.AddPeer(conn)
- }
-}
-
-func (s *Ethereum) Stop() {
- // Stop eventMux first, it will close all subscriptions.
- s.eventMux.Stop()
-
- // Close the database
- defer s.db.Close()
-
- var ips []string
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- ips = append(ips, p.conn.RemoteAddr().String())
- })
-
- if len(ips) > 0 {
- d, _ := json.MarshalIndent(ips, "", " ")
- ethutil.WriteFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"), d)
- }
-
- eachPeer(s.peers, func(p *Peer, e *list.Element) {
- p.Stop()
- })
-
- close(s.quit)
-
- if s.RpcServer != nil {
- s.RpcServer.Stop()
- }
- s.txPool.Stop()
- s.blockPool.Stop()
-
- loggerger.Infoln("Server stopped")
- close(s.shutdownChan)
-}
-
-// This function will wait for a shutdown and resumes main thread execution
-func (s *Ethereum) WaitForShutdown() {
- <-s.shutdownChan
-}
-
-func (s *Ethereum) upnpUpdateThread() {
- // Go off immediately to prevent code duplication, thereafter we renew
- // lease every 15 minutes.
- timer := time.NewTimer(5 * time.Minute)
- lport, _ := strconv.ParseInt(s.Port, 10, 16)
- first := true
-out:
- for {
- select {
- case <-timer.C:
- var err error
- _, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
- if err != nil {
- loggerger.Debugln("can't add UPnP port mapping:", err)
- break out
- }
- if first && err == nil {
- _, err = s.nat.GetExternalAddress()
- if err != nil {
- loggerger.Debugln("UPnP can't get external address:", err)
- continue out
- }
- first = false
- }
- timer.Reset(time.Minute * 15)
- case <-s.quit:
- break out
- }
- }
-
- timer.Stop()
-
- if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
- loggerger.Debugln("unable to remove UPnP port mapping:", err)
- } else {
- loggerger.Debugln("succesfully disestablished UPnP port mapping")
- }
-}
-
-func (self *Ethereum) update() {
- upToDateTimer := time.NewTicker(1 * time.Second)
-
-out:
- for {
- select {
- case <-upToDateTimer.C:
- if self.IsUpToDate() && !self.isUpToDate {
- self.eventMux.Post(ChainSyncEvent{false})
- self.isUpToDate = true
- } else if !self.IsUpToDate() && self.isUpToDate {
- self.eventMux.Post(ChainSyncEvent{true})
- self.isUpToDate = false
- }
- case <-self.quit:
- break out
- }
- }
-}
-
-// InstallFilter adds filter for blockchain events.
-// The filter's callbacks will run for matching blocks and messages.
-// The filter should not be modified after it has been installed.
-func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
- self.filterMu.Lock()
- id = self.filterId
- self.filters[id] = filter
- self.filterId++
- self.filterMu.Unlock()
- return id
-}
-
-func (self *Ethereum) UninstallFilter(id int) {
- self.filterMu.Lock()
- delete(self.filters, id)
- self.filterMu.Unlock()
-}
-
-// GetFilter retrieves a filter installed using InstallFilter.
-// The filter may not be modified.
-func (self *Ethereum) GetFilter(id int) *core.Filter {
- self.filterMu.RLock()
- defer self.filterMu.RUnlock()
- return self.filters[id]
-}
-
-func (self *Ethereum) filterLoop() {
- // Subscribe to events
- events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
- for event := range events.Chan() {
- switch event := event.(type) {
- case core.NewBlockEvent:
- self.filterMu.RLock()
- for _, filter := range self.filters {
- if filter.BlockCallback != nil {
- filter.BlockCallback(event.Block)
- }
- }
- self.filterMu.RUnlock()
-
- case state.Messages:
- self.filterMu.RLock()
- for _, filter := range self.filters {
- if filter.MessageCallback != nil {
- msgs := filter.FilterMessages(event)
- if len(msgs) > 0 {
- filter.MessageCallback(msgs)
- }
- }
- }
- self.filterMu.RUnlock()
- }
- }
-}
-
-func bootstrapDb(db ethutil.Database) {
- d, _ := db.Get([]byte("ProtocolVersion"))
- protov := ethutil.NewValue(d).Uint()
-
- if protov == 0 {
- db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
- }
-}
-
-func PastPeers() []string {
- var ips []string
- data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"))
- json.Unmarshal([]byte(data), &ips)
-
- return ips
-}
diff --git a/event/filter/generic_filter.go b/event/filter/generic_filter.go
index b04b4801e..2ce0f0642 100644
--- a/event/filter/generic_filter.go
+++ b/event/filter/generic_filter.go
@@ -2,19 +2,29 @@ package filter
type Generic struct {
Str1, Str2, Str3 string
+ Data map[string]struct{}
Fn func(data interface{})
}
+// self = registered, f = incoming
func (self Generic) Compare(f Filter) bool {
+ var strMatch, dataMatch = true, true
+
filter := f.(Generic)
- if (len(self.Str1) == 0 || filter.Str1 == self.Str1) &&
- (len(self.Str2) == 0 || filter.Str2 == self.Str2) &&
- (len(self.Str3) == 0 || filter.Str3 == self.Str3) {
- return true
+ if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
+ (len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
+ (len(self.Str3) > 0 && filter.Str3 != self.Str3) {
+ strMatch = false
+ }
+
+ for k, _ := range self.Data {
+ if _, ok := filter.Data[k]; !ok {
+ return false
+ }
}
- return false
+ return strMatch && dataMatch
}
func (self Generic) Trigger(data interface{}) {
diff --git a/event/filter/old_filter.go b/event/filter/old_filter.go
new file mode 100644
index 000000000..1a9a88173
--- /dev/null
+++ b/event/filter/old_filter.go
@@ -0,0 +1,94 @@
+// XXX This is the old filter system specifically for messages. This is till in used and could use some refactoring
+package filter
+
+import (
+ "sync"
+
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/state"
+)
+
+type FilterManager struct {
+ eventMux *event.TypeMux
+
+ filterMu sync.RWMutex
+ filterId int
+ filters map[int]*core.Filter
+
+ quit chan struct{}
+}
+
+func NewFilterManager(mux *event.TypeMux) *FilterManager {
+ return &FilterManager{
+ eventMux: mux,
+ filters: make(map[int]*core.Filter),
+ }
+}
+
+func (self *FilterManager) Start() {
+ go self.filterLoop()
+}
+
+func (self *FilterManager) Stop() {
+ close(self.quit)
+}
+
+func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
+ self.filterMu.Lock()
+ id = self.filterId
+ self.filters[id] = filter
+ self.filterId++
+ self.filterMu.Unlock()
+ return id
+}
+
+func (self *FilterManager) UninstallFilter(id int) {
+ self.filterMu.Lock()
+ delete(self.filters, id)
+ self.filterMu.Unlock()
+}
+
+// GetFilter retrieves a filter installed using InstallFilter.
+// The filter may not be modified.
+func (self *FilterManager) GetFilter(id int) *core.Filter {
+ self.filterMu.RLock()
+ defer self.filterMu.RUnlock()
+ return self.filters[id]
+}
+
+func (self *FilterManager) filterLoop() {
+ // Subscribe to events
+ events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
+
+out:
+ for {
+ select {
+ case <-self.quit:
+ break out
+ case event := <-events.Chan():
+ switch event := event.(type) {
+ case core.NewBlockEvent:
+ self.filterMu.RLock()
+ for _, filter := range self.filters {
+ if filter.BlockCallback != nil {
+ filter.BlockCallback(event.Block)
+ }
+ }
+ self.filterMu.RUnlock()
+
+ case state.Messages:
+ self.filterMu.RLock()
+ for _, filter := range self.filters {
+ if filter.MessageCallback != nil {
+ msgs := filter.FilterMessages(event)
+ if len(msgs) > 0 {
+ filter.MessageCallback(msgs)
+ }
+ }
+ }
+ self.filterMu.RUnlock()
+ }
+ }
+ }
+}
diff --git a/events.go b/events.go
deleted file mode 100644
index 5fff1d831..000000000
--- a/events.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package eth
-
-import "container/list"
-
-type PeerListEvent struct {
- Peers *list.List
-}
-
-type ChainSyncEvent struct {
- InSync bool
-}
diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go
index a26f0154e..af1405049 100644
--- a/javascript/javascript_runtime.go
+++ b/javascript/javascript_runtime.go
@@ -7,10 +7,10 @@ import (
"path"
"path/filepath"
- "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
@@ -203,7 +203,7 @@ func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
if err != nil {
return otto.FalseValue()
}
- self.ethereum.ConnectToPeer(host)
+ self.ethereum.SuggestPeer(host)
return otto.TrueValue()
}
diff --git a/javascript/types.go b/javascript/types.go
index d5acaecce..ce1d9995a 100644
--- a/javascript/types.go
+++ b/javascript/types.go
@@ -3,7 +3,7 @@ package javascript
import (
"fmt"
- "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/ui"
@@ -18,11 +18,11 @@ type JSStateObject struct {
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
cb := call.Argument(0)
- self.JSObject.EachStorage(func(key string, value *ethutil.Value) {
- value.Decode()
- cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes())))
- })
+ it := self.JSObject.Trie().Iterator()
+ for it.Next() {
+ cb.Call(self.eth.toVal(self), self.eth.toVal(ethutil.Bytes2Hex(it.Key)), self.eth.toVal(ethutil.Bytes2Hex(it.Value)))
+ }
return otto.UndefinedValue()
}
diff --git a/miner/miner.go b/miner/miner.go
index f63096b63..c9a6922bb 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -27,7 +27,7 @@ import (
"math/big"
"sort"
- "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/pow/ezp"
@@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/wire"
)
type LocalTx struct {
@@ -57,7 +56,7 @@ type Miner struct {
eth *eth.Ethereum
events event.Subscription
- uncles types.Blocks
+ uncles []*types.Header
localTxs map[int]*LocalTx
localTxId int
@@ -185,9 +184,9 @@ func (self *Miner) mine() {
block.SetUncles(self.uncles)
}
- parent := chainMan.GetBlock(block.PrevHash)
- coinbase := block.State().GetOrNewStateObject(block.Coinbase)
- coinbase.SetGasPool(block.CalcGasLimit(parent))
+ parent := chainMan.GetBlock(block.ParentHash())
+ coinbase := block.State().GetOrNewStateObject(block.Coinbase())
+ coinbase.SetGasPool(core.CalcGasLimit(parent, block))
transactions := self.finiliseTxs()
@@ -212,12 +211,12 @@ func (self *Miner) mine() {
// Find a valid nonce
nonce := self.pow.Search(block, self.powQuitCh)
if nonce != nil {
- block.Nonce = nonce
+ block.Header().Nonce = nonce
err := chainMan.InsertChain(types.Blocks{block})
if err != nil {
minerlogger.Infoln(err)
} else {
- self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})
+ self.eth.EventMux().Post(core.NewMinedBlockEvent{block})
minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
minerlogger.Infoln(block)
@@ -246,7 +245,7 @@ func (self *Miner) finiliseTxs() types.Transactions {
}
// Faster than append
- for _, tx := range self.eth.TxPool().CurrentTransactions() {
+ for _, tx := range self.eth.TxPool().GetTransactions() {
if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 {
txs[actualSize] = tx
actualSize++
diff --git a/nat.go b/nat.go
deleted file mode 100644
index 999308eb2..000000000
--- a/nat.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package eth
-
-import (
- "net"
-)
-
-// protocol is either "udp" or "tcp"
-type NAT interface {
- GetExternalAddress() (addr net.IP, err error)
- AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
- DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
-}
diff --git a/natpmp.go b/natpmp.go
deleted file mode 100644
index 489342a4b..000000000
--- a/natpmp.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package eth
-
-import (
- "fmt"
- "net"
-
- natpmp "github.com/jackpal/go-nat-pmp"
-)
-
-// Adapt the NAT-PMP protocol to the NAT interface
-
-// TODO:
-// + Register for changes to the external address.
-// + Re-register port mapping when router reboots.
-// + A mechanism for keeping a port mapping registered.
-
-type natPMPClient struct {
- client *natpmp.Client
-}
-
-func NewNatPMP(gateway net.IP) (nat NAT) {
- return &natPMPClient{natpmp.NewClient(gateway)}
-}
-
-func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
- response, err := n.client.GetExternalAddress()
- if err != nil {
- return
- }
- ip := response.ExternalIPAddress
- addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
- return
-}
-
-func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
- description string, timeout int) (mappedExternalPort int, err error) {
- if timeout <= 0 {
- err = fmt.Errorf("timeout must not be <= 0")
- return
- }
- // Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
- response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
- if err != nil {
- return
- }
- mappedExternalPort = int(response.MappedExternalPort)
- return
-}
-
-func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
- // To destroy a mapping, send an add-port with
- // an internalPort of the internal port to destroy, an external port of zero and a time of zero.
- _, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
- return
-}
diff --git a/natupnp.go b/natupnp.go
deleted file mode 100644
index c7f9eeb62..000000000
--- a/natupnp.go
+++ /dev/null
@@ -1,338 +0,0 @@
-package eth
-
-// Just enough UPnP to be able to forward ports
-//
-
-import (
- "bytes"
- "encoding/xml"
- "errors"
- "net"
- "net/http"
- "os"
- "strconv"
- "strings"
- "time"
-)
-
-type upnpNAT struct {
- serviceURL string
- ourIP string
-}
-
-func Discover() (nat NAT, err error) {
- ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
- if err != nil {
- return
- }
- conn, err := net.ListenPacket("udp4", ":0")
- if err != nil {
- return
- }
- socket := conn.(*net.UDPConn)
- defer socket.Close()
-
- err = socket.SetDeadline(time.Now().Add(10 * time.Second))
- if err != nil {
- return
- }
-
- st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
- buf := bytes.NewBufferString(
- "M-SEARCH * HTTP/1.1\r\n" +
- "HOST: 239.255.255.250:1900\r\n" +
- st +
- "MAN: \"ssdp:discover\"\r\n" +
- "MX: 2\r\n\r\n")
- message := buf.Bytes()
- answerBytes := make([]byte, 1024)
- for i := 0; i < 3; i++ {
- _, err = socket.WriteToUDP(message, ssdp)
- if err != nil {
- return
- }
- var n int
- n, _, err = socket.ReadFromUDP(answerBytes)
- if err != nil {
- continue
- // socket.Close()
- // return
- }
- answer := string(answerBytes[0:n])
- if strings.Index(answer, "\r\n"+st) < 0 {
- continue
- }
- // HTTP header field names are case-insensitive.
- // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
- locString := "\r\nlocation: "
- answer = strings.ToLower(answer)
- locIndex := strings.Index(answer, locString)
- if locIndex < 0 {
- continue
- }
- loc := answer[locIndex+len(locString):]
- endIndex := strings.Index(loc, "\r\n")
- if endIndex < 0 {
- continue
- }
- locURL := loc[0:endIndex]
- var serviceURL string
- serviceURL, err = getServiceURL(locURL)
- if err != nil {
- return
- }
- var ourIP string
- ourIP, err = getOurIP()
- if err != nil {
- return
- }
- nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
- return
- }
- err = errors.New("UPnP port discovery failed.")
- return
-}
-
-// service represents the Service type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type service struct {
- ServiceType string `xml:"serviceType"`
- ControlURL string `xml:"controlURL"`
-}
-
-// deviceList represents the deviceList type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type deviceList struct {
- XMLName xml.Name `xml:"deviceList"`
- Device []device `xml:"device"`
-}
-
-// serviceList represents the serviceList type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type serviceList struct {
- XMLName xml.Name `xml:"serviceList"`
- Service []service `xml:"service"`
-}
-
-// device represents the device type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type device struct {
- XMLName xml.Name `xml:"device"`
- DeviceType string `xml:"deviceType"`
- DeviceList deviceList `xml:"deviceList"`
- ServiceList serviceList `xml:"serviceList"`
-}
-
-// specVersion represents the specVersion in a UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type specVersion struct {
- XMLName xml.Name `xml:"specVersion"`
- Major int `xml:"major"`
- Minor int `xml:"minor"`
-}
-
-// root represents the Root document for a UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type root struct {
- XMLName xml.Name `xml:"root"`
- SpecVersion specVersion
- Device device
-}
-
-func getChildDevice(d *device, deviceType string) *device {
- dl := d.DeviceList.Device
- for i := 0; i < len(dl); i++ {
- if dl[i].DeviceType == deviceType {
- return &dl[i]
- }
- }
- return nil
-}
-
-func getChildService(d *device, serviceType string) *service {
- sl := d.ServiceList.Service
- for i := 0; i < len(sl); i++ {
- if sl[i].ServiceType == serviceType {
- return &sl[i]
- }
- }
- return nil
-}
-
-func getOurIP() (ip string, err error) {
- hostname, err := os.Hostname()
- if err != nil {
- return
- }
- p, err := net.LookupIP(hostname)
- if err != nil && len(p) > 0 {
- return
- }
- return p[0].String(), nil
-}
-
-func getServiceURL(rootURL string) (url string, err error) {
- r, err := http.Get(rootURL)
- if err != nil {
- return
- }
- defer r.Body.Close()
- if r.StatusCode >= 400 {
- err = errors.New(string(r.StatusCode))
- return
- }
- var root root
- err = xml.NewDecoder(r.Body).Decode(&root)
-
- if err != nil {
- return
- }
- a := &root.Device
- if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
- err = errors.New("No InternetGatewayDevice")
- return
- }
- b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
- if b == nil {
- err = errors.New("No WANDevice")
- return
- }
- c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
- if c == nil {
- err = errors.New("No WANConnectionDevice")
- return
- }
- d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
- if d == nil {
- err = errors.New("No WANIPConnection")
- return
- }
- url = combineURL(rootURL, d.ControlURL)
- return
-}
-
-func combineURL(rootURL, subURL string) string {
- protocolEnd := "://"
- protoEndIndex := strings.Index(rootURL, protocolEnd)
- a := rootURL[protoEndIndex+len(protocolEnd):]
- rootIndex := strings.Index(a, "/")
- return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
-}
-
-func soapRequest(url, function, message string) (r *http.Response, err error) {
- fullMessage := "<?xml version=\"1.0\" ?>" +
- "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
- "<s:Body>" + message + "</s:Body></s:Envelope>"
-
- req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
- req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
- //req.Header.Set("Transfer-Encoding", "chunked")
- req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
- req.Header.Set("Connection", "Close")
- req.Header.Set("Cache-Control", "no-cache")
- req.Header.Set("Pragma", "no-cache")
-
- // log.Stderr("soapRequest ", req)
- //fmt.Println(fullMessage)
-
- r, err = http.DefaultClient.Do(req)
- if err != nil {
- return
- }
-
- if r.Body != nil {
- defer r.Body.Close()
- }
-
- if r.StatusCode >= 400 {
- // log.Stderr(function, r.StatusCode)
- err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
- r = nil
- return
- }
- return
-}
-
-type statusInfo struct {
- externalIpAddress string
-}
-
-func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
-
- message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
- "</u:GetStatusInfo>"
-
- var response *http.Response
- response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
- if err != nil {
- return
- }
-
- // TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
-
- response.Body.Close()
- return
-}
-
-func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
- info, err := n.getStatusInfo()
- if err != nil {
- return
- }
- addr = net.ParseIP(info.externalIpAddress)
- return
-}
-
-func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
- // A single concatenation would break ARM compilation.
- message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
- "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
- message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
- message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
- "<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
- "<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
- message += description +
- "</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
- "</NewLeaseDuration></u:AddPortMapping>"
-
- var response *http.Response
- response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
- if err != nil {
- return
- }
-
- // TODO: check response to see if the port was forwarded
- // log.Println(message, response)
- mappedExternalPort = externalPort
- _ = response
- return
-}
-
-func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
-
- message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
- "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
- "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
- "</u:DeletePortMapping>"
-
- var response *http.Response
- response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
- if err != nil {
- return
- }
-
- // TODO: check response to see if the port was deleted
- // log.Println(message, response)
- _ = response
- return
-}
diff --git a/p2p/server.go b/p2p/server.go
index 8a6087566..326781234 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -246,12 +246,7 @@ func (srv *Server) Stop() {
func (srv *Server) discLoop() {
for peer := range srv.peerDisconnect {
- // peer has just disconnected. free up its slot.
- srvlog.Infof("%v is gone", peer)
- srv.peerSlots <- peer.slot
- srv.lock.Lock()
- srv.peers[peer.slot] = nil
- srv.lock.Unlock()
+ srv.removePeer(peer)
}
}
@@ -384,7 +379,7 @@ func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer {
func (srv *Server) removePeer(peer *Peer) {
srv.lock.Lock()
defer srv.lock.Unlock()
- srvlog.Debugf("Removing peer %v %v (slot %v)\n", peer, peer.slot)
+ srvlog.Debugf("Removing %v (slot %v)\n", peer, peer.slot)
if srv.peers[peer.slot] != peer {
srvlog.Warnln("Invalid peer to remove:", peer)
return
@@ -416,6 +411,7 @@ func (srv *Server) verifyPeer(addr *peerAddr) error {
return nil
}
+// TODO replace with "Set"
type Blacklist interface {
Get([]byte) (bool, error)
Put([]byte) error
diff --git a/peer.go b/peer.go
deleted file mode 100644
index ceb49a5af..000000000
--- a/peer.go
+++ /dev/null
@@ -1,887 +0,0 @@
-package eth
-
-import (
- "bytes"
- "container/list"
- "fmt"
- "math"
- "math/big"
- "net"
- "strconv"
- "strings"
- "sync/atomic"
- "time"
-
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/wire"
-)
-
-var peerlogger = logger.NewLogger("PEER")
-
-const (
- // The size of the output buffer for writing messages
- outputBufferSize = 50
- // Current protocol version
- ProtocolVersion = 49
- // Current P2P version
- P2PVersion = 2
- // Ethereum network version
- NetVersion = 0
- // Interval for ping/pong message
- pingPongTimer = 2 * time.Second
-)
-
-type DiscReason byte
-
-const (
- // Values are given explicitly instead of by iota because these values are
- // defined by the wire protocol spec; it is easier for humans to ensure
- // correctness when values are explicit.
- DiscRequested DiscReason = iota
- DiscReTcpSysErr
- DiscBadProto
- DiscBadPeer
- DiscTooManyPeers
- DiscConnDup
- DiscGenesisErr
- DiscProtoErr
- DiscQuitting
-)
-
-var discReasonToString = []string{
- "requested",
- "TCP sys error",
- "bad protocol",
- "useless peer",
- "too many peers",
- "already connected",
- "wrong genesis block",
- "incompatible network",
- "quitting",
-}
-
-func (d DiscReason) String() string {
- if len(discReasonToString) < int(d) {
- return "Unknown"
- }
-
- return discReasonToString[d]
-}
-
-// Peer capabilities
-type Caps byte
-
-const (
- CapPeerDiscTy Caps = 1 << iota
- CapTxTy
- CapChainTy
-
- CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
-)
-
-var capsToString = map[Caps]string{
- CapPeerDiscTy: "Peer discovery",
- CapTxTy: "Transaction relaying",
- CapChainTy: "Block chain relaying",
-}
-
-func (c Caps) IsCap(cap Caps) bool {
- return c&cap > 0
-}
-
-func (c Caps) String() string {
- var caps []string
- if c.IsCap(CapPeerDiscTy) {
- caps = append(caps, capsToString[CapPeerDiscTy])
- }
- if c.IsCap(CapChainTy) {
- caps = append(caps, capsToString[CapChainTy])
- }
- if c.IsCap(CapTxTy) {
- caps = append(caps, capsToString[CapTxTy])
- }
-
- return strings.Join(caps, " | ")
-}
-
-type Peer struct {
- // Ethereum interface
- ethereum *Ethereum
- // Net connection
- conn net.Conn
- // Output queue which is used to communicate and handle messages
- outputQueue chan *wire.Msg
- // Quit channel
- quit chan bool
- // Determines whether it's an inbound or outbound peer
- inbound bool
- // Flag for checking the peer's connectivity state
- connected int32
- disconnect int32
- // Last known message send
- lastSend time.Time
- // Indicated whether a verack has been send or not
- // This flag is used by writeMessage to check if messages are allowed
- // to be send or not. If no version is known all messages are ignored.
- versionKnown bool
- statusKnown bool
-
- // Last received pong message
- lastPong int64
- lastBlockReceived time.Time
- doneFetchingHashes bool
- lastHashAt time.Time
- lastHashRequestedAt time.Time
-
- host []byte
- port uint16
- caps Caps
- td *big.Int
- bestHash []byte
- lastReceivedHash []byte
- requestedHashes [][]byte
-
- // This peer's public key
- pubkey []byte
-
- // Indicated whether the node is catching up or not
- catchingUp bool
- diverted bool
- blocksRequested int
-
- version string
-
- // We use this to give some kind of pingtime to a node, not very accurate, could be improved.
- pingTime time.Duration
- pingStartTime time.Time
-
- lastRequestedBlock *types.Block
-
- protocolCaps *ethutil.Value
-}
-
-func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
- pubkey := ethereum.KeyManager().PublicKey()[1:]
-
- return &Peer{
- outputQueue: make(chan *wire.Msg, outputBufferSize),
- quit: make(chan bool),
- ethereum: ethereum,
- conn: conn,
- inbound: inbound,
- disconnect: 0,
- connected: 1,
- port: 30303,
- pubkey: pubkey,
- blocksRequested: 10,
- caps: ethereum.ServerCaps(),
- version: ethereum.ClientIdentity().String(),
- protocolCaps: ethutil.NewValue(nil),
- td: big.NewInt(0),
- doneFetchingHashes: true,
- }
-}
-
-func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
- p := &Peer{
- outputQueue: make(chan *wire.Msg, outputBufferSize),
- quit: make(chan bool),
- ethereum: ethereum,
- inbound: false,
- connected: 0,
- disconnect: 0,
- port: 30303,
- caps: caps,
- version: ethereum.ClientIdentity().String(),
- protocolCaps: ethutil.NewValue(nil),
- td: big.NewInt(0),
- doneFetchingHashes: true,
- }
-
- // Set up the connection in another goroutine so we don't block the main thread
- go func() {
- conn, err := p.Connect(addr)
- if err != nil {
- //peerlogger.Debugln("Connection to peer failed. Giving up.", err)
- p.Stop()
- return
- }
- p.conn = conn
-
- // Atomically set the connection state
- atomic.StoreInt32(&p.connected, 1)
- atomic.StoreInt32(&p.disconnect, 0)
-
- p.Start()
- }()
-
- return p
-}
-
-func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
- const maxTries = 3
- for attempts := 0; attempts < maxTries; attempts++ {
- conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
- if err != nil {
- time.Sleep(time.Duration(attempts*20) * time.Second)
- continue
- }
-
- // Success
- return
- }
-
- return
-}
-
-// Getters
-func (p *Peer) PingTime() string {
- return p.pingTime.String()
-}
-func (p *Peer) Inbound() bool {
- return p.inbound
-}
-func (p *Peer) LastSend() time.Time {
- return p.lastSend
-}
-func (p *Peer) LastPong() int64 {
- return p.lastPong
-}
-func (p *Peer) Host() []byte {
- return p.host
-}
-func (p *Peer) Port() uint16 {
- return p.port
-}
-func (p *Peer) Version() string {
- return p.version
-}
-func (p *Peer) Connected() *int32 {
- return &p.connected
-}
-
-// Setters
-func (p *Peer) SetVersion(version string) {
- p.version = version
-}
-
-// Outputs any RLP encoded data to the peer
-func (p *Peer) QueueMessage(msg *wire.Msg) {
- if atomic.LoadInt32(&p.connected) != 1 {
- return
- }
- p.outputQueue <- msg
-}
-
-func (p *Peer) writeMessage(msg *wire.Msg) {
- // Ignore the write if we're not connected
- if atomic.LoadInt32(&p.connected) != 1 {
- return
- }
-
- if !p.versionKnown {
- switch msg.Type {
- case wire.MsgHandshakeTy: // Ok
- default: // Anything but ack is allowed
- return
- }
- } else {
- /*
- if !p.statusKnown {
- switch msg.Type {
- case wire.MsgStatusTy: // Ok
- default: // Anything but ack is allowed
- return
- }
- }
- */
- }
-
- peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
-
- err := wire.WriteMessage(p.conn, msg)
- if err != nil {
- peerlogger.Debugln(" Can't send message:", err)
- // Stop the client if there was an error writing to it
- p.Stop()
- return
- }
-}
-
-// Outbound message handler. Outbound messages are handled here
-func (p *Peer) HandleOutbound() {
- // The ping timer. Makes sure that every 2 minutes a ping is send to the peer
- pingTimer := time.NewTicker(pingPongTimer)
- serviceTimer := time.NewTicker(10 * time.Second)
-
-out:
- for {
- skip:
- select {
- // Main message queue. All outbound messages are processed through here
- case msg := <-p.outputQueue:
- if !p.statusKnown {
- switch msg.Type {
- case wire.MsgTxTy, wire.MsgGetBlockHashesTy, wire.MsgBlockHashesTy, wire.MsgGetBlocksTy, wire.MsgBlockTy:
- break skip
- }
- }
-
- switch msg.Type {
- case wire.MsgGetBlockHashesTy:
- p.lastHashRequestedAt = time.Now()
- }
-
- p.writeMessage(msg)
- p.lastSend = time.Now()
-
- // Ping timer
- case <-pingTimer.C:
- p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
- p.pingStartTime = time.Now()
-
- // Service timer takes care of peer broadcasting, transaction
- // posting or block posting
- case <-serviceTimer.C:
- p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
-
- case <-p.quit:
- // Break out of the for loop if a quit message is posted
- break out
- }
- }
-
-clean:
- // This loop is for draining the output queue and anybody waiting for us
- for {
- select {
- case <-p.outputQueue:
- // TODO
- default:
- break clean
- }
- }
-}
-
-func formatMessage(msg *wire.Msg) (ret string) {
- ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
-
- /*
- XXX Commented out because I need the log level here to determine
- if i should or shouldn't generate this message
- */
- /*
- switch msg.Type {
- case wire.MsgPeersTy:
- ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
- case wire.MsgBlockTy:
- b1, b2 := chain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
- ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
- case wire.MsgBlockHashesTy:
- h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
- ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
- }
- */
-
- return
-}
-
-// Inbound handler. Inbound messages are received here and passed to the appropriate methods
-func (p *Peer) HandleInbound() {
- for atomic.LoadInt32(&p.disconnect) == 0 {
-
- // HMM?
- time.Sleep(50 * time.Millisecond)
- // Wait for a message from the peer
- msgs, err := wire.ReadMessages(p.conn)
- if err != nil {
- peerlogger.Debugln(err)
- }
- for _, msg := range msgs {
- peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
-
- switch msg.Type {
- case wire.MsgHandshakeTy:
- // Version message
- p.handleHandshake(msg)
-
- //if p.caps.IsCap(CapPeerDiscTy) {
- p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
- //}
-
- case wire.MsgDiscTy:
- blockPool := p.ethereum.blockPool
- if blockPool.peer == p {
- blockPool.peer = nil
- blockPool.td = ethutil.Big0
- }
-
- p.Stop()
- peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
- case wire.MsgPingTy:
- // Respond back with pong
- p.QueueMessage(wire.NewMessage(wire.MsgPongTy, ""))
- case wire.MsgPongTy:
- // If we received a pong back from a peer we set the
- // last pong so the peer handler knows this peer is still
- // active.
- p.lastPong = time.Now().Unix()
- p.pingTime = time.Since(p.pingStartTime)
- case wire.MsgTxTy:
- // If the message was a transaction queue the transaction
- // in the TxPool where it will undergo validation and
- // processing when a new block is found
- for i := 0; i < msg.Data.Len(); i++ {
- tx := types.NewTransactionFromValue(msg.Data.Get(i))
- err := p.ethereum.TxPool().Add(tx)
- if err != nil {
- peerlogger.Infoln(err)
- } else {
- peerlogger.Infof("tx OK (%x)\n", tx.Hash()[0:4])
- }
- }
- case wire.MsgGetPeersTy:
- // Peer asked for list of connected peers
- //p.pushPeers()
- case wire.MsgPeersTy:
- // Received a list of peers (probably because MsgGetPeersTy was send)
- data := msg.Data
- // Create new list of possible peers for the ethereum to process
- peers := make([]string, data.Len())
- // Parse each possible peer
- for i := 0; i < data.Len(); i++ {
- value := data.Get(i)
- peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
- }
-
- // Connect to the list of peers
- p.ethereum.ProcessPeerList(peers)
-
- case wire.MsgStatusTy:
- // Handle peer's status msg
- p.handleStatus(msg)
- }
-
- // TMP
- if p.statusKnown {
- switch msg.Type {
-
- case wire.MsgGetBlockHashesTy:
- if msg.Data.Len() < 2 {
- peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
- }
-
- hash := msg.Data.Get(0).Bytes()
- amount := msg.Data.Get(1).Uint()
-
- hashes := p.ethereum.ChainManager().GetChainHashesFromHash(hash, amount)
-
- p.QueueMessage(wire.NewMessage(wire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
-
- case wire.MsgGetBlocksTy:
- // Limit to max 300 blocks
- max := int(math.Min(float64(msg.Data.Len()), 300.0))
- var blocks []interface{}
-
- for i := 0; i < max; i++ {
- hash := msg.Data.Get(i).Bytes()
- block := p.ethereum.ChainManager().GetBlock(hash)
- if block != nil {
- blocks = append(blocks, block.Value().Raw())
- }
- }
-
- p.QueueMessage(wire.NewMessage(wire.MsgBlockTy, blocks))
-
- case wire.MsgBlockHashesTy:
- p.catchingUp = true
-
- blockPool := p.ethereum.blockPool
-
- foundCommonHash := false
- p.lastHashAt = time.Now()
-
- it := msg.Data.NewIterator()
- for it.Next() {
- hash := it.Value().Bytes()
- p.lastReceivedHash = hash
-
- if blockPool.HasCommonHash(hash) {
- foundCommonHash = true
-
- break
- }
-
- blockPool.AddHash(hash, p)
- }
-
- if !foundCommonHash {
- p.FetchHashes()
- } else {
- peerlogger.Infof("Found common hash (%x...)\n", p.lastReceivedHash[0:4])
- p.doneFetchingHashes = true
- }
-
- case wire.MsgBlockTy:
- p.catchingUp = true
-
- blockPool := p.ethereum.blockPool
-
- it := msg.Data.NewIterator()
- for it.Next() {
- block := types.NewBlockFromRlpValue(it.Value())
- blockPool.Add(block, p)
-
- p.lastBlockReceived = time.Now()
- }
- case wire.MsgNewBlockTy:
- var (
- blockPool = p.ethereum.blockPool
- block = types.NewBlockFromRlpValue(msg.Data.Get(0))
- td = msg.Data.Get(1).BigInt()
- )
-
- if td.Cmp(blockPool.td) > 0 {
- p.ethereum.blockPool.AddNew(block, p)
- }
- }
-
- }
- }
- }
-
- p.Stop()
-}
-
-func (self *Peer) FetchBlocks(hashes [][]byte) {
- if len(hashes) > 0 {
- peerlogger.Debugf("Fetching blocks (%d)\n", len(hashes))
-
- self.QueueMessage(wire.NewMessage(wire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
- }
-}
-
-func (self *Peer) FetchHashes() bool {
- blockPool := self.ethereum.blockPool
-
- return blockPool.FetchHashes(self)
-}
-
-func (self *Peer) FetchingHashes() bool {
- return !self.doneFetchingHashes
-}
-
-// General update method
-func (self *Peer) update() {
- serviceTimer := time.NewTicker(100 * time.Millisecond)
-
-out:
- for {
- select {
- case <-serviceTimer.C:
- if self.IsCap("eth") {
- var (
- sinceBlock = time.Since(self.lastBlockReceived)
- )
-
- if sinceBlock > 5*time.Second {
- self.catchingUp = false
- }
- }
- case <-self.quit:
- break out
- }
- }
-
- serviceTimer.Stop()
-}
-
-func (p *Peer) Start() {
- peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
- servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
-
- if p.inbound {
- p.host, p.port = packAddr(peerHost, peerPort)
- } else {
- p.host, p.port = packAddr(servHost, servPort)
- }
-
- err := p.pushHandshake()
- if err != nil {
- peerlogger.Debugln("Peer can't send outbound version ack", err)
-
- p.Stop()
-
- return
- }
-
- go p.HandleOutbound()
- // Run the inbound handler in a new goroutine
- go p.HandleInbound()
- // Run the general update handler
- go p.update()
-
- // Wait a few seconds for startup and then ask for an initial ping
- time.Sleep(2 * time.Second)
- p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
- p.pingStartTime = time.Now()
-
-}
-
-func (p *Peer) Stop() {
- p.StopWithReason(DiscRequested)
-}
-
-func (p *Peer) StopWithReason(reason DiscReason) {
- if atomic.AddInt32(&p.disconnect, 1) != 1 {
- return
- }
-
- // Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
- p.ethereum.RemovePeer(p)
-
- close(p.quit)
- if atomic.LoadInt32(&p.connected) != 0 {
- p.writeMessage(wire.NewMessage(wire.MsgDiscTy, reason))
- p.conn.Close()
- }
-}
-
-func (p *Peer) peersMessage() *wire.Msg {
- outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
- // Serialise each peer
- for i, peer := range p.ethereum.InOutPeers() {
- // Don't return localhost as valid peer
- if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
- outPeers[i] = peer.RlpData()
- }
- }
-
- // Return the message to the peer with the known list of connected clients
- return wire.NewMessage(wire.MsgPeersTy, outPeers)
-}
-
-// Pushes the list of outbound peers to the client when requested
-func (p *Peer) pushPeers() {
- p.QueueMessage(p.peersMessage())
-}
-
-func (self *Peer) pushStatus() {
- msg := wire.NewMessage(wire.MsgStatusTy, []interface{}{
- uint32(ProtocolVersion),
- uint32(NetVersion),
- self.ethereum.ChainManager().Td(),
- self.ethereum.ChainManager().CurrentBlock().Hash(),
- self.ethereum.ChainManager().Genesis().Hash(),
- })
-
- self.QueueMessage(msg)
-}
-
-func (self *Peer) handleStatus(msg *wire.Msg) {
- c := msg.Data
-
- var (
- //protoVersion = c.Get(0).Uint()
- netVersion = c.Get(1).Uint()
- td = c.Get(2).BigInt()
- bestHash = c.Get(3).Bytes()
- genesis = c.Get(4).Bytes()
- )
-
- if bytes.Compare(self.ethereum.ChainManager().Genesis().Hash(), genesis) != 0 {
- loggerger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
- return
- }
-
- if netVersion != NetVersion {
- loggerger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
- return
- }
-
- /*
- if protoVersion != ProtocolVersion {
- loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
- return
- }
- */
-
- // Get the td and last hash
- self.td = td
- self.bestHash = bestHash
- self.lastReceivedHash = bestHash
-
- self.statusKnown = true
-
- // Compare the total TD with the blockchain TD. If remote is higher
- // fetch hashes from highest TD node.
- self.FetchHashes()
-
- loggerger.Infof("Peer is [eth] capable. (TD = %v ~ %x)", self.td, self.bestHash)
-
-}
-
-func (p *Peer) pushHandshake() error {
- pubkey := p.ethereum.KeyManager().PublicKey()
- msg := wire.NewMessage(wire.MsgHandshakeTy, []interface{}{
- P2PVersion, []byte(p.version), []interface{}{[]interface{}{"eth", ProtocolVersion}}, p.port, pubkey[1:],
- })
-
- p.QueueMessage(msg)
-
- return nil
-}
-
-func (p *Peer) handleHandshake(msg *wire.Msg) {
- c := msg.Data
-
- var (
- p2pVersion = c.Get(0).Uint()
- clientId = c.Get(1).Str()
- caps = c.Get(2)
- port = c.Get(3).Uint()
- pub = c.Get(4).Bytes()
- )
-
- // Check correctness of p2p protocol version
- if p2pVersion != P2PVersion {
- peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
- p.Stop()
- return
- }
-
- // Handle the pub key (validation, uniqueness)
- if len(pub) == 0 {
- peerlogger.Warnln("Pubkey required, not supplied in handshake.")
- p.Stop()
- return
- }
-
- // Self connect detection
- pubkey := p.ethereum.KeyManager().PublicKey()
- if bytes.Compare(pubkey[1:], pub) == 0 {
- p.Stop()
-
- return
- }
-
- // Check for blacklisting
- for _, pk := range p.ethereum.blacklist {
- if bytes.Compare(pk, pub) == 0 {
- peerlogger.Debugf("Blacklisted peer tried to connect (%x...)\n", pubkey[0:4])
- p.StopWithReason(DiscBadPeer)
-
- return
- }
- }
-
- usedPub := 0
- // This peer is already added to the peerlist so we expect to find a double pubkey at least once
- eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
- if bytes.Compare(pub, peer.pubkey) == 0 {
- usedPub++
- }
- })
-
- if usedPub > 0 {
- peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
- p.Stop()
- return
- }
- p.pubkey = pub
-
- // If this is an inbound connection send an ack back
- if p.inbound {
- p.port = uint16(port)
- }
-
- p.SetVersion(clientId)
-
- p.versionKnown = true
-
- p.ethereum.PushPeer(p)
- p.ethereum.eventMux.Post(PeerListEvent{p.ethereum.Peers()})
-
- p.protocolCaps = caps
-
- it := caps.NewIterator()
- var capsStrs []string
- for it.Next() {
- cap := it.Value().Get(0).Str()
- ver := it.Value().Get(1).Uint()
- switch cap {
- case "eth":
- if ver != ProtocolVersion {
- loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", ver)
- continue
- }
- p.pushStatus()
- }
-
- capsStrs = append(capsStrs, fmt.Sprintf("%s/%d", cap, ver))
- }
-
- peerlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
-
- peerlogger.Debugln(p)
-}
-
-func (self *Peer) IsCap(cap string) bool {
- capsIt := self.protocolCaps.NewIterator()
- for capsIt.Next() {
- if capsIt.Value().Str() == cap {
- return true
- }
- }
-
- return false
-}
-
-func (self *Peer) Caps() *ethutil.Value {
- return self.protocolCaps
-}
-
-func (p *Peer) String() string {
- var strBoundType string
- if p.inbound {
- strBoundType = "inbound"
- } else {
- strBoundType = "outbound"
- }
- var strConnectType string
- if atomic.LoadInt32(&p.disconnect) == 0 {
- strConnectType = "connected"
- } else {
- strConnectType = "disconnected"
- }
-
- return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version)
-
-}
-
-func (p *Peer) RlpData() []interface{} {
- return []interface{}{p.host, p.port, p.pubkey}
-}
-
-func packAddr(address, _port string) (host []byte, port uint16) {
- p, _ := strconv.Atoi(_port)
- port = uint16(p)
-
- h := net.ParseIP(address)
- if ip := h.To4(); ip != nil {
- host = []byte(ip)
- } else {
- host = []byte(h)
- }
-
- return
-}
-
-func unpackAddr(value *ethutil.Value, p uint64) string {
- host, _ := net.IP(value.Bytes()).MarshalText()
- prt := strconv.Itoa(int(p))
-
- return net.JoinHostPort(string(host), prt)
-}
diff --git a/pow/block.go b/pow/block.go
index 4759e19fb..62df2b5ff 100644
--- a/pow/block.go
+++ b/pow/block.go
@@ -3,7 +3,7 @@ package pow
import "math/big"
type Block interface {
- Diff() *big.Int
+ Difficulty() *big.Int
HashNoNonce() []byte
N() []byte
}
diff --git a/pow/ezp/pow.go b/pow/ezp/pow.go
index bfe3ea098..f9f27326f 100644
--- a/pow/ezp/pow.go
+++ b/pow/ezp/pow.go
@@ -35,7 +35,7 @@ func (pow *EasyPow) Turbo(on bool) {
func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce()
- diff := block.Diff()
+ diff := block.Difficulty()
i := int64(0)
start := time.Now().UnixNano()
t := time.Now()
@@ -59,7 +59,7 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
}
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
- if pow.verify(hash, diff, sha) {
+ if verify(hash, diff, sha) {
return sha
}
}
@@ -72,7 +72,11 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
return nil
}
-func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
+func (pow *EasyPow) Verify(block pow.Block) bool {
+ return Verify(block)
+}
+
+func verify(hash []byte, diff *big.Int, nonce []byte) bool {
sha := sha3.NewKeccak256()
d := append(hash, nonce...)
@@ -84,6 +88,6 @@ func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
return res.Cmp(verification) <= 0
}
-func (pow *EasyPow) Verify(block pow.Block) bool {
- return pow.verify(block.HashNoNonce(), block.Diff(), block.N())
+func Verify(block pow.Block) bool {
+ return verify(block.HashNoNonce(), block.Difficulty(), block.N())
}
diff --git a/ptrie/fullnode.go b/ptrie/fullnode.go
index 7a7f7d22d..d6b0745ec 100644
--- a/ptrie/fullnode.go
+++ b/ptrie/fullnode.go
@@ -1,5 +1,7 @@
package ptrie
+import "fmt"
+
type FullNode struct {
trie *Trie
nodes [17]Node
@@ -56,6 +58,11 @@ func (self *FullNode) RlpData() interface{} {
}
func (self *FullNode) set(k byte, value Node) {
+ if _, ok := value.(*ValueNode); ok && k != 16 {
+ fmt.Println(value, k)
+ panic(":(")
+ }
+
self.nodes[int(k)] = value
}
diff --git a/ptrie/trie.go b/ptrie/trie.go
index 9fe9ea52a..d8135f36c 100644
--- a/ptrie/trie.go
+++ b/ptrie/trie.go
@@ -19,7 +19,7 @@ func ParanoiaCheck(t1 *Trie, backend Backend) (bool, *Trie) {
t2.Update(it.Key, it.Value)
}
- return bytes.Compare(t2.Hash(), t1.Hash()) == 0, t2
+ return bytes.Equal(t2.Hash(), t1.Hash()), t2
}
type Trie struct {
@@ -49,14 +49,17 @@ func (self *Trie) Iterator() *Iterator {
return NewIterator(self)
}
+func (self *Trie) Copy() *Trie {
+ return New(self.roothash, self.cache.backend)
+}
+
// Legacy support
func (self *Trie) Root() []byte { return self.Hash() }
func (self *Trie) Hash() []byte {
var hash []byte
if self.root != nil {
- //hash = self.root.Hash().([]byte)
t := self.root.Hash()
- if byts, ok := t.([]byte); ok {
+ if byts, ok := t.([]byte); ok && len(byts) > 0 {
hash = byts
} else {
hash = crypto.Sha3(ethutil.Encode(self.root.RlpData()))
@@ -73,6 +76,9 @@ func (self *Trie) Hash() []byte {
return hash
}
func (self *Trie) Commit() {
+ self.mu.Lock()
+ defer self.mu.Unlock()
+
// Hash first
self.Hash()
@@ -81,10 +87,15 @@ func (self *Trie) Commit() {
// Reset should only be called if the trie has been hashed
func (self *Trie) Reset() {
+ self.mu.Lock()
+ defer self.mu.Unlock()
+
self.cache.Reset()
- revision := self.revisions.Remove(self.revisions.Back()).([]byte)
- self.roothash = revision
+ if self.revisions.Len() > 0 {
+ revision := self.revisions.Remove(self.revisions.Back()).([]byte)
+ self.roothash = revision
+ }
value := ethutil.NewValueFromBytes(self.cache.Get(self.roothash))
self.root = self.mknode(value)
}
@@ -173,7 +184,7 @@ func (self *Trie) insert(node Node, key []byte, value Node) Node {
return cpy
default:
- panic("Invalid node")
+ panic(fmt.Sprintf("%T: invalid node: %v", node, node))
}
}
@@ -274,6 +285,8 @@ func (self *Trie) delete(node Node, key []byte) Node {
func (self *Trie) mknode(value *ethutil.Value) Node {
l := value.Len()
switch l {
+ case 0:
+ return nil
case 2:
return NewShortNode(self, trie.CompactDecode(string(value.Get(0).Bytes())), self.mknode(value.Get(1)))
case 17:
diff --git a/ptrie/trie_test.go b/ptrie/trie_test.go
index 5b1c64140..63a8ed36e 100644
--- a/ptrie/trie_test.go
+++ b/ptrie/trie_test.go
@@ -141,7 +141,7 @@ func TestReplication(t *testing.T) {
trie2 := New(trie.roothash, trie.cache.backend)
if string(trie2.GetString("horse")) != "stallion" {
- t.Error("expected to have harse => stallion")
+ t.Error("expected to have horse => stallion")
}
hash := trie2.Hash()
diff --git a/state/dump.go b/state/dump.go
index c1f5ecf3a..40ecff50c 100644
--- a/state/dump.go
+++ b/state/dump.go
@@ -22,22 +22,23 @@ type World struct {
func (self *StateDB) Dump() []byte {
world := World{
- Root: ethutil.Bytes2Hex(self.Trie.GetRoot()),
+ Root: ethutil.Bytes2Hex(self.trie.Root()),
Accounts: make(map[string]Account),
}
- self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) {
- stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes())
+ it := self.trie.Iterator()
+ for it.Next() {
+ stateObject := NewStateObjectFromBytes(it.Key, it.Value)
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.Nonce, Root: ethutil.Bytes2Hex(stateObject.Root()), CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string)
- stateObject.EachStorage(func(key string, value *ethutil.Value) {
- value.Decode()
- account.Storage[ethutil.Bytes2Hex([]byte(key))] = ethutil.Bytes2Hex(value.Bytes())
- })
- world.Accounts[ethutil.Bytes2Hex([]byte(key))] = account
- })
+ storageIt := stateObject.State.trie.Iterator()
+ for storageIt.Next() {
+ account.Storage[ethutil.Bytes2Hex(it.Key)] = ethutil.Bytes2Hex(it.Value)
+ }
+ world.Accounts[ethutil.Bytes2Hex(it.Key)] = account
+ }
json, err := json.MarshalIndent(world, "", " ")
if err != nil {
@@ -50,7 +51,8 @@ func (self *StateDB) Dump() []byte {
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce)
- self.EachStorage(func(addr string, value *ethutil.Value) {
- fmt.Printf("%x %x\n", addr, value.Bytes())
- })
+ it := self.State.trie.Iterator()
+ for it.Next() {
+ fmt.Printf("%x %x\n", it.Key, it.Value)
+ }
}
diff --git a/state/state_object.go b/state/state_object.go
index b8af4e702..420ad9757 100644
--- a/state/state_object.go
+++ b/state/state_object.go
@@ -6,7 +6,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
- "github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/ptrie"
)
type Code []byte
@@ -62,7 +62,7 @@ func NewStateObject(addr []byte) *StateObject {
address := ethutil.Address(addr)
object := &StateObject{address: address, balance: new(big.Int), gasPool: new(big.Int)}
- object.State = New(trie.New(ethutil.Config.Db, ""))
+ object.State = New(ptrie.New(nil, ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, ""))
object.storage = make(Storage)
object.gasPool = new(big.Int)
@@ -72,7 +72,7 @@ func NewStateObject(addr []byte) *StateObject {
func NewContract(address []byte, balance *big.Int, root []byte) *StateObject {
contract := NewStateObject(address)
contract.balance = balance
- contract.State = New(trie.New(ethutil.Config.Db, string(root)))
+ contract.State = New(ptrie.New(nil, ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, string(root)))
return contract
}
@@ -89,12 +89,12 @@ func (self *StateObject) MarkForDeletion() {
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.balance)
}
-func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
- return ethutil.NewValueFromBytes([]byte(c.State.Trie.Get(string(addr))))
+func (c *StateObject) getAddr(addr []byte) *ethutil.Value {
+ return ethutil.NewValueFromBytes([]byte(c.State.trie.Get(addr)))
}
-func (c *StateObject) SetAddr(addr []byte, value interface{}) {
- c.State.Trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
+func (c *StateObject) setAddr(addr []byte, value interface{}) {
+ c.State.trie.Update(addr, ethutil.Encode(value))
}
func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
@@ -113,7 +113,7 @@ func (self *StateObject) GetState(k []byte) *ethutil.Value {
value := self.storage[string(key)]
if value == nil {
- value = self.GetAddr(key)
+ value = self.getAddr(key)
if !value.IsNil() {
self.storage[string(key)] = value
@@ -128,6 +128,7 @@ func (self *StateObject) SetState(k []byte, value *ethutil.Value) {
self.storage[string(key)] = value.Copy()
}
+/*
// Iterate over each storage address and yield callback
func (self *StateObject) EachStorage(cb trie.EachCallback) {
// First loop over the uncommit/cached values in storage
@@ -145,23 +146,26 @@ func (self *StateObject) EachStorage(cb trie.EachCallback) {
}
})
}
+*/
func (self *StateObject) Sync() {
for key, value := range self.storage {
if value.Len() == 0 {
- self.State.Trie.Delete(string(key))
+ self.State.trie.Delete([]byte(key))
continue
}
- self.SetAddr([]byte(key), value)
+ self.setAddr([]byte(key), value)
}
- valid, t2 := trie.ParanoiaCheck(self.State.Trie)
- if !valid {
- statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Root(), t2.GetRoot())
+ /*
+ valid, t2 := ptrie.ParanoiaCheck(self.State.trie, ethutil.Config.Db)
+ if !valid {
+ statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Root(), t2.Root())
- self.State.Trie = t2
- }
+ self.State.trie = t2
+ }
+ */
}
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
@@ -276,8 +280,12 @@ func (c *StateObject) Init() Code {
return c.InitCode
}
+func (self *StateObject) Trie() *ptrie.Trie {
+ return self.State.trie
+}
+
func (self *StateObject) Root() []byte {
- return self.State.Trie.GetRoot()
+ return self.Trie().Root()
}
func (self *StateObject) SetCode(code []byte) {
@@ -302,7 +310,7 @@ func (c *StateObject) RlpDecode(data []byte) {
c.Nonce = decoder.Get(0).Uint()
c.balance = decoder.Get(1).BigInt()
- c.State = New(trie.New(ethutil.Config.Db, decoder.Get(2).Interface()))
+ c.State = New(ptrie.New(decoder.Get(2).Bytes(), ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, decoder.Get(2).Interface()))
c.storage = make(map[string]*ethutil.Value)
c.gasPool = new(big.Int)
diff --git a/state/state.go b/state/statedb.go
index f77da72f0..6a11fc328 100644
--- a/state/state.go
+++ b/state/statedb.go
@@ -1,11 +1,12 @@
package state
import (
+ "bytes"
"math/big"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/ptrie"
)
var statelogger = logger.NewLogger("STATE")
@@ -16,8 +17,8 @@ var statelogger = logger.NewLogger("STATE")
// * Contracts
// * Accounts
type StateDB struct {
- // The trie for this structure
- Trie *trie.Trie
+ //Trie *trie.Trie
+ trie *ptrie.Trie
stateObjects map[string]*StateObject
@@ -29,8 +30,8 @@ type StateDB struct {
}
// Create a new state from a given trie
-func New(trie *trie.Trie) *StateDB {
- return &StateDB{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest(), refund: make(map[string]*big.Int)}
+func New(trie *ptrie.Trie) *StateDB {
+ return &StateDB{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest(), refund: make(map[string]*big.Int)}
}
func (self *StateDB) EmptyLogs() {
@@ -140,12 +141,12 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
ethutil.Config.Db.Put(stateObject.CodeHash(), stateObject.Code)
}
- self.Trie.Update(string(addr), string(stateObject.RlpEncode()))
+ self.trie.Update(addr, stateObject.RlpEncode())
}
// Delete the given state object and delete it from the state trie
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
- self.Trie.Delete(string(stateObject.Address()))
+ self.trie.Delete(stateObject.Address())
delete(self.stateObjects, string(stateObject.Address()))
}
@@ -159,7 +160,7 @@ func (self *StateDB) GetStateObject(addr []byte) *StateObject {
return stateObject
}
- data := self.Trie.Get(string(addr))
+ data := self.trie.Get(addr)
if len(data) == 0 {
return nil
}
@@ -206,12 +207,12 @@ func (self *StateDB) GetAccount(addr []byte) *StateObject {
//
func (s *StateDB) Cmp(other *StateDB) bool {
- return s.Trie.Cmp(other.Trie)
+ return bytes.Equal(s.trie.Root(), other.trie.Root())
}
func (self *StateDB) Copy() *StateDB {
- if self.Trie != nil {
- state := New(self.Trie.Copy())
+ if self.trie != nil {
+ state := New(self.trie.Copy())
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
@@ -235,19 +236,19 @@ func (self *StateDB) Set(state *StateDB) {
panic("Tried setting 'state' to nil through 'Set'")
}
- self.Trie = state.Trie
+ self.trie = state.trie
self.stateObjects = state.stateObjects
self.refund = state.refund
self.logs = state.logs
}
func (s *StateDB) Root() []byte {
- return s.Trie.GetRoot()
+ return s.trie.Root()
}
// Resets the trie and all siblings
func (s *StateDB) Reset() {
- s.Trie.Undo()
+ s.trie.Reset()
// Reset all nested states
for _, stateObject := range s.stateObjects {
@@ -272,7 +273,7 @@ func (s *StateDB) Sync() {
stateObject.State.Sync()
}
- s.Trie.Sync()
+ s.trie.Commit()
s.Empty()
}
@@ -304,11 +305,11 @@ func (self *StateDB) Update(gasUsed *big.Int) {
// FIXME trie delete is broken
if deleted {
- valid, t2 := trie.ParanoiaCheck(self.Trie)
+ valid, t2 := ptrie.ParanoiaCheck(self.trie, ethutil.Config.Db)
if !valid {
- statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.Trie.GetRoot(), t2.GetRoot())
+ statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root(), t2.Root())
- self.Trie = t2
+ self.trie = t2
}
}
}
diff --git a/ui/qt/qwhisper/message.go b/ui/qt/qwhisper/message.go
new file mode 100644
index 000000000..c87647399
--- /dev/null
+++ b/ui/qt/qwhisper/message.go
@@ -0,0 +1,23 @@
+package qwhisper
+
+import (
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/whisper"
+)
+
+type Message struct {
+ ref *whisper.Message
+ Flags int32
+ Payload string
+ From string
+}
+
+func ToQMessage(msg *whisper.Message) *Message {
+ return &Message{
+ ref: msg,
+ Flags: int32(msg.Flags),
+ Payload: ethutil.Bytes2Hex(msg.Payload),
+ From: ethutil.Bytes2Hex(crypto.FromECDSAPub(msg.Recover())),
+ }
+}
diff --git a/ui/qt/qwhisper/watch.go b/ui/qt/qwhisper/watch.go
new file mode 100644
index 000000000..0ccedc719
--- /dev/null
+++ b/ui/qt/qwhisper/watch.go
@@ -0,0 +1,13 @@
+package qwhisper
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+type Watch struct {
+}
+
+func (self *Watch) Arrived(v unsafe.Pointer) {
+ fmt.Println(v)
+}
diff --git a/ui/qt/qwhisper/whisper.go b/ui/qt/qwhisper/whisper.go
index bed23c8a7..0627acd29 100644
--- a/ui/qt/qwhisper/whisper.go
+++ b/ui/qt/qwhisper/whisper.go
@@ -1,11 +1,13 @@
package qwhisper
import (
+ "fmt"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/whisper"
+ "gopkg.in/qml.v1"
)
func fromHex(s string) []byte {
@@ -18,25 +20,40 @@ func toHex(b []byte) string { return "0x" + ethutil.Bytes2Hex(b) }
type Whisper struct {
*whisper.Whisper
+ view qml.Object
+
+ watches map[int]*Watch
}
func New(w *whisper.Whisper) *Whisper {
- return &Whisper{w}
+ return &Whisper{w, nil, make(map[int]*Watch)}
+}
+
+func (self *Whisper) SetView(view qml.Object) {
+ self.view = view
}
-func (self *Whisper) Post(data string, pow, ttl uint32, to, from string) {
- msg := whisper.NewMessage(fromHex(data))
- envelope, err := msg.Seal(time.Duration(pow), whisper.Opts{
- Ttl: time.Duration(ttl),
- To: crypto.ToECDSAPub(fromHex(to)),
- From: crypto.ToECDSA(fromHex(from)),
+func (self *Whisper) Post(payload []string, to, from string, topics []string, priority, ttl uint32) {
+ var data []byte
+ for _, d := range payload {
+ data = append(data, fromHex(d)...)
+ }
+
+ msg := whisper.NewMessage(data)
+ envelope, err := msg.Seal(time.Duration(priority*100000), whisper.Opts{
+ Ttl: time.Duration(ttl),
+ To: crypto.ToECDSAPub(fromHex(to)),
+ From: crypto.ToECDSA(fromHex(from)),
+ Topics: whisper.TopicsFromString(topics...),
})
if err != nil {
+ fmt.Println(err)
// handle error
return
}
if err := self.Whisper.Send(envelope); err != nil {
+ fmt.Println(err)
// handle error
return
}
@@ -46,16 +63,23 @@ func (self *Whisper) NewIdentity() string {
return toHex(self.Whisper.NewIdentity().D.Bytes())
}
-func (self *Whisper) HasIdentify(key string) bool {
+func (self *Whisper) HasIdentity(key string) bool {
return self.Whisper.HasIdentity(crypto.ToECDSA(fromHex(key)))
}
-func (self *Whisper) Watch(opts map[string]interface{}) {
+func (self *Whisper) Watch(opts map[string]interface{}, view *qml.Common) int {
filter := filterFromMap(opts)
+ var i int
filter.Fn = func(msg *whisper.Message) {
- // TODO POST TO QT WINDOW
+ if view != nil {
+ view.Call("onShhMessage", ToQMessage(msg), i)
+ }
}
- self.Whisper.Watch(filter)
+
+ i = self.Whisper.Watch(filter)
+ self.watches[i] = &Watch{}
+
+ return i
}
func filterFromMap(opts map[string]interface{}) (f whisper.Filter) {
@@ -65,6 +89,11 @@ func filterFromMap(opts map[string]interface{}) (f whisper.Filter) {
if from, ok := opts["from"].(string); ok {
f.From = crypto.ToECDSAPub(fromHex(from))
}
+ if topicList, ok := opts["topics"].(*qml.List); ok {
+ var topics []string
+ topicList.Convert(&topics)
+ f.Topics = whisper.TopicsFromString(topics...)
+ }
return
}
diff --git a/ui/qt/qwhisper/whisper_test.go b/ui/qt/qwhisper/whisper_test.go
new file mode 100644
index 000000000..efa4e6238
--- /dev/null
+++ b/ui/qt/qwhisper/whisper_test.go
@@ -0,0 +1,15 @@
+package qwhisper
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/whisper"
+)
+
+func TestHasIdentity(t *testing.T) {
+ qw := New(whisper.New())
+ id := qw.NewIdentity()
+ if !qw.HasIdentity(id) {
+ t.Error("expected to have identity")
+ }
+}
diff --git a/whisper/doc.go b/whisper/doc.go
new file mode 100644
index 000000000..986df8fb9
--- /dev/null
+++ b/whisper/doc.go
@@ -0,0 +1,16 @@
+/*
+Package whisper implements the Whisper PoC-1.
+
+(https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec)
+
+Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
+As such it may be likened and compared to both, not dissimilar to the
+matter/energy duality (apologies to physicists for the blatant abuse of a
+fundamental and beautiful natural principle).
+
+Whisper is a pure identity-based messaging system. Whisper provides a low-level
+(non-application-specific) but easily-accessible API without being based upon
+or prejudiced by the low-level hardware attributes and characteristics,
+particularly the notion of singular endpoints.
+*/
+package whisper
diff --git a/whisper/envelope.go b/whisper/envelope.go
index 683e88128..066e20f6a 100644
--- a/whisper/envelope.go
+++ b/whisper/envelope.go
@@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/obscuren/ecies"
)
const (
@@ -73,10 +74,15 @@ func (self *Envelope) Open(prv *ecdsa.PrivateKey) (msg *Message, err error) {
message.Flags = data[0]
message.Signature = data[1:66]
}
- message.Payload = data[dataStart:]
+
+ payload := data[dataStart:]
if prv != nil {
- message.Payload, err = crypto.Decrypt(prv, message.Payload)
- if err != nil {
+ message.Payload, err = crypto.Decrypt(prv, payload)
+ switch err {
+ case ecies.ErrInvalidPublicKey: // Payload isn't encrypted
+ message.Payload = payload
+ return &message, err
+ default:
return nil, fmt.Errorf("unable to open envelope. Decrypt failed: %v", err)
}
}
diff --git a/whisper/main.go b/whisper/main.go
index 2ee2f3ff1..edd5f7004 100644
--- a/whisper/main.go
+++ b/whisper/main.go
@@ -5,10 +5,8 @@ package main
import (
"fmt"
"log"
- "net"
"os"
- "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/whisper"
@@ -20,12 +18,12 @@ func main() {
pub, _ := secp256k1.GenerateKeyPair()
- whisper := whisper.New(&event.TypeMux{})
+ whisper := whisper.New()
srv := p2p.Server{
MaxPeers: 10,
Identity: p2p.NewSimpleClientIdentity("whisper-go", "1.0", "", string(pub)),
- ListenAddr: ":30303",
+ ListenAddr: ":30300",
NAT: p2p.UPNP(),
Protocols: []p2p.Protocol{whisper.Protocol()},
@@ -35,13 +33,5 @@ func main() {
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 {}
}
diff --git a/whisper/util.go b/whisper/util.go
index abef1d667..7a222395f 100644
--- a/whisper/util.go
+++ b/whisper/util.go
@@ -18,10 +18,19 @@ func Topics(data [][]byte) [][]byte {
return d
}
-func TopicsFromString(data []string) [][]byte {
+func TopicsFromString(data ...string) [][]byte {
d := make([][]byte, len(data))
for i, str := range data {
d[i] = hashTopic([]byte(str))
}
return d
}
+
+func bytesToMap(s [][]byte) map[string]struct{} {
+ m := make(map[string]struct{})
+ for _, topic := range s {
+ m[string(topic)] = struct{}{}
+ }
+
+ return m
+}
diff --git a/whisper/whisper.go b/whisper/whisper.go
index 356debd1c..ffcdd7d40 100644
--- a/whisper/whisper.go
+++ b/whisper/whisper.go
@@ -4,13 +4,14 @@ import (
"bytes"
"crypto/ecdsa"
"errors"
- "fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event/filter"
+ "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
+ "github.com/obscuren/ecies"
"gopkg.in/fatih/set.v0"
)
@@ -47,6 +48,8 @@ type MessageEvent struct {
const DefaultTtl = 50 * time.Second
+var wlogger = logger.NewLogger("SHH")
+
type Whisper struct {
protocol p2p.Protocol
filters *filter.Filters
@@ -68,17 +71,6 @@ func New() *Whisper {
quit: make(chan struct{}),
}
whisper.filters.Start()
- go whisper.update()
-
- // XXX TODO REMOVE TESTING CODE
- msg := NewMessage([]byte(fmt.Sprintf("Hello world. This is whisper-go. Incase you're wondering; the time is %v", time.Now())))
- envelope, _ := msg.Seal(DefaultPow, Opts{
- Ttl: DefaultTtl,
- })
- if err := whisper.Send(envelope); err != nil {
- fmt.Println(err)
- }
- // XXX TODO REMOVE TESTING CODE
// p2p whisper sub protocol handler
whisper.protocol = p2p.Protocol{
@@ -91,6 +83,11 @@ func New() *Whisper {
return whisper
}
+func (self *Whisper) Start() {
+ wlogger.Infoln("Whisper started")
+ go self.update()
+}
+
func (self *Whisper) Stop() {
close(self.quit)
}
@@ -122,6 +119,7 @@ func (self *Whisper) Watch(opts Filter) int {
return self.filters.Install(filter.Generic{
Str1: string(crypto.FromECDSA(opts.To)),
Str2: string(crypto.FromECDSAPub(opts.From)),
+ Data: bytesToMap(opts.Topics),
Fn: func(data interface{}) {
opts.Fn(data.(*Message))
},
@@ -177,7 +175,7 @@ func (self *Whisper) add(envelope *Envelope) error {
if !self.expiry[envelope.Expiry].Has(hash) {
self.expiry[envelope.Expiry].Add(hash)
- self.postEvent(envelope)
+ go self.postEvent(envelope)
}
return nil
@@ -230,13 +228,14 @@ func (self *Whisper) envelopes() (envelopes []*Envelope) {
func (self *Whisper) postEvent(envelope *Envelope) {
for _, key := range self.keys {
- if message, err := envelope.Open(key); err == nil {
+ if message, err := envelope.Open(key); err == nil || (err != nil && err == ecies.ErrInvalidPublicKey) {
// Create a custom filter?
self.filters.Notify(filter.Generic{
Str1: string(crypto.FromECDSA(key)), Str2: string(crypto.FromECDSAPub(message.Recover())),
+ Data: bytesToMap(envelope.Topics),
}, message)
} else {
- fmt.Println(err)
+ wlogger.Infoln(err)
}
}
}
diff --git a/wire/README.md b/wire/README.md
deleted file mode 100644
index 7f63688b3..000000000
--- a/wire/README.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# ethwire
-
-The ethwire package contains the ethereum wire protocol. The ethwire
-package is required to write and read from the ethereum network.
-
-# Installation
-
-`go get github.com/ethereum/ethwire-go`
-
-# Messaging overview
-
-The Ethereum Wire protocol defines the communication between the nodes
-running Ethereum. Further reader reading can be done on the
-[Wiki](http://wiki.ethereum.org/index.php/Wire_Protocol).
-
-# Reading Messages
-
-```go
-// Read and validate the next eth message from the provided connection.
-// returns a error message with the details.
-msg, err := ethwire.ReadMessage(conn)
-if err != nil {
- // Handle error
-}
-```
-
-# Writing Messages
-
-```go
-// Constructs a message which can be interpreted by the eth network.
-// Write the inventory to network
-err := ethwire.WriteMessage(conn, &Msg{
- Type: ethwire.MsgInvTy,
- Data : []interface{}{...},
-})
-```
diff --git a/wire/client_identity.go b/wire/client_identity.go
deleted file mode 100644
index 0a268024a..000000000
--- a/wire/client_identity.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package wire
-
-import (
- "fmt"
- "runtime"
-)
-
-// should be used in Peer handleHandshake, incorporate Caps, ProtocolVersion, Pubkey etc.
-type ClientIdentity interface {
- String() string
-}
-
-type SimpleClientIdentity struct {
- clientIdentifier string
- version string
- customIdentifier string
- os string
- implementation string
-}
-
-func NewSimpleClientIdentity(clientIdentifier string, version string, customIdentifier string) *SimpleClientIdentity {
- clientIdentity := &SimpleClientIdentity{
- clientIdentifier: clientIdentifier,
- version: version,
- customIdentifier: customIdentifier,
- os: runtime.GOOS,
- implementation: runtime.Version(),
- }
-
- return clientIdentity
-}
-
-func (c *SimpleClientIdentity) init() {
-}
-
-func (c *SimpleClientIdentity) String() string {
- var id string
- if len(c.customIdentifier) > 0 {
- id = "/" + c.customIdentifier
- }
-
- return fmt.Sprintf("%s/v%s%s/%s/%s",
- c.clientIdentifier,
- c.version,
- id,
- c.os,
- c.implementation)
-}
-
-func (c *SimpleClientIdentity) SetCustomIdentifier(customIdentifier string) {
- c.customIdentifier = customIdentifier
-}
-
-func (c *SimpleClientIdentity) GetCustomIdentifier() string {
- return c.customIdentifier
-}
diff --git a/wire/client_identity_test.go b/wire/client_identity_test.go
deleted file mode 100644
index c0e7a0159..000000000
--- a/wire/client_identity_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package wire
-
-import (
- "fmt"
- "runtime"
- "testing"
-)
-
-func TestClientIdentity(t *testing.T) {
- clientIdentity := NewSimpleClientIdentity("Ethereum(G)", "0.5.16", "test")
- clientString := clientIdentity.String()
- expected := fmt.Sprintf("Ethereum(G)/v0.5.16/test/%s/%s", runtime.GOOS, runtime.Version())
- if clientString != expected {
- t.Errorf("Expected clientIdentity to be %q, got %q", expected, clientString)
- }
- customIdentifier := clientIdentity.GetCustomIdentifier()
- if customIdentifier != "test" {
- t.Errorf("Expected clientIdentity.GetCustomIdentifier() to be 'test', got %q", customIdentifier)
- }
- clientIdentity.SetCustomIdentifier("test2")
- customIdentifier = clientIdentity.GetCustomIdentifier()
- if customIdentifier != "test2" {
- t.Errorf("Expected clientIdentity.GetCustomIdentifier() to be 'test2', got %q", customIdentifier)
- }
- clientString = clientIdentity.String()
- expected = fmt.Sprintf("Ethereum(G)/v0.5.16/test2/%s/%s", runtime.GOOS, runtime.Version())
- if clientString != expected {
- t.Errorf("Expected clientIdentity to be %q, got %q", expected, clientString)
- }
-}
diff --git a/wire/messages2.go b/wire/messages2.go
deleted file mode 100644
index acbd9e0d5..000000000
--- a/wire/messages2.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package wire
-
-import (
- "bytes"
- "errors"
- "fmt"
- "net"
- "time"
-
- "github.com/ethereum/go-ethereum/ethutil"
-)
-
-// The connection object allows you to set up a connection to the Ethereum network.
-// The Connection object takes care of all encoding and sending objects properly over
-// the network.
-type Connection struct {
- conn net.Conn
- nTimeout time.Duration
- pendingMessages Messages
-}
-
-// Create a new connection to the Ethereum network
-func New(conn net.Conn) *Connection {
- return &Connection{conn: conn, nTimeout: 500}
-}
-
-// Read, reads from the network. It will block until the next message is received.
-func (self *Connection) Read() *Msg {
- if len(self.pendingMessages) == 0 {
- self.readMessages()
- }
-
- ret := self.pendingMessages[0]
- self.pendingMessages = self.pendingMessages[1:]
-
- return ret
-
-}
-
-// Write to the Ethereum network specifying the type of the message and
-// the data. Data can be of type RlpEncodable or []interface{}. Returns
-// nil or if something went wrong an error.
-func (self *Connection) Write(typ MsgType, v ...interface{}) error {
- var pack []byte
-
- slice := [][]interface{}{[]interface{}{byte(typ)}}
- for _, value := range v {
- if encodable, ok := value.(ethutil.RlpEncodeDecode); ok {
- slice = append(slice, encodable.RlpValue())
- } else if raw, ok := value.([]interface{}); ok {
- slice = append(slice, raw)
- } else {
- panic(fmt.Sprintf("Unable to 'write' object of type %T", value))
- }
- }
-
- // Encode the type and the (RLP encoded) data for sending over the wire
- encoded := ethutil.NewValue(slice).Encode()
- payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
-
- // Write magic token and payload length (first 8 bytes)
- pack = append(MagicToken, payloadLength...)
- pack = append(pack, encoded...)
-
- // Write to the connection
- _, err := self.conn.Write(pack)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
- if len(data) == 0 {
- return nil, nil, true, nil
- }
-
- if len(data) <= 8 {
- return nil, remaining, false, errors.New("Invalid message")
- }
-
- // Check if the received 4 first bytes are the magic token
- if bytes.Compare(MagicToken, data[:4]) != 0 {
- return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
- }
-
- messageLength := ethutil.BytesToNumber(data[4:8])
- remaining = data[8+messageLength:]
- if int(messageLength) > len(data[8:]) {
- return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
- }
-
- message := data[8 : 8+messageLength]
- decoder := ethutil.NewValueFromBytes(message)
- // Type of message
- t := decoder.Get(0).Uint()
- // Actual data
- d := decoder.SliceFrom(1)
-
- msg = &Msg{
- Type: MsgType(t),
- Data: d,
- }
-
- return
-}
-
-// The basic message reader waits for data on the given connection, decoding
-// and doing a few sanity checks such as if there's a data type and
-// unmarhals the given data
-func (self *Connection) readMessages() (err error) {
- // The recovering function in case anything goes horribly wrong
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("wire.ReadMessage error: %v", r)
- }
- }()
-
- // Buff for writing network message to
- //buff := make([]byte, 1440)
- var buff []byte
- var totalBytes int
- for {
- // Give buffering some time
- self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond))
- // Create a new temporarily buffer
- b := make([]byte, 1440)
- // Wait for a message from this peer
- n, _ := self.conn.Read(b)
- if err != nil && n == 0 {
- if err.Error() != "EOF" {
- fmt.Println("err now", err)
- return err
- } else {
- break
- }
-
- // Messages can't be empty
- } else if n == 0 {
- break
- }
-
- buff = append(buff, b[:n]...)
- totalBytes += n
- }
-
- // Reslice buffer
- buff = buff[:totalBytes]
- msg, remaining, done, err := self.readMessage(buff)
- for ; done != true; msg, remaining, done, err = self.readMessage(remaining) {
- //log.Println("rx", msg)
-
- if msg != nil {
- self.pendingMessages = append(self.pendingMessages, msg)
- }
- }
-
- return
-}
-
-func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
- if len(data) == 0 {
- return nil, nil, true, nil
- }
-
- if len(data) <= 8 {
- return nil, remaining, false, errors.New("Invalid message")
- }
-
- // Check if the received 4 first bytes are the magic token
- if bytes.Compare(MagicToken, data[:4]) != 0 {
- return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
- }
-
- messageLength := ethutil.BytesToNumber(data[4:8])
- remaining = data[8+messageLength:]
- if int(messageLength) > len(data[8:]) {
- return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
- }
-
- message := data[8 : 8+messageLength]
- decoder := ethutil.NewValueFromBytes(message)
- // Type of message
- t := decoder.Get(0).Uint()
- // Actual data
- d := decoder.SliceFrom(1)
-
- msg = &Msg{
- Type: MsgType(t),
- Data: d,
- }
-
- return
-}
-
-func bufferedRead(conn net.Conn) ([]byte, error) {
- return nil, nil
-}
diff --git a/wire/messaging.go b/wire/messaging.go
deleted file mode 100644
index 9c6cb5944..000000000
--- a/wire/messaging.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Package wire provides low level access to the Ethereum network and allows
-// you to broadcast data over the network.
-package wire
-
-import (
- "bytes"
- "fmt"
- "net"
- "time"
-
- "github.com/ethereum/go-ethereum/ethutil"
-)
-
-// Connection interface describing the methods required to implement the wire protocol.
-type Conn interface {
- Write(typ MsgType, v ...interface{}) error
- Read() *Msg
-}
-
-// The magic token which should be the first 4 bytes of every message and can be used as separator between messages.
-var MagicToken = []byte{34, 64, 8, 145}
-
-type MsgType byte
-
-const (
- // Values are given explicitly instead of by iota because these values are
- // defined by the wire protocol spec; it is easier for humans to ensure
- // correctness when values are explicit.
- MsgHandshakeTy = 0x00
- MsgDiscTy = 0x01
- MsgPingTy = 0x02
- MsgPongTy = 0x03
- MsgGetPeersTy = 0x04
- MsgPeersTy = 0x05
-
- MsgStatusTy = 0x10
- MsgTxTy = 0x12
- MsgGetBlockHashesTy = 0x13
- MsgBlockHashesTy = 0x14
- MsgGetBlocksTy = 0x15
- MsgBlockTy = 0x16
- MsgNewBlockTy = 0x17
-)
-
-var msgTypeToString = map[MsgType]string{
- MsgHandshakeTy: "Handshake",
- MsgDiscTy: "Disconnect",
- MsgPingTy: "Ping",
- MsgPongTy: "Pong",
- MsgGetPeersTy: "Get peers",
- MsgStatusTy: "Status",
- MsgPeersTy: "Peers",
- MsgTxTy: "Transactions",
- MsgBlockTy: "Blocks",
- //MsgGetTxsTy: "Get Txs",
- MsgGetBlockHashesTy: "Get block hashes",
- MsgBlockHashesTy: "Block hashes",
- MsgGetBlocksTy: "Get blocks",
-}
-
-func (mt MsgType) String() string {
- return msgTypeToString[mt]
-}
-
-type Msg struct {
- Type MsgType // Specifies how the encoded data should be interpreted
- //Data []byte
- Data *ethutil.Value
-}
-
-func NewMessage(msgType MsgType, data interface{}) *Msg {
- return &Msg{
- Type: msgType,
- Data: ethutil.NewValue(data),
- }
-}
-
-type Messages []*Msg
-
-// The basic message reader waits for data on the given connection, decoding
-// and doing a few sanity checks such as if there's a data type and
-// unmarhals the given data
-func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
- // The recovering function in case anything goes horribly wrong
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("wire.ReadMessage error: %v", r)
- }
- }()
-
- var (
- buff []byte
- messages [][]byte
- msgLength int
- )
-
- for {
- // Give buffering some time
- conn.SetReadDeadline(time.Now().Add(5 * time.Millisecond))
- // Create a new temporarily buffer
- b := make([]byte, 1440)
- n, _ := conn.Read(b)
- if err != nil && n == 0 {
- if err.Error() != "EOF" {
- fmt.Println("err now", err)
- return nil, err
- } else {
- break
- }
- }
-
- if n == 0 && len(buff) == 0 {
- // If there's nothing on the wire wait for a bit
- time.Sleep(200 * time.Millisecond)
-
- continue
- }
-
- buff = append(buff, b[:n]...)
- if msgLength == 0 {
- // Check if the received 4 first bytes are the magic token
- if bytes.Compare(MagicToken, buff[:4]) != 0 {
- return nil, fmt.Errorf("MagicToken mismatch. Received %v", buff[:4])
- }
-
- // Read the length of the message
- msgLength = int(ethutil.BytesToNumber(buff[4:8]))
-
- // Remove the token and length
- buff = buff[8:]
- }
-
- if len(buff) >= msgLength {
- messages = append(messages, buff[:msgLength])
- buff = buff[msgLength:]
- msgLength = 0
-
- if len(buff) == 0 {
- break
- }
- }
- }
-
- for _, m := range messages {
- decoder := ethutil.NewValueFromBytes(m)
- // Type of message
- t := decoder.Get(0).Uint()
- // Actual data
- d := decoder.SliceFrom(1)
-
- msgs = append(msgs, &Msg{Type: MsgType(t), Data: d})
- }
-
- return
-}
-
-// The basic message writer takes care of writing data over the given
-// connection and does some basic error checking
-func WriteMessage(conn net.Conn, msg *Msg) error {
- var pack []byte
-
- // Encode the type and the (RLP encoded) data for sending over the wire
- encoded := ethutil.NewValue(append([]interface{}{byte(msg.Type)}, msg.Data.Slice()...)).Encode()
- payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
-
- // Write magic token and payload length (first 8 bytes)
- pack = append(MagicToken, payloadLength...)
- pack = append(pack, encoded...)
- //fmt.Printf("payload %v (%v) %q\n", msg.Type, conn.RemoteAddr(), encoded)
-
- // Write to the connection
- _, err := conn.Write(pack)
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/xeth/hexface.go b/xeth/hexface.go
index bfd2dddd9..c3d8cef86 100644
--- a/xeth/hexface.go
+++ b/xeth/hexface.go
@@ -3,7 +3,6 @@ package xeth
import (
"bytes"
"encoding/json"
- "sync/atomic"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
@@ -63,12 +62,8 @@ func (self *JSXEth) PeerCount() int {
func (self *JSXEth) Peers() []JSPeer {
var peers []JSPeer
- for peer := self.obj.Peers().Front(); peer != nil; peer = peer.Next() {
- p := peer.Value.(core.Peer)
- // we only want connected peers
- if atomic.LoadInt32(p.Connected()) != 0 {
- peers = append(peers, *NewJSPeer(p))
- }
+ for _, peer := range self.obj.Peers() {
+ peers = append(peers, *NewJSPeer(peer))
}
return peers
@@ -143,10 +138,10 @@ type KeyVal struct {
func (self *JSXEth) EachStorage(addr string) string {
var values []KeyVal
object := self.World().SafeGet(ethutil.Hex2Bytes(addr))
- object.EachStorage(func(name string, value *ethutil.Value) {
- value.Decode()
- values = append(values, KeyVal{ethutil.Bytes2Hex([]byte(name)), ethutil.Bytes2Hex(value.Bytes())})
- })
+ it := object.Trie().Iterator()
+ for it.Next() {
+ values = append(values, KeyVal{ethutil.Bytes2Hex(it.Key), ethutil.Bytes2Hex(it.Value)})
+ }
valuesJson, err := json.Marshal(values)
if err != nil {
diff --git a/xeth/js_types.go b/xeth/js_types.go
index 62867d6a9..4bb1f4e7d 100644
--- a/xeth/js_types.go
+++ b/xeth/js_types.go
@@ -1,14 +1,13 @@
package xeth
import (
- "fmt"
- "strconv"
"strings"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/state"
)
@@ -43,21 +42,21 @@ func NewJSBlock(block *types.Block) *JSBlock {
}
txlist := ethutil.NewList(ptxs)
- puncles := make([]*JSBlock, len(block.Uncles))
- for i, uncle := range block.Uncles {
- puncles[i] = NewJSBlock(uncle)
+ puncles := make([]*JSBlock, len(block.Uncles()))
+ for i, uncle := range block.Uncles() {
+ puncles[i] = NewJSBlock(types.NewBlockWithHeader(uncle))
}
ulist := ethutil.NewList(puncles)
return &JSBlock{
ref: block, Size: block.Size().String(),
- Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(),
- GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()),
+ Number: int(block.NumberU64()), GasUsed: block.GasUsed().String(),
+ GasLimit: block.GasLimit().String(), Hash: ethutil.Bytes2Hex(block.Hash()),
Transactions: txlist, Uncles: ulist,
- Time: block.Time,
- Coinbase: ethutil.Bytes2Hex(block.Coinbase),
- PrevHash: ethutil.Bytes2Hex(block.PrevHash),
- Bloom: ethutil.Bytes2Hex(block.LogsBloom),
+ Time: block.Time(),
+ Coinbase: ethutil.Bytes2Hex(block.Coinbase()),
+ PrevHash: ethutil.Bytes2Hex(block.ParentHash()),
+ Bloom: ethutil.Bytes2Hex(block.Bloom()),
Raw: block.String(),
}
}
@@ -71,7 +70,7 @@ func (self *JSBlock) ToString() string {
}
func (self *JSBlock) GetTransaction(hash string) *JSTransaction {
- tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash))
+ tx := self.ref.Transaction(ethutil.Hex2Bytes(hash))
if tx == nil {
return nil
}
@@ -101,7 +100,7 @@ func NewJSTx(tx *types.Transaction, state *state.StateDB) *JSTransaction {
if receiver == "0000000000000000000000000000000000000000" {
receiver = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
}
- sender := ethutil.Bytes2Hex(tx.Sender())
+ sender := ethutil.Bytes2Hex(tx.From())
createsContract := core.MessageCreatesContract(tx)
var data string
@@ -155,38 +154,36 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *
// Peer interface exposed to QML
type JSPeer struct {
- ref *core.Peer
- Inbound bool `json:"isInbound"`
- LastSend int64 `json:"lastSend"`
- LastPong int64 `json:"lastPong"`
- Ip string `json:"ip"`
- Port int `json:"port"`
- Version string `json:"version"`
- LastResponse string `json:"lastResponse"`
- Latency string `json:"latency"`
- Caps string `json:"caps"`
-}
-
-func NewJSPeer(peer core.Peer) *JSPeer {
- if peer == nil {
- return nil
- }
-
- var ip []string
- for _, i := range peer.Host() {
- ip = append(ip, strconv.Itoa(int(i)))
- }
- ipAddress := strings.Join(ip, ".")
-
- var caps []string
- capsIt := peer.Caps().NewIterator()
- for capsIt.Next() {
- cap := capsIt.Value().Get(0).Str()
- ver := capsIt.Value().Get(1).Uint()
- caps = append(caps, fmt.Sprintf("%s/%d", cap, ver))
- }
-
- return &JSPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime(), Caps: "[" + strings.Join(caps, ", ") + "]"}
+ ref *p2p.Peer
+ // Inbound bool `json:"isInbound"`
+ // LastSend int64 `json:"lastSend"`
+ // LastPong int64 `json:"lastPong"`
+ // Ip string `json:"ip"`
+ // Port int `json:"port"`
+ // Version string `json:"version"`
+ // LastResponse string `json:"lastResponse"`
+ // Latency string `json:"latency"`
+ // Caps string `json:"caps"`
+}
+
+func NewJSPeer(peer *p2p.Peer) *JSPeer {
+
+ // var ip []string
+ // for _, i := range peer.Host() {
+ // ip = append(ip, strconv.Itoa(int(i)))
+ // }
+ // ipAddress := strings.Join(ip, ".")
+
+ // var caps []string
+ // capsIt := peer.Caps().NewIterator()
+ // for capsIt.Next() {
+ // cap := capsIt.Value().Get(0).Str()
+ // ver := capsIt.Value().Get(1).Uint()
+ // caps = append(caps, fmt.Sprintf("%s/%d", cap, ver))
+ // }
+
+ return &JSPeer{ref: peer}
+ // return &JSPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime(), Caps: "[" + strings.Join(caps, ", ") + "]"}
}
type JSReceipt struct {
diff --git a/xeth/pipe.go b/xeth/pipe.go
index 06820cc86..775d5cfc5 100644
--- a/xeth/pipe.go
+++ b/xeth/pipe.go
@@ -140,7 +140,7 @@ func (self *XEth) Transact(key *crypto.KeyPair, to []byte, value, gas, price *et
// Do some pre processing for our "pre" events and hooks
block := self.chainManager.NewBlock(key.Address())
coinbase := state.GetStateObject(key.Address())
- coinbase.SetGasPool(block.GasLimit)
+ coinbase.SetGasPool(block.GasLimit())
self.blockManager.ApplyTransactions(coinbase, state, block, types.Transactions{tx}, true)
err := self.obj.TxPool().Add(tx)
diff --git a/xeth/vm_env.go b/xeth/vm_env.go
index 7fb674a94..7633e0640 100644
--- a/xeth/vm_env.go
+++ b/xeth/vm_env.go
@@ -28,15 +28,15 @@ func NewEnv(state *state.StateDB, block *types.Block, value *big.Int, sender []b
}
func (self *VMEnv) Origin() []byte { return self.sender }
-func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
-func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
-func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
-func (self *VMEnv) Time() int64 { return self.block.Time }
-func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
+func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
+func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
+func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
+func (self *VMEnv) Time() int64 { return self.block.Time() }
+func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
+func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) State() *state.StateDB { return self.state }
-func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) AddLog(log state.Log) {
diff --git a/xeth/world.go b/xeth/world.go
index 956ef1e15..008a08423 100644
--- a/xeth/world.go
+++ b/xeth/world.go
@@ -1,8 +1,7 @@
package xeth
import (
- "container/list"
-
+ "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/state"
)
@@ -55,7 +54,7 @@ func (self *World) IsListening() bool {
return self.pipe.obj.IsListening()
}
-func (self *World) Peers() *list.List {
+func (self *World) Peers() []*p2p.Peer {
return self.pipe.obj.Peers()
}