Merge eth-go repository into go-ethereum
mist, etheruem have been moved to cmd/
3
.gitignore
vendored
@ -9,6 +9,3 @@
|
|||||||
*un~
|
*un~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/**/.DS_Store
|
*/**/.DS_Store
|
||||||
ethereum/ethereum
|
|
||||||
ethereal/ethereal
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
8
LICENSE
@ -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
|
||||||
|
60
README.md
@ -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
@ -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
@ -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
|
@ -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"
|
||||||
)
|
)
|
@ -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 (
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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 (
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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"
|
@ -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"
|
||||||
)
|
)
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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")
|
||||||
|
}
|
||||||
|
}
|
1
ethchain/chain_manager_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package ethchain
|
239
ethchain/dagger.go
Normal 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
@ -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
@ -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
@ -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
@ -0,0 +1,7 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var BlockReward *big.Int = big.NewInt(1.5e+18)
|
206
ethchain/filter.go
Normal 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
@ -0,0 +1,7 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
NewFilter(NewTestManager())
|
||||||
|
}
|
48
ethchain/genesis.go
Normal 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
@ -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
@ -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
|
||||||
|
}
|
276
ethchain/state_transition.go
Normal 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(ðstate.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
@ -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
|
||||||
|
}
|
224
ethchain/transaction_pool.go
Normal 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")
|
||||||
|
}
|
1
ethchain/transaction_test.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package ethchain
|
312
ethchain/types.go
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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)
|
||||||
|
}
|