Merge eth-go repository into go-ethereum

mist, etheruem have been moved to cmd/
This commit is contained in:
Felix Lange 2014-10-23 15:01:27 +02:00
commit 69baa465ea
246 changed files with 35798 additions and 100 deletions

3
.gitignore vendored
View File

@ -9,6 +9,3 @@
*un~ *un~
.DS_Store .DS_Store
*/**/.DS_Store */**/.DS_Store
ethereum/ethereum
ethereal/ethereal

View File

@ -1,4 +1,4 @@
before_install: sudo apt-get install libgmp3-dev before_install: sudo apt-get install libgmp3-dev
language: go language: go
go: go:
- 1.2 - 1.3

View File

@ -1,16 +1,16 @@
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details. Lesser General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU Lesser General Public
along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA MA 02110-1301 USA

View File

@ -10,7 +10,34 @@ Ethereum Go Client © 2014 Jeffrey Wilcke.
Current state: Proof of Concept 0.6.7. Current state: Proof of Concept 0.6.7.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go). Ethereum is currently in its testing phase.
For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
Ethereum Go is split up in several sub packages Please refer to each
individual package for more information.
1. [eth](https://github.com/ethereum/go-ethereum)
2. [ethchain](https://github.com/ethereum/go-ethereum/tree/master/ethchain)
3. [ethwire](https://github.com/ethereum/go-ethereum/tree/master/ethwire)
4. [ethdb](https://github.com/ethereum/go-ethereum/tree/master/ethdb)
5. [ethutil](https://github.com/ethereum/go-ethereum/tree/master/ethutil)
6. [ethpipe](https://github.com/ethereum/go-ethereum/tree/master/ethpipe)
7. [ethvm](https://github.com/ethereum/go-ethereum/tree/master/ethvm)
8. [ethtrie](https://github.com/ethereum/go-ethereum/tree/master/ethtrie)
9. [ethreact](https://github.com/ethereum/go-ethereum/tree/master/ethreact)
10. [ethlog](https://github.com/ethereum/go-ethereum/tree/master/ethlog)
The [eth](https://github.com/ethereum/go-ethereum) is the top-level package
of the Ethereum protocol. It functions as the Ethereum bootstrapping and
peer communication layer. The [ethchain](https://github.com/ethereum/go-ethereum/tree/master/ethchain)
contains the Ethereum blockchain, block manager, transaction and
transaction handlers. The [ethwire](https://github.com/ethereum/go-ethereum/tree/master/ethwire) contains
the Ethereum [wire protocol](http://wiki.ethereum.org/index.php/Wire_Protocol) which can be used
to hook in to the Ethereum network. [ethutil](https://github.com/ethereum/go-ethereum/tree/master/ethutil) contains
utility functions which are not Ethereum specific. The utility package
contains the [patricia trie](http://wiki.ethereum.org/index.php/Patricia_Tree),
[RLP Encoding](http://wiki.ethereum.org/index.php/RLP) and hex encoding
helpers. The [ethdb](https://github.com/ethereum/go-ethereum/tree/master/ethdb) package
contains the LevelDB interface and memory DB interface.
Build Build
======= =======
@ -54,24 +81,26 @@ Mist only
Contribution Contribution
============ ============
If you would like to contribute to Ethereum Go, please fork, fix, commit and If you'd like to contribute to Ethereum please fork, fix, commit and
send a pull request to the main repository. Commits which do not comply with the coding standards explained below send a pull request. Commits who do not comply with the coding standards
will be ignored. If you send a pull request, make sure that you are ignored (use gofmt!). If you send pull requests make absolute sure that you
commit to the `develop` branch and that you do not merge to `master`. commit on the `develop` branch and that you do not merge to master.
Commits that are directly based off of the `master` branch instead of the `develop` branch will be ignored. Commits that are directly based on master are simply ignored.
To make this process simpler try following the [git flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model, as it sets this process up and streamlines work flow. To make life easier try [git flow](http://nvie.com/posts/a-successful-git-branching-model/) it sets
this all up and streamlines your work flow.
Coding standards Coding standards
================ ================
Code should be formatted according to the [Go Formatting Sources should be formatted according to the [Go Formatting
Style](http://golang.org/doc/effective_go.html#formatting). Style](http://golang.org/doc/effective_go.html#formatting).
Unless struct fields are supposed to be directly accessible, provide Unless structs fields are supposed to be directly accesible, provide
getters and hide the fields through Go's exporting facility. Getters and hide the fields through Go's exporting facility.
Make comments in your code meaningful and only use them when necessary. Describe in detail what your code is trying to achieve. For example, this would be redundant and unnecessary commenting: When you comment put meaningfull comments. Describe in detail what you
want to achieve.
*wrong* *wrong*
@ -82,7 +111,12 @@ if x > y {
} }
``` ```
Everyone reading the source code should know what this code snippet was meant to achieve, and so those are **not** meaningful comments. Everyone reading the source probably know what you wanted to achieve
with above code. Those are **not** meaningful comments.
While this project is constantly tested and run, code tests should be written regardless. There is not time to evaluate every person's code specifically, so it is expected of you to write tests for the code so that it does not have to be tested manually. In fact, contributing by simply writing tests is perfectly fine! While the project isn't 100% tested I want you to write tests non the
less. I haven't got time to evaluate everyone's code in detail so I
expect you to write tests for me so I don't have to test your code
manually. (If you want to contribute by just writing tests that's fine
too!)

340
block_pool.go Normal file
View File

@ -0,0 +1,340 @@
package eth
import (
"bytes"
"container/list"
"fmt"
"math"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/ethwire"
)
var poollogger = ethlog.NewLogger("BPOOL")
type block struct {
from *Peer
peer *Peer
block *ethchain.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 ethchain.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.Debugf("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() {
peer.doneFetchingHashes = false
const amount = 256
peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4])
peer.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)}))
}
return true
}
return false
}
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 *ethchain.Block, peer *Peer) {
self.addBlock(b, peer, false)
}
func (self *BlockPool) AddNew(b *ethchain.Block, peer *Peer) {
self.addBlock(b, peer, true)
}
func (self *BlockPool) addBlock(b *ethchain.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(ethwire.NewMessage(ethwire.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 {
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.fetchingHashes {
blocks := self.Blocks()
ethchain.BlockBy(ethchain.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()
ethchain.BlockBy(ethchain.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
}
}
var err error
for i, block := range blocks {
err = self.eth.StateManager().Process(block, false)
if err != nil {
poollogger.Infoln(err)
poollogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
poollogger.Debugln(block)
blocks = blocks[i:]
break
}
self.Remove(block.Hash())
}
if err != nil {
self.Reset()
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
}
}
}
}

16
cmd/LICENSE Normal file
View File

@ -0,0 +1,16 @@
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA

View File

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

View File

@ -8,8 +8,8 @@ import (
"os/user" "os/user"
"path" "path"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/vm" "github.com/ethereum/go-ethereum/vm"
) )
var ( var (

View File

@ -5,9 +5,9 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
) )

View File

@ -7,9 +7,9 @@ import (
"os" "os"
"path" "path"
"github.com/ethereum/eth-go" "github.com/ethereum/go-ethereum"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
) )

View File

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 1004 B

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 905 B

After

Width:  |  Height:  |  Size: 905 B

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 932 B

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -5,10 +5,10 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
) )

View File

@ -7,11 +7,11 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/eth-go/vm"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
"github.com/ethereum/go-ethereum/vm"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

View File

@ -3,12 +3,12 @@ package main
import ( import (
"encoding/json" "encoding/json"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/eth-go/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/eth-go/ui/qt"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/ui/qt"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

View File

@ -11,8 +11,8 @@ import (
"runtime" "runtime"
"bitbucket.org/kardianos/osext" "bitbucket.org/kardianos/osext"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/vm" "github.com/ethereum/go-ethereum/vm"
) )
var ( var (

View File

@ -13,14 +13,14 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/eth-go" "github.com/ethereum/go-ethereum"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/eth-go/ethminer" "github.com/ethereum/go-ethereum/ethminer"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/go-ethereum/ethwire"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

View File

@ -10,10 +10,10 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/howeyc/fsnotify" "github.com/howeyc/fsnotify"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"

View File

@ -4,8 +4,8 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/ethereum/eth-go" "github.com/ethereum/go-ethereum"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/utils" "github.com/ethereum/go-ethereum/utils"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

View File

@ -4,10 +4,10 @@ import (
"fmt" "fmt"
"runtime" "runtime"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

View File

@ -7,14 +7,14 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum/eth-go" "github.com/ethereum/go-ethereum"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/go-ethereum/ethchain"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/eth-go/ethpipe" "github.com/ethereum/go-ethereum/ethpipe"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/eth-go/ui/qt"
"github.com/ethereum/go-ethereum/javascript" "github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/ui/qt"
"gopkg.in/qml.v1" "gopkg.in/qml.v1"
) )

12
ethchain/.gitignore vendored Normal file
View File

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

45
ethchain/asm.go Normal file
View File

@ -0,0 +1,45 @@
package ethchain
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/ethutil"
)
func Disassemble(script []byte) (asm []string) {
pc := new(big.Int)
for {
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
return
}
// Get the memory location of pc
val := script[pc.Int64()]
// Get the opcode (it must be an opcode!)
op := OpCode(val)
asm = append(asm, fmt.Sprintf("%04v: %v", pc, op))
switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1
if int(pc.Int64()+a) > len(script) {
return
}
data := script[pc.Int64() : pc.Int64()+a]
if len(data) == 0 {
data = []byte{0}
}
asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data))
pc.Add(pc, big.NewInt(a-1))
}
pc.Add(pc, ethutil.Big1)
}
return asm
}

439
ethchain/block.go Normal file
View File

@ -0,0 +1,439 @@
package ethchain
import (
"bytes"
"fmt"
"math/big"
"sort"
_ "strconv"
"time"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/ethtrie"
"github.com/ethereum/go-ethereum/ethutil"
)
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 {
// Hash to the previous block
PrevHash ethutil.Bytes
// Uncles of this block
Uncles Blocks
UncleSha []byte
// The coin base address
Coinbase []byte
// Block Trie state
//state *ethutil.Trie
state *ethstate.State
// Difficulty for the current block
Difficulty *big.Int
// Creation time
Time int64
// The block number
Number *big.Int
// Minimum Gas Price
MinGasPrice *big.Int
// Gas limit
GasLimit *big.Int
// Gas used
GasUsed *big.Int
// Extra data
Extra string
// Block Nonce for verification
Nonce ethutil.Bytes
// List of transactions and/or contracts
transactions []*Transaction
receipts []*Receipt
TxSha []byte
}
func NewBlockFromBytes(raw []byte) *Block {
block := &Block{}
block.RlpDecode(raw)
return block
}
// New block takes a raw encoded string
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
block := &Block{}
block.RlpValueDecode(rlpValue)
return block
}
func CreateBlock(root interface{},
prevHash []byte,
base []byte,
Difficulty *big.Int,
Nonce []byte,
extra string) *Block {
block := &Block{
PrevHash: prevHash,
Coinbase: base,
Difficulty: Difficulty,
Nonce: Nonce,
Time: time.Now().Unix(),
Extra: extra,
UncleSha: nil,
GasUsed: new(big.Int),
MinGasPrice: new(big.Int),
GasLimit: new(big.Int),
}
block.SetUncles([]*Block{})
block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, root))
return block
}
// Returns a hash of the block
func (block *Block) Hash() ethutil.Bytes {
return ethcrypto.Sha3(ethutil.NewValue(block.header()).Encode())
//return ethcrypto.Sha3(block.Value().Encode())
}
func (block *Block) HashNoNonce() []byte {
return ethcrypto.Sha3(ethutil.Encode([]interface{}{block.PrevHash,
block.UncleSha, block.Coinbase, block.state.Trie.Root,
block.TxSha, block.Difficulty, block.Number, block.MinGasPrice,
block.GasLimit, block.GasUsed, block.Time, block.Extra}))
}
func (block *Block) State() *ethstate.State {
return block.state
}
func (block *Block) Transactions() []*Transaction {
return block.transactions
}
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
if block.Number.Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6)
}
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit)
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5))
curInt := new(big.Int).Div(current.Num(), current.Denom())
result := new(big.Int).Add(previous, curInt)
result.Div(result, big.NewInt(1024))
min := big.NewInt(125000)
return ethutil.BigMax(min, result)
}
func (block *Block) BlockInfo() BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (self *Block) GetTransaction(hash []byte) *Transaction {
for _, receipt := range self.receipts {
if bytes.Compare(receipt.Tx.Hash(), hash) == 0 {
return receipt.Tx
}
}
return nil
}
// Sync the block's state and contract respectively
func (block *Block) Sync() {
block.state.Sync()
}
func (block *Block) Undo() {
// Sync the block state itself
block.state.Reset()
}
/////// Block Encoding
func (block *Block) rlpReceipts() interface{} {
// Marshal the transactions of this block
encR := make([]interface{}, len(block.receipts))
for i, r := range block.receipts {
// Cast it to a string (safe)
encR[i] = r.RlpData()
}
return encR
}
func (block *Block) rlpUncles() interface{} {
// Marshal the transactions of this block
uncles := make([]interface{}, len(block.Uncles))
for i, uncle := range block.Uncles {
// Cast it to a string (safe)
uncles[i] = uncle.header()
}
return uncles
}
func (block *Block) SetUncles(uncles []*Block) {
block.Uncles = uncles
// Sha of the concatenated uncles
if len(uncles) > 0 {
block.UncleSha = ethcrypto.Sha3(ethutil.Encode(block.rlpUncles()))
}
}
func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) {
self.receipts = receipts
self.setTransactions(txs)
}
func (block *Block) setTransactions(txs []*Transaction) {
block.transactions = txs
}
func CreateTxSha(receipts Receipts) (sha []byte) {
trie := ethtrie.New(ethutil.Config.Db, "")
for i, receipt := range receipts {
trie.Update(string(ethutil.NewValue(i).Encode()), string(ethutil.NewValue(receipt.RlpData()).Encode()))
}
switch trie.Root.(type) {
case string:
sha = []byte(trie.Root.(string))
case []byte:
sha = trie.Root.([]byte)
default:
panic(fmt.Sprintf("invalid root type %T", trie.Root))
}
return sha
}
func (self *Block) SetTxHash(receipts Receipts) {
self.TxSha = CreateTxSha(receipts)
}
func (block *Block) Value() *ethutil.Value {
return ethutil.NewValue([]interface{}{block.header(), block.rlpReceipts(), block.rlpUncles()})
}
func (block *Block) RlpEncode() []byte {
// Encode a slice interface which contains the header and the list of
// transactions.
return block.Value().Encode()
}
func (block *Block) RlpDecode(data []byte) {
rlpValue := ethutil.NewValueFromBytes(data)
block.RlpValueDecode(rlpValue)
}
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
header := decoder.Get(0)
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt()
//fmt.Printf("#%v : %x\n", block.Number, block.Coinbase)
block.MinGasPrice = header.Get(7).BigInt()
block.GasLimit = header.Get(8).BigInt()
block.GasUsed = header.Get(9).BigInt()
block.Time = int64(header.Get(10).BigInt().Uint64())
block.Extra = header.Get(11).Str()
block.Nonce = header.Get(12).Bytes()
// 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.transactions = make([]*Transaction, receipts.Len())
block.receipts = make([]*Receipt, receipts.Len())
for i := 0; i < receipts.Len(); i++ {
receipt := NewRecieptFromValue(receipts.Get(i))
block.transactions[i] = receipt.Tx
block.receipts[i] = receipt
}
}
if decoder.Get(2).IsNil() == false { // Yes explicitness
uncles := decoder.Get(2)
block.Uncles = make([]*Block, uncles.Len())
for i := 0; i < uncles.Len(); i++ {
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
}
}
}
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
block := &Block{}
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt()
block.MinGasPrice = header.Get(7).BigInt()
block.GasLimit = header.Get(8).BigInt()
block.GasUsed = header.Get(9).BigInt()
block.Time = int64(header.Get(10).BigInt().Uint64())
block.Extra = header.Get(11).Str()
block.Nonce = header.Get(12).Bytes()
return block
}
func (block *Block) Trie() *ethtrie.Trie {
return block.state.Trie
}
func (block *Block) GetRoot() interface{} {
return block.state.Trie.Root
}
func (block *Block) Diff() *big.Int {
return block.Difficulty
}
func (self *Block) Receipts() []*Receipt {
return self.receipts
}
func (block *Block) header() []interface{} {
return []interface{}{
// Sha of the previous block
block.PrevHash,
// Sha of uncles
block.UncleSha,
// Coinbase address
block.Coinbase,
// root state
block.state.Trie.Root,
// Sha of tx
block.TxSha,
// Current block Difficulty
block.Difficulty,
// The block number
block.Number,
// Block minimum gas price
block.MinGasPrice,
// Block upper gas bound
block.GasLimit,
// Block gas used
block.GasUsed,
// Time the block was found?
block.Time,
// Extra data
block.Extra,
// Block's Nonce for validation
block.Nonce,
}
}
func (block *Block) String() string {
return fmt.Sprintf(`
BLOCK(%x): Size: %v
PrevHash: %x
UncleSha: %x
Coinbase: %x
Root: %x
TxSha: %x
Difficulty: %v
Number: %v
MinGas: %v
MaxLimit: %v
GasUsed: %v
Time: %v
Extra: %v
Nonce: %x
NumTx: %v
`,
block.Hash(),
block.Size(),
block.PrevHash,
block.UncleSha,
block.Coinbase,
block.state.Trie.Root,
block.TxSha,
block.Difficulty,
block.Number,
block.MinGasPrice,
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()))
}

47
ethchain/bloom.go Normal file
View File

@ -0,0 +1,47 @@
package ethchain
type BloomFilter struct {
bin []byte
}
func NewBloomFilter(bin []byte) *BloomFilter {
if bin == nil {
bin = make([]byte, 256)
}
return &BloomFilter{
bin: bin,
}
}
func (self *BloomFilter) Set(addr []byte) {
if len(addr) < 8 {
chainlogger.Warnf("err: bloom set to small: %x\n", addr)
return
}
for _, i := range addr[len(addr)-8:] {
self.bin[i] = 1
}
}
func (self *BloomFilter) Search(addr []byte) bool {
if len(addr) < 8 {
chainlogger.Warnf("err: bloom search to small: %x\n", addr)
return false
}
for _, i := range addr[len(addr)-8:] {
if self.bin[i] == 0 {
return false
}
}
return true
}
func (self *BloomFilter) Bin() []byte {
return self.bin
}

20
ethchain/bloom_test.go Normal file
View File

@ -0,0 +1,20 @@
package ethchain
import "testing"
func TestBloomFilter(t *testing.T) {
bf := NewBloomFilter(nil)
a := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
bf.Set(a)
b := []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
if bf.Search(a) == false {
t.Error("Expected 'a' to yield true using a bloom filter")
}
if bf.Search(b) {
t.Error("Expected 'b' not to field trie using a bloom filter")
}
}

291
ethchain/chain_manager.go Normal file
View File

@ -0,0 +1,291 @@
package ethchain
import (
"bytes"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/ethutil"
)
var chainlogger = ethlog.NewLogger("CHAIN")
type ChainManager struct {
Ethereum EthManager
// The famous, the fabulous Mister GENESIIIIIIS (block)
genesisBlock *Block
// Last known total difficulty
TD *big.Int
LastBlockNumber uint64
CurrentBlock *Block
LastBlockHash []byte
}
func NewChainManager(ethereum EthManager) *ChainManager {
bc := &ChainManager{}
bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
bc.Ethereum = ethereum
bc.setLastBlock()
return bc
}
func (bc *ChainManager) Genesis() *Block {
return bc.genesisBlock
}
func (bc *ChainManager) NewBlock(coinbase []byte) *Block {
var root interface{}
hash := ZeroHash256
if bc.CurrentBlock != nil {
root = bc.CurrentBlock.state.Trie.Root
hash = bc.LastBlockHash
}
block := CreateBlock(
root,
hash,
coinbase,
ethutil.BigPow(2, 32),
nil,
"")
block.MinGasPrice = big.NewInt(10000000000000)
parent := bc.CurrentBlock
if parent != nil {
block.Difficulty = CalcDifficulty(block, parent)
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
}
return block
}
func CalcDifficulty(block, parent *Block) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
if block.Time >= parent.Time+5 {
diff.Sub(parent.Difficulty, adjust)
} else {
diff.Add(parent.Difficulty, adjust)
}
return diff
}
func (bc *ChainManager) Reset() {
AddTestNetFunds(bc.genesisBlock)
bc.genesisBlock.state.Trie.Sync()
// Prepare the genesis block
bc.Add(bc.genesisBlock)
fk := append([]byte("bloom"), bc.genesisBlock.Hash()...)
bc.Ethereum.Db().Put(fk, make([]byte, 255))
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 (bc *ChainManager) HasBlock(hash []byte) bool {
data, _ := ethutil.Config.Db.Get(hash)
return len(data) != 0
}
// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *ChainManager) HasBlockWithPrevHash(hash []byte) bool {
block := bc.CurrentBlock
for ; block != nil; block = bc.GetBlock(block.PrevHash) {
if bytes.Compare(hash, block.PrevHash) == 0 {
return true
}
}
return false
}
func (bc *ChainManager) CalculateBlockTD(block *Block) *big.Int {
blockDiff := new(big.Int)
for _, uncle := range block.Uncles {
blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
}
blockDiff = blockDiff.Add(blockDiff, block.Difficulty)
return blockDiff
}
func (bc *ChainManager) GenesisBlock() *Block {
return bc.genesisBlock
}
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
block := self.GetBlock(hash)
if block == nil {
return
}
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
for i := uint64(0); i < max; i++ {
chain = append(chain, block.Hash())
if block.Number.Cmp(ethutil.Big0) <= 0 {
break
}
block = self.GetBlock(block.PrevHash)
}
return
}
func AddTestNetFunds(block *Block) {
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.state.GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account)
}
}
func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
// Prep genesis
AddTestNetFunds(bc.genesisBlock)
block := NewBlockFromBytes(data)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64()
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
} else {
bc.Reset()
}
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
}
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
bc.TD = td
}
// Add a block to the chain and record addition information
func (bc *ChainManager) Add(block *Block) {
bc.writeBlockInfo(block)
// Prepare the genesis block
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
encodedBlock := block.RlpEncode()
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}
func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) {
parent := self.GetBlock(block.PrevHash)
if parent == nil {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
}
parentTd := parent.BlockInfo().TD
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
td := new(big.Int)
td = td.Add(parentTd, uncleDiff)
td = td.Add(td, block.Difficulty)
return td, nil
}
func (bc *ChainManager) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 {
return nil
}
return NewBlockFromBytes(data)
}
func (self *ChainManager) GetBlockByNumber(num uint64) *Block {
block := self.CurrentBlock
for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
}
if block != nil && block.Number.Uint64() == 0 && num != 0 {
return nil
}
return block
}
func (self *ChainManager) GetBlockBack(num uint64) *Block {
block := self.CurrentBlock
for ; num != 0 && block != nil; num-- {
block = self.GetBlock(block.PrevHash)
}
return block
}
func (bc *ChainManager) BlockInfoByHash(hash []byte) BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (bc *ChainManager) BlockInfo(block *Block) BlockInfo {
bi := 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 *Block) {
bc.LastBlockNumber++
bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
// For now we use the block hash with the words "info" appended as key
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}
func (bc *ChainManager) Stop() {
if bc.CurrentBlock != nil {
chainlogger.Infoln("Stopped")
}
}

View File

@ -0,0 +1 @@
package ethchain

239
ethchain/dagger.go Normal file
View File

@ -0,0 +1,239 @@
package ethchain
import (
"hash"
"math/big"
"math/rand"
"time"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/obscuren/sha3"
)
var powlogger = ethlog.NewLogger("POW")
type PoW interface {
Search(block *Block, stop <-chan struct{}) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool
GetHashrate() int64
Turbo(bool)
}
type EasyPow struct {
hash *big.Int
HashRate int64
turbo bool
}
func (pow *EasyPow) GetHashrate() int64 {
return pow.HashRate
}
func (pow *EasyPow) Turbo(on bool) {
pow.turbo = on
}
func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce()
diff := block.Difficulty
i := int64(0)
start := time.Now().UnixNano()
t := time.Now()
for {
select {
case <-stop:
powlogger.Infoln("Breaking from mining")
return nil
default:
i++
if time.Since(t) > (1 * time.Second) {
elapsed := time.Now().UnixNano() - start
hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
pow.HashRate = int64(hashes)
powlogger.Infoln("Hashing @", int64(pow.HashRate), "khash")
t = time.Now()
}
sha := ethcrypto.Sha3(big.NewInt(r.Int63()).Bytes())
if pow.Verify(hash, diff, sha) {
return sha
}
}
if !pow.turbo {
time.Sleep(20 * time.Microsecond)
}
}
return nil
}
func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
sha := sha3.NewKeccak256()
d := append(hash, nonce...)
sha.Write(d)
v := ethutil.BigPow(2, 256)
ret := new(big.Int).Div(v, diff)
res := new(big.Int)
res.SetBytes(sha.Sum(nil))
return res.Cmp(ret) == -1
}
func (pow *EasyPow) SetHash(hash *big.Int) {
}
type Dagger struct {
hash *big.Int
xn *big.Int
}
var Found bool
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 1000; i++ {
rnd := r.Int63()
res := dag.Eval(big.NewInt(rnd))
powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
if res.Cmp(obj) < 0 {
// Post back result on the channel
resChan <- rnd
// Notify other threads we've found a valid nonce
Found = true
}
// Break out if found
if Found {
break
}
}
resChan <- 0
}
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
// TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines := 1
dag.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
Found = false
resChan := make(chan int64, 3)
var res int64
for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan)
// Wait for each go routine to finish
}
for k := 0; k < amountOfRoutines; k++ {
// Get the result from the channel. 0 = quit
if r := <-resChan; r != 0 {
res = r
}
}
return big.NewInt(res)
}
func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
dag.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
return dag.Eval(nonce).Cmp(obj) < 0
}
func DaggerVerify(hash, diff, nonce *big.Int) bool {
dagger := &Dagger{}
dagger.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
return dagger.Eval(nonce).Cmp(obj) < 0
}
func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
if L == i {
return dag.hash
}
var m *big.Int
if L == 9 {
m = big.NewInt(16)
} else {
m = big.NewInt(3)
}
sha := sha3.NewKeccak256()
sha.Reset()
d := sha3.NewKeccak256()
b := new(big.Int)
ret := new(big.Int)
for k := 0; k < int(m.Uint64()); k++ {
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(big.NewInt(int64(L)).Bytes())
d.Write(big.NewInt(int64(i)).Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
sha.Write(dag.Node(L-1, pk).Bytes())
}
ret.SetBytes(Sum(sha))
return ret
}
func Sum(sha hash.Hash) []byte {
//in := make([]byte, 32)
return sha.Sum(nil)
}
func (dag *Dagger) Eval(N *big.Int) *big.Int {
pow := ethutil.BigPow(2, 26)
dag.xn = pow.Div(N, pow)
sha := sha3.NewKeccak256()
sha.Reset()
ret := new(big.Int)
for k := 0; k < 4; k++ {
d := sha3.NewKeccak256()
b := new(big.Int)
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(N.Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := (b.Uint64() & 0x1ffffff)
sha.Write(dag.Node(9, pk).Bytes())
}
return ret.SetBytes(Sum(sha))
}

19
ethchain/dagger_test.go Normal file
View File

@ -0,0 +1,19 @@
package ethchain
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/ethutil"
)
func BenchmarkDaggerSearch(b *testing.B) {
hash := big.NewInt(0)
diff := ethutil.BigPow(2, 36)
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
// Reset timer so the big generation isn't included in the benchmark
b.ResetTimer()
// Validate
DaggerVerify(hash, diff, o)
}

116
ethchain/error.go Normal file
View File

@ -0,0 +1,116 @@
package ethchain
import (
"fmt"
"math/big"
)
// Parent error. In case a parent is unknown this error will be thrown
// by the block manager
type ParentErr struct {
Message string
}
func (err *ParentErr) Error() string {
return err.Message
}
func ParentError(hash []byte) error {
return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)}
}
func IsParentErr(err error) bool {
_, ok := err.(*ParentErr)
return ok
}
type UncleErr struct {
Message string
}
func (err *UncleErr) Error() string {
return err.Message
}
func UncleError(str string) error {
return &UncleErr{Message: str}
}
func IsUncleErr(err error) bool {
_, ok := err.(*UncleErr)
return ok
}
// Block validation error. If any validation fails, this error will be thrown
type ValidationErr struct {
Message string
}
func (err *ValidationErr) Error() string {
return err.Message
}
func ValidationError(format string, v ...interface{}) *ValidationErr {
return &ValidationErr{Message: fmt.Sprintf(format, v...)}
}
func IsValidationErr(err error) bool {
_, ok := err.(*ValidationErr)
return ok
}
type GasLimitErr struct {
Message string
Is, Max *big.Int
}
func IsGasLimitErr(err error) bool {
_, ok := err.(*GasLimitErr)
return ok
}
func (err *GasLimitErr) Error() string {
return err.Message
}
func GasLimitError(is, max *big.Int) *GasLimitErr {
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
}
type NonceErr struct {
Message string
Is, Exp uint64
}
func (err *NonceErr) Error() string {
return err.Message
}
func NonceError(is, exp uint64) *NonceErr {
return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp}
}
func IsNonceErr(err error) bool {
_, ok := err.(*NonceErr)
return ok
}
type OutOfGasErr struct {
Message string
}
func OutOfGasError() *OutOfGasErr {
return &OutOfGasErr{Message: "Out of gas"}
}
func (self *OutOfGasErr) Error() string {
return self.Message
}
func IsOutOfGasErr(err error) bool {
_, ok := err.(*OutOfGasErr)
return ok
}

10
ethchain/events.go Normal file
View File

@ -0,0 +1,10 @@
package ethchain
type TxEvent struct {
Type int // TxPre || TxPost
Tx *Transaction
}
type NewBlockEvent struct {
Block *Block
}

7
ethchain/fees.go Normal file
View File

@ -0,0 +1,7 @@
package ethchain
import (
"math/big"
)
var BlockReward *big.Int = big.NewInt(1.5e+18)

206
ethchain/filter.go Normal file
View File

@ -0,0 +1,206 @@
package ethchain
import (
"bytes"
"fmt"
"math"
"github.com/ethereum/go-ethereum/ethstate"
)
type AccountChange struct {
Address, StateAddress []byte
}
// Filtering interface
type Filter struct {
eth EthManager
earliest int64
latest int64
skip int
from, to [][]byte
max int
Altered []AccountChange
BlockCallback func(*Block)
MessageCallback func(ethstate.Messages)
}
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
// is interesting or not.
func NewFilter(eth EthManager) *Filter {
return &Filter{eth: eth}
}
func (self *Filter) AddAltered(address, stateAddress []byte) {
self.Altered = append(self.Altered, AccountChange{address, stateAddress})
}
// Set the earliest and latest block for filtering.
// -1 = latest block (i.e., the current block)
// hash = particular hash from-to
func (self *Filter) SetEarliestBlock(earliest int64) {
self.earliest = earliest
}
func (self *Filter) SetLatestBlock(latest int64) {
self.latest = latest
}
func (self *Filter) SetFrom(addr [][]byte) {
self.from = addr
}
func (self *Filter) AddFrom(addr []byte) {
self.from = append(self.from, addr)
}
func (self *Filter) SetTo(addr [][]byte) {
self.to = addr
}
func (self *Filter) AddTo(addr []byte) {
self.to = append(self.to, addr)
}
func (self *Filter) SetMax(max int) {
self.max = max
}
func (self *Filter) SetSkip(skip int) {
self.skip = skip
}
// Run filters messages with the current parameters set
func (self *Filter) Find() []*ethstate.Message {
var earliestBlockNo uint64 = uint64(self.earliest)
if self.earliest == -1 {
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
}
var latestBlockNo uint64 = uint64(self.latest)
if self.latest == -1 {
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
}
var (
messages []*ethstate.Message
block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
quit bool
)
for i := 0; !quit && block != nil; i++ {
// Quit on latest
switch {
case block.Number.Uint64() == earliestBlockNo:
quit = true
case self.max <= len(messages):
break
}
// Use bloom filtering to see if this block is interesting given the
// current parameters
if self.bloomFilter(block) {
// Get the messages of the block
msgs, err := self.eth.StateManager().GetMessages(block)
if err != nil {
chainlogger.Warnln("err: filter get messages ", err)
break
}
messages = append(messages, self.FilterMessages(msgs)...)
}
block = self.eth.ChainManager().GetBlock(block.PrevHash)
}
skip := int(math.Min(float64(len(messages)), float64(self.skip)))
return messages[skip:]
}
func includes(addresses [][]byte, a []byte) (found bool) {
for _, addr := range addresses {
if bytes.Compare(addr, a) == 0 {
return true
}
}
return
}
func (self *Filter) FilterMessages(msgs []*ethstate.Message) []*ethstate.Message {
var messages []*ethstate.Message
// Filter the messages for interesting stuff
for _, message := range msgs {
if len(self.to) > 0 && !includes(self.to, message.To) {
continue
}
if len(self.from) > 0 && !includes(self.from, message.From) {
continue
}
var match bool
if len(self.Altered) == 0 {
match = true
}
for _, accountChange := range self.Altered {
if len(accountChange.Address) > 0 && bytes.Compare(message.To, accountChange.Address) != 0 {
continue
}
if len(accountChange.StateAddress) > 0 && !includes(message.ChangedAddresses, accountChange.StateAddress) {
continue
}
match = true
break
}
if !match {
continue
}
messages = append(messages, message)
}
return messages
}
func (self *Filter) bloomFilter(block *Block) bool {
fk := append([]byte("bloom"), block.Hash()...)
bin, err := self.eth.Db().Get(fk)
if err != nil {
fmt.Println(err)
}
bloom := NewBloomFilter(bin)
var fromIncluded, toIncluded bool
if len(self.from) > 0 {
for _, from := range self.from {
if bloom.Search(from) {
fromIncluded = true
break
}
}
} else {
fromIncluded = true
}
if len(self.to) > 0 {
for _, to := range self.to {
if bloom.Search(to) {
toIncluded = true
break
}
}
} else {
toIncluded = true
}
return fromIncluded && toIncluded
}

7
ethchain/filter_test.go Normal file
View File

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

48
ethchain/genesis.go Normal file
View File

@ -0,0 +1,48 @@
package ethchain
import (
"math/big"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethutil"
)
/*
* This is the special genesis block.
*/
var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var EmptyShaList = ethcrypto.Sha3(ethutil.Encode([]interface{}{}))
var GenesisHeader = []interface{}{
// Previous hash (none)
ZeroHash256,
// Empty uncles
"",
// Coinbase
ZeroHash160,
// Root state
"",
// tx sha
"",
// Difficulty
//ethutil.BigPow(2, 22),
big.NewInt(131072),
// Number
ethutil.Big0,
// Block minimum gas price
ethutil.Big0,
// Block upper gas bound
big.NewInt(1000000),
// Block gas used
ethutil.Big0,
// Time
ethutil.Big0,
// Extra
nil,
// Nonce
ethcrypto.Sha3(big.NewInt(42).Bytes()),
}
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}

92
ethchain/helper_test.go Normal file
View File

@ -0,0 +1,92 @@
package ethchain
import (
"container/list"
"fmt"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/ethwire"
"github.com/ethereum/go-ethereum/event"
)
// Implement our EthTest Manager
type TestManager struct {
stateManager *StateManager
eventMux *event.TypeMux
db ethutil.Database
txPool *TxPool
blockChain *BlockChain
Blocks []*Block
}
func (s *TestManager) IsListening() bool {
return false
}
func (s *TestManager) IsMining() bool {
return false
}
func (s *TestManager) PeerCount() int {
return 0
}
func (s *TestManager) Peers() *list.List {
return list.New()
}
func (s *TestManager) BlockChain() *BlockChain {
return s.blockChain
}
func (tm *TestManager) TxPool() *TxPool {
return tm.txPool
}
func (tm *TestManager) StateManager() *StateManager {
return tm.stateManager
}
func (tm *TestManager) EventMux() *event.TypeMux {
return tm.eventMux
}
func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) {
fmt.Println("Broadcast not implemented")
}
func (tm *TestManager) ClientIdentity() ethwire.ClientIdentity {
return nil
}
func (tm *TestManager) KeyManager() *ethcrypto.KeyManager {
return nil
}
func (tm *TestManager) Db() ethutil.Database {
return tm.db
}
func NewTestManager() *TestManager {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH")
db, err := ethdb.NewMemDatabase()
if err != nil {
fmt.Println("Could not create mem-db, failing")
return nil
}
ethutil.Config.Db = db
testManager := &TestManager{}
testManager.eventMux = new(event.TypeMux)
testManager.db = db
testManager.txPool = NewTxPool(testManager)
testManager.blockChain = NewBlockChain(testManager)
testManager.stateManager = NewStateManager(testManager)
// Start the tx pool
testManager.txPool.Start()
return testManager
}

446
ethchain/state_manager.go Normal file
View File

@ -0,0 +1,446 @@
package ethchain
import (
"bytes"
"container/list"
"fmt"
"math/big"
"os"
"sync"
"time"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/ethwire"
"github.com/ethereum/go-ethereum/event"
)
var statelogger = ethlog.NewLogger("STATE")
type Peer interface {
Inbound() bool
LastSend() time.Time
LastPong() int64
Host() []byte
Port() uint16
Version() string
PingTime() string
Connected() *int32
Caps() *ethutil.Value
}
type EthManager interface {
StateManager() *StateManager
ChainManager() *ChainManager
TxPool() *TxPool
Broadcast(msgType ethwire.MsgType, data []interface{})
PeerCount() int
IsMining() bool
IsListening() bool
Peers() *list.List
KeyManager() *ethcrypto.KeyManager
ClientIdentity() ethwire.ClientIdentity
Db() ethutil.Database
EventMux() *event.TypeMux
}
type StateManager struct {
// Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex
// Canonical block chain
bc *ChainManager
// non-persistent key/value memory storage
mem map[string]*big.Int
// Proof of work used for validating
Pow PoW
// The ethereum manager interface
eth EthManager
// The managed states
// Transiently state. The trans state isn't ever saved, validated and
// it could be used for setting account nonces without effecting
// the main states.
transState *ethstate.State
// Mining state. The mining state is used purely and solely by the mining
// operation.
miningState *ethstate.State
// The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during
// 'Process' & canonical validation.
lastAttemptedBlock *Block
events event.Subscription
}
func NewStateManager(ethereum EthManager) *StateManager {
sm := &StateManager{
mem: make(map[string]*big.Int),
Pow: &EasyPow{},
eth: ethereum,
bc: ethereum.ChainManager(),
}
sm.transState = ethereum.ChainManager().CurrentBlock.State().Copy()
sm.miningState = ethereum.ChainManager().CurrentBlock.State().Copy()
return sm
}
func (self *StateManager) Start() {
statelogger.Debugln("Starting state manager")
self.events = self.eth.EventMux().Subscribe(Blocks(nil))
go self.updateThread()
}
func (self *StateManager) Stop() {
statelogger.Debugln("Stopping state manager")
self.events.Unsubscribe()
}
func (self *StateManager) updateThread() {
for ev := range self.events.Chan() {
for _, block := range ev.(Blocks) {
err := self.Process(block, false)
if err != nil {
statelogger.Infoln(err)
statelogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
statelogger.Debugln(block)
break
}
}
}
}
func (sm *StateManager) CurrentState() *ethstate.State {
return sm.eth.ChainManager().CurrentBlock.State()
}
func (sm *StateManager) TransState() *ethstate.State {
return sm.transState
}
func (sm *StateManager) MiningState() *ethstate.State {
return sm.miningState
}
func (sm *StateManager) NewMiningState() *ethstate.State {
sm.miningState = sm.eth.ChainManager().CurrentBlock.State().Copy()
return sm.miningState
}
func (sm *StateManager) ChainManager() *ChainManager {
return sm.bc
}
func (self *StateManager) ProcessTransactions(coinbase *ethstate.StateObject, state *ethstate.State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
var (
receipts Receipts
handled, unhandled Transactions
totalUsedGas = big.NewInt(0)
err error
)
done:
for i, tx := range txs {
txGas := new(big.Int).Set(tx.Gas)
cb := state.GetStateObject(coinbase.Address())
st := NewStateTransition(cb, tx, state, block)
err = st.TransitionState()
if err != nil {
statelogger.Infoln(err)
switch {
case IsNonceErr(err):
err = nil // ignore error
continue
case IsGasLimitErr(err):
unhandled = txs[i:]
break done
default:
statelogger.Infoln(err)
err = nil
//return nil, nil, nil, err
}
}
// Update the state with pending changes
state.Update()
txGas.Sub(txGas, st.gas)
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
if i < len(block.Receipts()) {
original := block.Receipts()[i]
if !original.Cmp(receipt) {
if ethutil.Config.Diff {
os.Exit(1)
}
err := fmt.Errorf("#%d receipt failed (r) %v ~ %x <=> (c) %v ~ %x (%x...)", i+1, original.CumulativeGasUsed, original.PostState[0:4], receipt.CumulativeGasUsed, receipt.PostState[0:4], receipt.Tx.Hash()[0:4])
return nil, nil, nil, err
}
}
// Notify all subscribers
self.eth.EventMux().Post(TxEvent{TxPost, tx})
receipts = append(receipts, receipt)
handled = append(handled, tx)
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" {
state.CreateOutputForDiff()
}
}
parent.GasUsed = totalUsedGas
return receipts, handled, unhandled, err
}
func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
// Processing a blocks may never happen simultaneously
sm.mutex.Lock()
defer sm.mutex.Unlock()
if sm.bc.HasBlock(block.Hash()) {
return nil
}
if !sm.bc.HasBlock(block.PrevHash) {
return ParentError(block.PrevHash)
}
sm.lastAttemptedBlock = block
var (
parent = sm.bc.GetBlock(block.PrevHash)
state = parent.State()
)
// 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()
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" {
fmt.Printf("## %x %x ##\n", block.Hash(), block.Number)
}
receipts, err := sm.ApplyDiff(state, parent, block)
if err != nil {
return err
}
txSha := CreateTxSha(receipts)
if bytes.Compare(txSha, block.TxSha) != 0 {
return fmt.Errorf("Error validating tx sha. Received %x, got %x", block.TxSha, txSha)
}
// Block validation
if err = sm.ValidateBlock(block); err != nil {
statelogger.Errorln("Error validating block:", err)
return err
}
if err = sm.AccumelateRewards(state, block, parent); err != nil {
statelogger.Errorln("Error accumulating reward", err)
return err
}
state.Update()
if !block.State().Cmp(state) {
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root)
return
}
// Calculate the new total difficulty and sync back to the db
if sm.CalculateTD(block) {
// Sync the current block's state to the database and cancelling out the deferred Undo
state.Sync()
// Add the block to the chain
sm.bc.Add(block)
sm.transState = state.Copy()
// Create a bloom bin for this block
filter := sm.createBloomFilter(state)
// Persist the data
fk := append([]byte("bloom"), block.Hash()...)
sm.eth.Db().Put(fk, filter.Bin())
statelogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
if dontReact == false {
sm.eth.EventMux().Post(NewBlockEvent{block})
state.Manifest().Reset()
}
sm.eth.TxPool().RemoveInvalid(state)
} else {
statelogger.Errorln("total diff failed")
}
return nil
}
func (sm *StateManager) ApplyDiff(state *ethstate.State, parent, block *Block) (receipts Receipts, err error) {
coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent))
// Process the transactions on to current block
receipts, _, _, err = sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
if err != nil {
return nil, err
}
return receipts, nil
}
func (sm *StateManager) CalculateTD(block *Block) bool {
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
td := new(big.Int)
td = td.Add(sm.bc.TD, uncleDiff)
td = td.Add(td, block.Difficulty)
// The new TD will only be accepted if the new difficulty is
// is greater than the previous.
if td.Cmp(sm.bc.TD) > 0 {
// Set the new total difficulty back to the block chain
sm.bc.SetTotalDifficulty(td)
return true
}
return false
}
// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *StateManager) ValidateBlock(block *Block) error {
// Check each uncle's previous hash. In order for it to be valid
// is if it has the same block hash as the current
parent := sm.bc.GetBlock(block.PrevHash)
/*
for _, uncle := range block.Uncles {
if bytes.Compare(uncle.PrevHash,parent.PrevHash) != 0 {
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x",parent.PrevHash, uncle.PrevHash)
}
}
*/
expd := CalcDifficulty(block, parent)
if expd.Cmp(block.Difficulty) < 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
}
diff := block.Time - parent.Time
if diff < 0 {
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time)
}
/* XXX
// New blocks must be within the 15 minute range of the last block.
if diff > int64(15*time.Minute) {
return ValidationError("Block is too far in the future of last block (> 15 minutes)")
}
*/
// Verify the nonce of the block. Return an error if it's not valid
if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce))
}
return nil
}
func (sm *StateManager) AccumelateRewards(state *ethstate.State, block, parent *Block) error {
reward := new(big.Int).Set(BlockReward)
knownUncles := ethutil.Set(parent.Uncles)
nonces := ethutil.NewSet(block.Nonce)
for _, uncle := range block.Uncles {
if nonces.Include(uncle.Nonce) {
// Error not unique
return UncleError("Uncle not unique")
}
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
if uncleParent == nil {
return UncleError("Uncle's parent unknown")
}
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
return UncleError("Uncle too old")
}
if knownUncles.Include(uncle.Hash()) {
return UncleError("Uncle in chain")
}
nonces.Insert(uncle.Nonce)
r := new(big.Int)
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
uncleAccount := state.GetAccount(uncle.Coinbase)
uncleAccount.AddAmount(r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
}
// Get the account associated with the coinbase
account := state.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address
account.AddAmount(reward)
return nil
}
// Manifest will handle both creating notifications and generating bloom bin data
func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter {
bloomf := NewBloomFilter(nil)
for _, msg := range state.Manifest().Messages {
bloomf.Set(msg.To)
bloomf.Set(msg.From)
}
sm.eth.EventMux().Post(state.Manifest().Messages)
return bloomf
}
func (sm *StateManager) GetMessages(block *Block) (messages []*ethstate.Message, err error) {
if !sm.bc.HasBlock(block.PrevHash) {
return nil, ParentError(block.PrevHash)
}
sm.lastAttemptedBlock = block
var (
parent = sm.bc.GetBlock(block.PrevHash)
state = parent.State().Copy()
)
defer state.Reset()
sm.ApplyDiff(state, parent, block)
sm.AccumelateRewards(state, block, parent)
return state.Manifest().Messages, nil
}

View File

@ -0,0 +1,276 @@
package ethchain
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/ethtrie"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/vm"
)
/*
* The State transitioning model
*
* A state transition is a change made when a transaction is applied to the current world state
* The state transitioning model does all all the necessary work to work out a valid new state root.
* 1) Nonce handling
* 2) Pre pay / buy gas of the coinbase (miner)
* 3) Create a new state object if the recipient is \0*32
* 4) Value transfer
* == If contract creation ==
* 4a) Attempt to run transaction data
* 4b) If valid, use result as code for the new state object
* == end ==
* 5) Run Script section
* 6) Derive new state root
*/
type StateTransition struct {
coinbase, receiver []byte
tx *Transaction
gas, gasPrice *big.Int
value *big.Int
data []byte
state *ethstate.State
block *Block
cb, rec, sen *ethstate.StateObject
}
func NewStateTransition(coinbase *ethstate.StateObject, tx *Transaction, state *ethstate.State, block *Block) *StateTransition {
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
}
func (self *StateTransition) Coinbase() *ethstate.StateObject {
if self.cb != nil {
return self.cb
}
self.cb = self.state.GetOrNewStateObject(self.coinbase)
return self.cb
}
func (self *StateTransition) Sender() *ethstate.StateObject {
if self.sen != nil {
return self.sen
}
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
return self.sen
}
func (self *StateTransition) Receiver() *ethstate.StateObject {
if self.tx != nil && self.tx.CreatesContract() {
return nil
}
if self.rec != nil {
return self.rec
}
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
return self.rec
}
func (self *StateTransition) UseGas(amount *big.Int) error {
if self.gas.Cmp(amount) < 0 {
return OutOfGasError()
}
self.gas.Sub(self.gas, amount)
return nil
}
func (self *StateTransition) AddGas(amount *big.Int) {
self.gas.Add(self.gas, amount)
}
func (self *StateTransition) BuyGas() error {
var err error
sender := self.Sender()
if sender.Balance().Cmp(self.tx.GasValue()) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance())
}
coinbase := self.Coinbase()
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
if err != nil {
return err
}
self.AddGas(self.tx.Gas)
sender.SubAmount(self.tx.GasValue())
return nil
}
func (self *StateTransition) RefundGas() {
coinbase, sender := self.Coinbase(), self.Sender()
coinbase.RefundGas(self.gas, self.tx.GasPrice)
// Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
sender.AddAmount(remaining)
}
func (self *StateTransition) preCheck() (err error) {
var (
tx = self.tx
sender = self.Sender()
)
// Make sure this transaction's nonce is correct
if sender.Nonce != tx.Nonce {
return NonceError(tx.Nonce, sender.Nonce)
}
// Pre-pay gas / Buy gas of the coinbase account
if err = self.BuyGas(); err != nil {
return err
}
return nil
}
func (self *StateTransition) TransitionState() (err error) {
statelogger.Debugf("(~) %x\n", self.tx.Hash())
defer func() {
if r := recover(); r != nil {
statelogger.Infoln(r)
err = fmt.Errorf("state transition err %v", r)
}
}()
// XXX Transactions after this point are considered valid.
if err = self.preCheck(); err != nil {
return
}
var (
tx = self.tx
sender = self.Sender()
receiver *ethstate.StateObject
)
defer self.RefundGas()
// Increment the nonce for the next transaction
sender.Nonce += 1
// Transaction gas
if err = self.UseGas(vm.GasTx); err != nil {
return
}
// Pay data gas
dataPrice := big.NewInt(int64(len(self.data)))
dataPrice.Mul(dataPrice, vm.GasData)
if err = self.UseGas(dataPrice); err != nil {
return
}
if sender.Balance().Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
}
var snapshot *ethstate.State
// If the receiver is nil it's a contract (\0*32).
if tx.CreatesContract() {
// Subtract the (irreversible) amount from the senders account
sender.SubAmount(self.value)
snapshot = self.state.Copy()
// Create a new state object for the contract
receiver := MakeContract(tx, self.state)
self.rec = receiver
if receiver == nil {
return fmt.Errorf("Unable to create contract")
}
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(self.value)
} else {
receiver = self.Receiver()
// Subtract the amount from the senders account
sender.SubAmount(self.value)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(self.value)
snapshot = self.state.Copy()
}
msg := self.state.Manifest().AddMessage(&ethstate.Message{
To: receiver.Address(), From: sender.Address(),
Input: self.tx.Data,
Origin: sender.Address(),
Block: self.block.Hash(), Timestamp: self.block.Time, Coinbase: self.block.Coinbase, Number: self.block.Number,
Value: self.value,
})
// Process the init code and create 'valid' contract
if IsContractAddr(self.receiver) {
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
self.data = nil
code, err := self.Eval(msg, receiver.Init(), receiver)
if err != nil {
self.state.Set(snapshot)
return fmt.Errorf("Error during init execution %v", err)
}
receiver.Code = code
msg.Output = code
} else {
if len(receiver.Code) > 0 {
ret, err := self.Eval(msg, receiver.Code, receiver)
if err != nil {
self.state.Set(snapshot)
return fmt.Errorf("Error during code execution %v", err)
}
msg.Output = ret
}
}
return
}
func (self *StateTransition) Eval(msg *ethstate.Message, script []byte, context *ethstate.StateObject) (ret []byte, err error) {
var (
transactor = self.Sender()
state = self.state
env = NewEnv(state, self.tx, self.block)
callerClosure = vm.NewClosure(msg, transactor, context, script, self.gas, self.gasPrice)
)
//vm := vm.New(env, vm.Type(ethutil.Config.VmType))
evm := vm.New(env, vm.DebugVmTy)
ret, _, err = callerClosure.Call(evm, self.tx.Data)
return
}
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject {
// Create contract if there's no recipient
if tx.IsContract() {
addr := tx.CreationAddress(state)
contract := state.GetOrNewStateObject(addr)
contract.InitCode = tx.Data
contract.State = ethstate.New(ethtrie.New(ethutil.Config.Db, ""))
return contract
}
return nil
}

257
ethchain/transaction.go Normal file
View File

@ -0,0 +1,257 @@
package ethchain
import (
"bytes"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/ethcrypto"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/obscuren/secp256k1-go"
)
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func IsContractAddr(addr []byte) bool {
return len(addr) == 0
//return bytes.Compare(addr, ContractAddr) == 0
}
type Transaction struct {
Nonce uint64
Recipient []byte
Value *big.Int
Gas *big.Int
GasPrice *big.Int
Data []byte
v byte
r, s []byte
// Indicates whether this tx is a contract creation transaction
contractCreation bool
}
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
}
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 NewTransactionFromBytes(data []byte) *Transaction {
tx := &Transaction{}
tx.RlpDecode(data)
return tx
}
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
tx := &Transaction{}
tx.RlpValueDecode(val)
return tx
}
func (self *Transaction) GasValue() *big.Int {
return new(big.Int).Mul(self.Gas, self.GasPrice)
}
func (self *Transaction) TotalValue() *big.Int {
v := self.GasValue()
return v.Add(v, self.Value)
}
func (tx *Transaction) Hash() []byte {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
return ethcrypto.Sha3(ethutil.NewValue(data).Encode())
}
func (tx *Transaction) CreatesContract() bool {
return tx.contractCreation
}
/* Deprecated */
func (tx *Transaction) IsContract() bool {
return tx.CreatesContract()
}
func (tx *Transaction) CreationAddress(state *ethstate.State) []byte {
// Generate a new address
addr := ethcrypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
//for i := uint64(0); state.GetStateObject(addr) != nil; i++ {
// addr = ethcrypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce + i}).Encode())[12:]
//}
return addr
}
func (tx *Transaction) Signature(key []byte) []byte {
hash := tx.Hash()
sig, _ := secp256k1.Sign(hash, key)
return sig
}
func (tx *Transaction) PublicKey() []byte {
hash := tx.Hash()
// TODO
r := ethutil.LeftPadBytes(tx.r, 32)
s := ethutil.LeftPadBytes(tx.s, 32)
sig := append(r, s...)
sig = append(sig, tx.v-27)
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
return pubkey
}
func (tx *Transaction) Sender() []byte {
pubkey := tx.PublicKey()
// Validate the returned key.
// Return nil if public key isn't in full format
if pubkey[0] != 4 {
return nil
}
return ethcrypto.Sha3(pubkey[1:])[12:]
}
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
return nil
}
func (tx *Transaction) RlpData() interface{} {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
// TODO Remove prefixing zero's
return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
}
func (tx *Transaction) RlpValue() *ethutil.Value {
return ethutil.NewValue(tx.RlpData())
}
func (tx *Transaction) RlpEncode() []byte {
return tx.RlpValue().Encode()
}
func (tx *Transaction) RlpDecode(data []byte) {
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
}
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()
if IsContractAddr(tx.Recipient) {
tx.contractCreation = true
}
}
func (tx *Transaction) String() string {
return fmt.Sprintf(`
TX(%x)
Contract: %v
From: %x
To: %x
Nonce: %v
GasPrice: %v
Gas: %v
Value: %v
Data: 0x%x
V: 0x%x
R: 0x%x
S: 0x%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)
}
type Receipt struct {
Tx *Transaction
PostState []byte
CumulativeGasUsed *big.Int
}
type Receipts []*Receipt
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
r := &Receipt{}
r.RlpValueDecode(val)
return r
}
func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) {
self.Tx = NewTransactionFromValue(decoder.Get(0))
self.PostState = decoder.Get(1).Bytes()
self.CumulativeGasUsed = decoder.Get(2).BigInt()
}
func (self *Receipt) RlpData() interface{} {
return []interface{}{self.Tx.RlpData(), self.PostState, self.CumulativeGasUsed}
}
func (self *Receipt) String() string {
return fmt.Sprintf(`
R
Tx:[ %v]
PostState: 0x%x
CumulativeGasUsed: %v
`,
self.Tx,
self.PostState,
self.CumulativeGasUsed)
}
func (self *Receipt) Cmp(other *Receipt) bool {
if bytes.Compare(self.PostState, other.PostState) != 0 {
return false
}
return true
}
// Transaction slice type for basic sorting
type Transactions []*Transaction
func (s Transactions) Len() int { return len(s) }
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type TxByNonce struct{ Transactions }
func (s TxByNonce) Less(i, j int) bool {
return s.Transactions[i].Nonce < s.Transactions[j].Nonce
}

View File

@ -0,0 +1,224 @@
package ethchain
import (
"bytes"
"container/list"
"fmt"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/ethlog"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/ethwire"
)
var txplogger = ethlog.NewLogger("TXP")
const (
txPoolQueueSize = 50
)
type TxPoolHook chan *Transaction
type TxMsgTy byte
const (
TxPre = iota
TxPost
minGasPrice = 1000000
)
var MinGasPrice = big.NewInt(10000000000000)
type TxMsg struct {
Tx *Transaction
Type TxMsgTy
}
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*Transaction); ok {
if finder(tx, e) {
return tx
}
}
}
return nil
}
type TxProcessor interface {
ProcessTransaction(tx *Transaction)
}
// The tx pool a thread safe transaction pool handler. In order to
// guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the
// pool is being drained or synced for whatever reason the transactions
// will simple queue up and handled when the mutex is freed.
type TxPool struct {
Ethereum EthManager
// The mutex for accessing the Tx pool.
mutex sync.Mutex
// Queueing channel for reading and writing incoming
// transactions to
queueChan chan *Transaction
// Quiting channel
quit chan bool
// The actual pool
pool *list.List
SecondaryProcessor TxProcessor
subscribers []chan TxMsg
}
func NewTxPool(ethereum EthManager) *TxPool {
return &TxPool{
pool: list.New(),
queueChan: make(chan *Transaction, txPoolQueueSize),
quit: make(chan bool),
Ethereum: ethereum,
}
}
// Blocking function. Don't use directly. Use QueueTransaction instead
func (pool *TxPool) addTransaction(tx *Transaction) {
pool.mutex.Lock()
defer pool.mutex.Unlock()
pool.pool.PushBack(tx)
// Broadcast the transaction to the rest of the peers
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
}
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// Get the last block so we can retrieve the sender and receiver from
// the merkle trie
block := pool.Ethereum.ChainManager().CurrentBlock
// Something has gone horribly wrong if this happens
if block == nil {
return fmt.Errorf("[TXPL] No last block on the block chain")
}
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient))
}
if tx.GasPrice.Cmp(MinGasPrice) < 0 {
return fmt.Errorf("Gas price to low. Require %v > Got %v", MinGasPrice, tx.GasPrice)
}
// Get the sender
//sender := pool.Ethereum.StateManager().procState.GetAccount(tx.Sender())
sender := pool.Ethereum.StateManager().CurrentState().GetAccount(tx.Sender())
totAmount := new(big.Int).Set(tx.Value)
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
if sender.Balance().Cmp(totAmount) < 0 {
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
}
if tx.IsContract() {
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
return fmt.Errorf("[TXPL] Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
}
}
// Increment the nonce making each tx valid only once to prevent replay
// attacks
return nil
}
func (pool *TxPool) queueHandler() {
out:
for {
select {
case tx := <-pool.queueChan:
hash := tx.Hash()
foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool {
return bytes.Compare(tx.Hash(), hash) == 0
})
if foundTx != nil {
break
}
// Validate the transaction
err := pool.ValidateTransaction(tx)
if err != nil {
txplogger.Debugln("Validating Tx failed", err)
} else {
// Call blocking version.
pool.addTransaction(tx)
tmp := make([]byte, 4)
copy(tmp, tx.Recipient)
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
// Notify the subscribers
pool.Ethereum.EventMux().Post(TxEvent{TxPre, tx})
}
case <-pool.quit:
break out
}
}
}
func (pool *TxPool) QueueTransaction(tx *Transaction) {
pool.queueChan <- tx
}
func (pool *TxPool) CurrentTransactions() []*Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
txList := make([]*Transaction, pool.pool.Len())
i := 0
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*Transaction)
txList[i] = tx
i++
}
return txList
}
func (pool *TxPool) RemoveInvalid(state *ethstate.State) {
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*Transaction)
sender := state.GetAccount(tx.Sender())
err := pool.ValidateTransaction(tx)
if err != nil || sender.Nonce >= tx.Nonce {
pool.pool.Remove(e)
}
}
}
func (pool *TxPool) Flush() []*Transaction {
txList := pool.CurrentTransactions()
// Recreate a new list all together
// XXX Is this the fastest way?
pool.pool = list.New()
return txList
}
func (pool *TxPool) Start() {
go pool.queueHandler()
}
func (pool *TxPool) Stop() {
close(pool.quit)
pool.Flush()
txplogger.Infoln("Stopped")
}

View File

@ -0,0 +1 @@
package ethchain

312
ethchain/types.go Normal file
View File

@ -0,0 +1,312 @@
package ethchain
import (
"fmt"
)
type OpCode int
// Op codes
const (
// 0x0 range - arithmetic ops
STOP = 0x00
ADD = 0x01
MUL = 0x02
SUB = 0x03
DIV = 0x04
SDIV = 0x05
MOD = 0x06
SMOD = 0x07
EXP = 0x08
NEG = 0x09
LT = 0x0a
GT = 0x0b
SLT = 0x0c
SGT = 0x0d
EQ = 0x0e
NOT = 0x0f
// 0x10 range - bit ops
AND = 0x10
OR = 0x11
XOR = 0x12
BYTE = 0x13
ADDMOD = 0x14
MULMOD = 0x15
// 0x20 range - crypto
SHA3 = 0x20
// 0x30 range - closure state
ADDRESS = 0x30
BALANCE = 0x31
ORIGIN = 0x32
CALLER = 0x33
CALLVALUE = 0x34
CALLDATALOAD = 0x35
CALLDATASIZE = 0x36
CALLDATACOPY = 0x37
CODESIZE = 0x38
CODECOPY = 0x39
GASPRICE = 0x3a
EXTCODESIZE = 0x3b
EXTCODECOPY = 0x3c
// 0x40 range - block operations
PREVHASH = 0x40
COINBASE = 0x41
TIMESTAMP = 0x42
NUMBER = 0x43
DIFFICULTY = 0x44
GASLIMIT = 0x45
// 0x50 range - 'storage' and execution
POP = 0x50
//DUP = 0x51
//SWAP = 0x52
MLOAD = 0x53
MSTORE = 0x54
MSTORE8 = 0x55
SLOAD = 0x56
SSTORE = 0x57
JUMP = 0x58
JUMPI = 0x59
PC = 0x5a
MSIZE = 0x5b
GAS = 0x5c
// 0x60 range
PUSH1 = 0x60
PUSH2 = 0x61
PUSH3 = 0x62
PUSH4 = 0x63
PUSH5 = 0x64
PUSH6 = 0x65
PUSH7 = 0x66
PUSH8 = 0x67
PUSH9 = 0x68
PUSH10 = 0x69
PUSH11 = 0x6a
PUSH12 = 0x6b
PUSH13 = 0x6c
PUSH14 = 0x6d
PUSH15 = 0x6e
PUSH16 = 0x6f
PUSH17 = 0x70
PUSH18 = 0x71
PUSH19 = 0x72
PUSH20 = 0x73
PUSH21 = 0x74
PUSH22 = 0x75
PUSH23 = 0x76
PUSH24 = 0x77
PUSH25 = 0x78
PUSH26 = 0x79
PUSH27 = 0x7a
PUSH28 = 0x7b
PUSH29 = 0x7c
PUSH30 = 0x7d
PUSH31 = 0x7e
PUSH32 = 0x7f
DUP1 = 0x80
DUP2 = 0x81
DUP3 = 0x82
DUP4 = 0x83
DUP5 = 0x84
DUP6 = 0x85
DUP7 = 0x86
DUP8 = 0x87
DUP9 = 0x88
DUP10 = 0x89
DUP11 = 0x8a
DUP12 = 0x8b
DUP13 = 0x8c
DUP14 = 0x8d
DUP15 = 0x8e
DUP16 = 0x8f
SWAP1 = 0x90
SWAP2 = 0x91
SWAP3 = 0x92
SWAP4 = 0x93
SWAP5 = 0x94
SWAP6 = 0x95
SWAP7 = 0x96
SWAP8 = 0x97
SWAP9 = 0x98
SWAP10 = 0x99
SWAP11 = 0x9a
SWAP12 = 0x9b
SWAP13 = 0x9c
SWAP14 = 0x9d
SWAP15 = 0x9e
SWAP16 = 0x9f
// 0xf0 range - closures
CREATE = 0xf0
CALL = 0xf1
RETURN = 0xf2
CALLCODE = 0xf3
// 0x70 range - other
LOG = 0xfe // XXX Unofficial
SUICIDE = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
STOP: "STOP",
ADD: "ADD",
MUL: "MUL",
SUB: "SUB",
DIV: "DIV",
SDIV: "SDIV",
MOD: "MOD",
SMOD: "SMOD",
EXP: "EXP",
NEG: "NEG",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
NOT: "NOT",
// 0x10 range - bit ops
AND: "AND",
OR: "OR",
XOR: "XOR",
BYTE: "BYTE",
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",
// 0x20 range - crypto
SHA3: "SHA3",
// 0x30 range - closure state
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "TXGASPRICE",
// 0x40 range - block operations
PREVHASH: "PREVHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution
POP: "POP",
//DUP: "DUP",
//SWAP: "SWAP",
MLOAD: "MLOAD",
MSTORE: "MSTORE",
MSTORE8: "MSTORE8",
SLOAD: "SLOAD",
SSTORE: "SSTORE",
JUMP: "JUMP",
JUMPI: "JUMPI",
PC: "PC",
MSIZE: "MSIZE",
GAS: "GAS",
// 0x60 range - push
PUSH1: "PUSH1",
PUSH2: "PUSH2",
PUSH3: "PUSH3",
PUSH4: "PUSH4",
PUSH5: "PUSH5",
PUSH6: "PUSH6",
PUSH7: "PUSH7",
PUSH8: "PUSH8",
PUSH9: "PUSH9",
PUSH10: "PUSH10",
PUSH11: "PUSH11",
PUSH12: "PUSH12",
PUSH13: "PUSH13",
PUSH14: "PUSH14",
PUSH15: "PUSH15",
PUSH16: "PUSH16",
PUSH17: "PUSH17",
PUSH18: "PUSH18",
PUSH19: "PUSH19",
PUSH20: "PUSH20",
PUSH21: "PUSH21",
PUSH22: "PUSH22",
PUSH23: "PUSH23",
PUSH24: "PUSH24",
PUSH25: "PUSH25",
PUSH26: "PUSH26",
PUSH27: "PUSH27",
PUSH28: "PUSH28",
PUSH29: "PUSH29",
PUSH30: "PUSH30",
PUSH31: "PUSH31",
PUSH32: "PUSH32",
DUP1: "DUP1",
DUP2: "DUP2",
DUP3: "DUP3",
DUP4: "DUP4",
DUP5: "DUP5",
DUP6: "DUP6",
DUP7: "DUP7",
DUP8: "DUP8",
DUP9: "DUP9",
DUP10: "DUP10",
DUP11: "DUP11",
DUP12: "DUP12",
DUP13: "DUP13",
DUP14: "DUP14",
DUP15: "DUP15",
DUP16: "DUP16",
SWAP1: "SWAP1",
SWAP2: "SWAP2",
SWAP3: "SWAP3",
SWAP4: "SWAP4",
SWAP5: "SWAP5",
SWAP6: "SWAP6",
SWAP7: "SWAP7",
SWAP8: "SWAP8",
SWAP9: "SWAP9",
SWAP10: "SWAP10",
SWAP11: "SWAP11",
SWAP12: "SWAP12",
SWAP13: "SWAP13",
SWAP14: "SWAP14",
SWAP15: "SWAP15",
SWAP16: "SWAP16",
// 0xf0 range
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
// 0x70 range - other
LOG: "LOG",
SUICIDE: "SUICIDE",
}
func (o OpCode) String() string {
str := opCodeToString[o]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o))
}
return str
}

36
ethchain/vm_env.go Normal file
View File

@ -0,0 +1,36 @@
package ethchain
import (
"math/big"
"github.com/ethereum/go-ethereum/ethstate"
"github.com/ethereum/go-ethereum/vm"
)
type VMEnv struct {
state *ethstate.State
block *Block
tx *Transaction
}
func NewEnv(state *ethstate.State, tx *Transaction, block *Block) *VMEnv {
return &VMEnv{
state: state,
block: block,
tx: tx,
}
}
func (self *VMEnv) Origin() []byte { return self.tx.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) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
func (self *VMEnv) State() *ethstate.State { return self.state }
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
return vm.Transfer(from, to, amount)
}

47
ethcrypto/crypto.go Normal file
View File

@ -0,0 +1,47 @@
package ethcrypto
import (
"crypto/sha256"
"code.google.com/p/go.crypto/ripemd160"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/obscuren/secp256k1-go"
"github.com/obscuren/sha3"
)
// TODO refactor, remove (bin)
func Sha3(data []byte) []byte {
d := sha3.NewKeccak256()
d.Write(data)
return d.Sum(nil)
}
// Creates an ethereum address given the bytes and the nonce
func CreateAddress(b []byte, nonce uint64) []byte {
return Sha3(ethutil.NewValue([]interface{}{b, nonce}).Encode())[12:]
}
func Sha256(data []byte) []byte {
hash := sha256.Sum256(data)
return hash[:]
}
func Ripemd160(data []byte) []byte {
ripemd := ripemd160.New()
ripemd.Write(data)
return ripemd.Sum(nil)
}
func Ecrecover(data []byte) []byte {
var in = struct {
hash []byte
sig []byte
}{data[:32], data[32:]}
r, _ := secp256k1.RecoverPubkey(in.hash, in.sig)
return r
}

17
ethcrypto/crypto_test.go Normal file
View File

@ -0,0 +1,17 @@
package ethcrypto
import (
"bytes"
"testing"
"github.com/ethereum/go-ethereum/ethutil"
)
// FIPS 202 test (reverted back to FIPS 180)
func TestSha3(t *testing.T) {
const exp = "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
sha3_256 := Sha3Bin([]byte("abc"))
if bytes.Compare(sha3_256, ethutil.Hex2Bytes(exp)) != 0 {
t.Errorf("Sha3_256 failed. Incorrect result %x", sha3_256)
}
}

130
ethcrypto/key_manager.go Normal file
View File

@ -0,0 +1,130 @@
package ethcrypto
import (
"fmt"
"sync"
"github.com/ethereum/go-ethereum/ethutil"
)
type KeyManager struct {
keyRing *KeyRing
session string
keyStore KeyStore // interface
keyRings map[string]*KeyRing // cache
keyPair *KeyPair
}
func NewDBKeyManager(db ethutil.Database) *KeyManager {
return &KeyManager{keyStore: &DBKeyStore{db: db}, keyRings: make(map[string]*KeyRing)}
}
func NewFileKeyManager(basedir string) *KeyManager {
return &KeyManager{keyStore: &FileKeyStore{basedir: basedir}, keyRings: make(map[string]*KeyRing)}
}
func (k *KeyManager) KeyPair() *KeyPair {
return k.keyPair
}
func (k *KeyManager) KeyRing() *KeyPair {
return k.keyPair
}
func (k *KeyManager) PrivateKey() []byte {
return k.keyPair.PrivateKey
}
func (k *KeyManager) PublicKey() []byte {
return k.keyPair.PublicKey
}
func (k *KeyManager) Address() []byte {
return k.keyPair.Address()
}
func (k *KeyManager) save(session string, keyRing *KeyRing) error {
err := k.keyStore.Save(session, keyRing)
if err != nil {
return err
}
k.keyRings[session] = keyRing
return nil
}
func (k *KeyManager) load(session string) (*KeyRing, error) {
keyRing, found := k.keyRings[session]
if !found {
var err error
keyRing, err = k.keyStore.Load(session)
if err != nil {
return nil, err
}
}
return keyRing, nil
}
func cursorError(cursor int, len int) error {
return fmt.Errorf("cursor %d out of range (0..%d)", cursor, len)
}
func (k *KeyManager) reset(session string, cursor int, keyRing *KeyRing) error {
if cursor >= keyRing.Len() {
return cursorError(cursor, keyRing.Len())
}
lock := &sync.Mutex{}
lock.Lock()
defer lock.Unlock()
err := k.save(session, keyRing)
if err != nil {
return err
}
k.session = session
k.keyRing = keyRing
k.keyPair = keyRing.GetKeyPair(cursor)
return nil
}
func (k *KeyManager) SetCursor(cursor int) error {
if cursor >= k.keyRing.Len() {
return cursorError(cursor, k.keyRing.Len())
}
k.keyPair = k.keyRing.GetKeyPair(cursor)
return nil
}
func (k *KeyManager) Init(session string, cursor int, force bool) error {
var keyRing *KeyRing
if !force {
var err error
keyRing, err = k.load(session)
if err != nil {
return err
}
}
if keyRing == nil {
keyRing = NewGeneratedKeyRing(1)
}
return k.reset(session, cursor, keyRing)
}
func (k *KeyManager) InitFromSecretsFile(session string, cursor int, secretsfile string) error {
keyRing, err := NewKeyRingFromFile(secretsfile)
if err != nil {
return err
}
return k.reset(session, cursor, keyRing)
}
func (k *KeyManager) InitFromString(session string, cursor int, secrets string) error {
keyRing, err := NewKeyRingFromString(secrets)
if err != nil {
return err
}
return k.reset(session, cursor, keyRing)
}
func (k *KeyManager) Export(dir string) error {
fileKeyStore := FileKeyStore{dir}
return fileKeyStore.Save(k.session, k.keyRing)
}

113
ethcrypto/key_store.go Normal file
View File

@ -0,0 +1,113 @@
package ethcrypto
import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"github.com/ethereum/go-ethereum/ethutil"
)
type KeyStore interface {
Load(string) (*KeyRing, error)
Save(string, *KeyRing) error
}
type DBKeyStore struct {
db ethutil.Database
}
const dbKeyPrefix = "KeyRing"
func (k *DBKeyStore) dbKey(session string) []byte {
return []byte(fmt.Sprintf("%s%s", dbKeyPrefix, session))
}
func (k *DBKeyStore) Save(session string, keyRing *KeyRing) error {
k.db.Put(k.dbKey(session), keyRing.RlpEncode())
return nil
}
func (k *DBKeyStore) Load(session string) (*KeyRing, error) {
data, err := k.db.Get(k.dbKey(session))
if err != nil {
return nil, nil
}
var keyRing *KeyRing
keyRing, err = NewKeyRingFromBytes(data)
if err != nil {
return nil, err
}
// if empty keyRing is found we return nil, no error
if keyRing.Len() == 0 {
return nil, nil
}
return keyRing, nil
}
type FileKeyStore struct {
basedir string
}
func (k *FileKeyStore) Save(session string, keyRing *KeyRing) error {
var content []byte
var err error
var privateKeys []string
var publicKeys []string
var mnemonics []string
var addresses []string
keyRing.Each(func(keyPair *KeyPair) {
privateKeys = append(privateKeys, ethutil.Bytes2Hex(keyPair.PrivateKey))
publicKeys = append(publicKeys, ethutil.Bytes2Hex(keyPair.PublicKey))
addresses = append(addresses, ethutil.Bytes2Hex(keyPair.Address()))
mnemonics = append(mnemonics, keyPair.Mnemonic())
})
basename := session
if session == "" {
basename = "default"
}
path := path.Join(k.basedir, basename)
content = []byte(strings.Join(privateKeys, "\n"))
err = ioutil.WriteFile(path+".prv", content, 0600)
if err != nil {
return err
}
content = []byte(strings.Join(publicKeys, "\n"))
err = ioutil.WriteFile(path+".pub", content, 0644)
if err != nil {
return err
}
content = []byte(strings.Join(addresses, "\n"))
err = ioutil.WriteFile(path+".addr", content, 0644)
if err != nil {
return err
}
content = []byte(strings.Join(mnemonics, "\n"))
err = ioutil.WriteFile(path+".mne", content, 0600)
if err != nil {
return err
}
return nil
}
func (k *FileKeyStore) Load(session string) (*KeyRing, error) {
basename := session
if session == "" {
basename = "default"
}
secfile := path.Join(k.basedir, basename+".prv")
_, err := os.Stat(secfile)
// if file is not found then we return nil, no error
if err != nil {
return nil, nil
}
return NewKeyRingFromFile(secfile)
}

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