Merge branch 'release/0.6.3'

This commit is contained in:
obscuren 2014-08-21 15:46:26 +02:00
commit 0af0f0d890
64 changed files with 4472 additions and 3122 deletions

31
LICENSE
View File

@ -1,21 +1,16 @@
The MIT License (MIT) Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
Copyright (c) 2013 Jeffrey Wilcke This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
Permission is hereby granted, free of charge, to any person obtaining a copy This library is distributed in the hope that it will be useful,
of this software and associated documentation files (the "Software"), to deal but WITHOUT ANY WARRANTY; without even the implied warranty of
in the Software without restriction, including without limitation the rights MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell Lesser General Public License for more details.
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in You should have received a copy of the GNU Lesser General Public
all copies or substantial portions of the Software. License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR MA 02110-1301 USA
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof Ethereum is currently in its testing phase. The current state is "Proof
of Concept 0.6.0". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). of Concept 0.6.3". 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 Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.
@ -15,6 +15,11 @@ individual package for more information.
3. [ethwire](https://github.com/ethereum/eth-go/tree/master/ethwire) 3. [ethwire](https://github.com/ethereum/eth-go/tree/master/ethwire)
4. [ethdb](https://github.com/ethereum/eth-go/tree/master/ethdb) 4. [ethdb](https://github.com/ethereum/eth-go/tree/master/ethdb)
5. [ethutil](https://github.com/ethereum/eth-go/tree/master/ethutil) 5. [ethutil](https://github.com/ethereum/eth-go/tree/master/ethutil)
6. [ethpipe](https://github.com/ethereum/eth-go/tree/master/ethpipe)
7. [ethvm](https://github.com/ethereum/eth-go/tree/master/ethvm)
8. [ethtrie](https://github.com/ethereum/eth-go/tree/master/ethtrie)
9. [ethreact](https://github.com/ethereum/eth-go/tree/master/ethreact)
10. [ethlog](https://github.com/ethereum/eth-go/tree/master/ethlog)
The [eth](https://github.com/ethereum/eth-go) is the top-level package The [eth](https://github.com/ethereum/eth-go) is the top-level package
of the Ethereum protocol. It functions as the Ethereum bootstrapping and of the Ethereum protocol. It functions as the Ethereum bootstrapping and
@ -45,7 +50,7 @@ Contribution
If you'd like to contribute to Eth please fork, fix, commit and If you'd like to contribute to Eth please fork, fix, commit and
send a pull request. Commits who do not comply with the coding standards send a pull request. Commits who do not comply with the coding standards
are ignored. If you send pull requests make absolute sure that you are ignored (use gofmt!). If you send pull requests make absolute sure that you
commit on 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 on master are simply ignored. Commits that are directly based on master are simply ignored.

116
block_pool.go Normal file
View File

@ -0,0 +1,116 @@
package eth
import (
"math"
"math/big"
"sync"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
)
type block struct {
peer *Peer
block *ethchain.Block
}
type BlockPool struct {
mut sync.Mutex
eth *Ethereum
hashPool [][]byte
pool map[string]*block
td *big.Int
}
func NewBlockPool(eth *Ethereum) *BlockPool {
return &BlockPool{
eth: eth,
pool: make(map[string]*block),
td: ethutil.Big0,
}
}
func (self *BlockPool) HasLatestHash() bool {
return self.pool[string(self.eth.BlockChain().CurrentBlock.Hash())] != nil
}
func (self *BlockPool) HasCommonHash(hash []byte) bool {
return self.eth.BlockChain().GetBlock(hash) != nil
}
func (self *BlockPool) AddHash(hash []byte) {
if self.pool[string(hash)] == nil {
self.pool[string(hash)] = &block{nil, nil}
self.hashPool = append([][]byte{hash}, self.hashPool...)
}
}
func (self *BlockPool) SetBlock(b *ethchain.Block) {
hash := string(b.Hash())
if self.pool[string(hash)] == nil {
self.pool[hash] = &block{nil, nil}
}
self.pool[hash].block = b
}
func (self *BlockPool) CheckLinkAndProcess(f func(block *ethchain.Block)) bool {
self.mut.Lock()
defer self.mut.Unlock()
if self.IsLinked() {
for i, hash := range self.hashPool {
block := self.pool[string(hash)].block
if block != nil {
f(block)
delete(self.pool, string(hash))
} else {
self.hashPool = self.hashPool[i:]
return false
}
}
return true
}
return false
}
func (self *BlockPool) IsLinked() bool {
if len(self.hashPool) == 0 {
return false
}
block := self.pool[string(self.hashPool[0])].block
if block != nil {
return self.eth.BlockChain().HasBlock(block.PrevHash)
}
return false
}
func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) {
self.mut.Lock()
defer self.mut.Unlock()
num := int(math.Min(float64(amount), float64(len(self.pool))))
j := 0
for i := 0; i < len(self.hashPool) && j < num; i++ {
hash := string(self.hashPool[i])
if self.pool[hash].peer == nil || self.pool[hash].peer == peer {
self.pool[hash].peer = peer
hashes = append(hashes, self.hashPool[i])
j++
}
}
return
}

View File

@ -3,13 +3,14 @@ package ethchain
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math/big"
_ "strconv"
"time"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big"
_ "strconv"
"time"
) )
type BlockInfo struct { type BlockInfo struct {
@ -63,12 +64,6 @@ type Block struct {
TxSha []byte TxSha []byte
} }
// New block takes a raw encoded string
// XXX DEPRICATED
func NewBlockFromData(raw []byte) *Block {
return NewBlockFromBytes(raw)
}
func NewBlockFromBytes(raw []byte) *Block { func NewBlockFromBytes(raw []byte) *Block {
block := &Block{} block := &Block{}
block.RlpDecode(raw) block.RlpDecode(raw)
@ -105,7 +100,7 @@ func CreateBlock(root interface{},
} }
block.SetUncles([]*Block{}) block.SetUncles([]*Block{})
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, root)) block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, root))
return block return block
} }
@ -130,40 +125,15 @@ func (block *Block) Transactions() []*Transaction {
return block.transactions return block.transactions
} }
func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
contract := block.state.GetStateObject(addr)
// If we can't pay the fee return
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
fmt.Println("Contract has insufficient funds", contract.Amount, fee)
return false
}
base := new(big.Int)
contract.Amount = base.Sub(contract.Amount, fee)
block.state.Trie.Update(string(addr), string(contract.RlpEncode()))
data := block.state.Trie.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner)
account := ethstate.NewStateObjectFromBytes(block.Coinbase, []byte(data))
base = new(big.Int)
account.Amount = base.Add(account.Amount, fee)
//block.state.Trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
block.state.UpdateStateObject(account)
return true
}
func (block *Block) CalcGasLimit(parent *Block) *big.Int { func (block *Block) CalcGasLimit(parent *Block) *big.Int {
if block.Number.Cmp(big.NewInt(0)) == 0 { if block.Number.Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6) return ethutil.BigPow(10, 6)
} }
previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit) // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5))
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()) curInt := new(big.Int).Div(current.Num(), current.Denom())
result := new(big.Int).Add(previous, curInt) result := new(big.Int).Add(previous, curInt)
@ -172,19 +142,6 @@ func (block *Block) CalcGasLimit(parent *Block) *big.Int {
min := big.NewInt(125000) min := big.NewInt(125000)
return ethutil.BigMax(min, result) return ethutil.BigMax(min, result)
/*
base := new(big.Int)
base2 := new(big.Int)
parentGL := bc.CurrentBlock.GasLimit
parentUsed := bc.CurrentBlock.GasUsed
base.Mul(parentGL, big.NewInt(1024-1))
base2.Mul(parentUsed, big.NewInt(6))
base2.Div(base2, big.NewInt(5))
base.Add(base, base2)
base.Div(base, big.NewInt(1024))
*/
} }
func (block *Block) BlockInfo() BlockInfo { func (block *Block) BlockInfo() BlockInfo {
@ -252,26 +209,10 @@ func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) {
func (block *Block) setTransactions(txs []*Transaction) { func (block *Block) setTransactions(txs []*Transaction) {
block.transactions = txs block.transactions = txs
/*
trie := ethtrie.NewTrie(ethutil.Config.Db, "")
for i, tx := range txs {
trie.Update(strconv.Itoa(i), string(tx.RlpEncode()))
}
switch trie.Root.(type) {
case string:
block.TxSha = []byte(trie.Root.(string))
case []byte:
block.TxSha = trie.Root.([]byte)
default:
panic(fmt.Sprintf("invalid root type %T", trie.Root))
}
*/
} }
func CreateTxSha(receipts Receipts) (sha []byte) { func CreateTxSha(receipts Receipts) (sha []byte) {
trie := ethtrie.NewTrie(ethutil.Config.Db, "") trie := ethtrie.New(ethutil.Config.Db, "")
for i, receipt := range receipts { for i, receipt := range receipts {
trie.Update(string(ethutil.NewValue(i).Encode()), string(ethutil.NewValue(receipt.RlpData()).Encode())) trie.Update(string(ethutil.NewValue(i).Encode()), string(ethutil.NewValue(receipt.RlpData()).Encode()))
} }
@ -313,7 +254,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
block.PrevHash = header.Get(0).Bytes() block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes() block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes() block.Coinbase = header.Get(2).Bytes()
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val)) block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes() block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt() block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt() block.Number = header.Get(6).BigInt()
@ -355,7 +296,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
block.PrevHash = header.Get(0).Bytes() block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes() block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes() block.Coinbase = header.Get(2).Bytes()
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val)) block.state = ethstate.New(ethtrie.New(ethutil.Config.Db, header.Get(3).Val))
block.TxSha = header.Get(4).Bytes() block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt() block.Difficulty = header.Get(5).BigInt()
block.Number = header.Get(6).BigInt() block.Number = header.Get(6).BigInt()

View File

@ -2,11 +2,12 @@ package ethchain
import ( import (
"bytes" "bytes"
"math"
"math/big"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"math"
"math/big"
) )
var chainlogger = ethlog.NewLogger("CHAIN") var chainlogger = ethlog.NewLogger("CHAIN")
@ -131,7 +132,7 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte
// Start with the newest block we got, all the way back to the common block we both know // Start with the newest block we got, all the way back to the common block we both know
for _, block := range blocks { for _, block := range blocks {
if bytes.Compare(block.Hash(), commonBlockHash) == 0 { if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
chainlogger.Infoln("[CHAIN] We have found the common parent block, breaking") chainlogger.Infoln("We have found the common parent block, breaking")
break break
} }
chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block)) chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
@ -144,13 +145,13 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte
for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) { for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
i++ i++
if bytes.Compare(block.Hash(), commonBlockHash) == 0 { if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
chainlogger.Infoln("We have found the common parent block, breaking") chainlogger.Infoln("Found the common parent block")
break break
} }
anOtherBlock := bc.GetBlock(block.PrevHash) anOtherBlock := bc.GetBlock(block.PrevHash)
if anOtherBlock == nil { if anOtherBlock == nil {
// We do not want to count the genesis block for difficulty since that's not being sent // We do not want to count the genesis block for difficulty since that's not being sent
chainlogger.Infoln("At genesis block, breaking") chainlogger.Infoln("Found genesis block. Stop")
break break
} }
curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
@ -158,11 +159,11 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte
chainlogger.Infoln("Current chain difficulty:", curChainDifficulty) chainlogger.Infoln("Current chain difficulty:", curChainDifficulty)
if chainDifficulty.Cmp(curChainDifficulty) == 1 { if chainDifficulty.Cmp(curChainDifficulty) == 1 {
chainlogger.Infof("The incoming Chain beat our asses, resetting to block: %x", commonBlockHash) chainlogger.Infof("Resetting to block %x. Changing chain.")
bc.ResetTillBlockHash(commonBlockHash) bc.ResetTillBlockHash(commonBlockHash)
return false return false
} else { } else {
chainlogger.Infoln("Our chain showed the incoming chain who is boss. Ignoring.") chainlogger.Infoln("Current chain is longest chain. Ignoring incoming chain.")
return true return true
} }
} }
@ -207,6 +208,26 @@ func (bc *BlockChain) GenesisBlock() *Block {
return bc.genesisBlock return bc.genesisBlock
} }
func (self *BlockChain) 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
}
// Get chain return blocks from hash up to max in RLP format // Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
var chain []interface{} var chain []interface{}
@ -280,7 +301,7 @@ func AddTestNetFunds(block *Block) {
} { } {
codedAddr := ethutil.Hex2Bytes(addr) codedAddr := ethutil.Hex2Bytes(addr)
account := block.state.GetAccount(codedAddr) account := block.state.GetAccount(codedAddr)
account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200) account.Balance = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account) block.state.UpdateStateObject(account)
} }
} }
@ -289,7 +310,6 @@ func (bc *BlockChain) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 { if len(data) != 0 {
block := NewBlockFromBytes(data) block := NewBlockFromBytes(data)
//info := bc.BlockInfo(block)
bc.CurrentBlock = block bc.CurrentBlock = block
bc.LastBlockHash = block.Hash() bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64() bc.LastBlockNumber = block.Number.Uint64()
@ -300,9 +320,8 @@ func (bc *BlockChain) setLastBlock() {
bc.genesisBlock.state.Trie.Sync() bc.genesisBlock.state.Trie.Sync()
// Prepare the genesis block // Prepare the genesis block
bc.Add(bc.genesisBlock) bc.Add(bc.genesisBlock)
fk := append([]byte("bloom"), bc.genesisBlock.Hash()...)
//chainlogger.Infof("root %x\n", bm.bc.genesisBlock.State().Root) bc.Ethereum.Db().Put(fk, make([]byte, 255))
//bm.bc.genesisBlock.PrintHash()
} }
// Set the last know difficulty (might be 0x0 as initial value, Genesis) // Set the last know difficulty (might be 0x0 as initial value, Genesis)
@ -338,6 +357,18 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block {
return NewBlockFromBytes(data) return NewBlockFromBytes(data)
} }
func (self *BlockChain) GetBlockByNumber(num uint64) *Block {
block := self.CurrentBlock
for ; block.Number.Uint64() != num; block = self.GetBlock(block.PrevHash) {
}
if block.Number.Uint64() == 0 && num != 0 {
return nil
}
return block
}
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo { func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
bi := BlockInfo{} bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...)) data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))

View File

@ -3,16 +3,19 @@ package ethchain
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"testing"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"testing"
) )
// Implement our EthTest Manager // Implement our EthTest Manager
type TestManager struct { type TestManager struct {
stateManager *StateManager stateManager *StateManager
reactor *ethutil.ReactorEngine reactor *ethreact.ReactorEngine
txPool *TxPool txPool *TxPool
blockChain *BlockChain blockChain *BlockChain
@ -47,16 +50,24 @@ func (tm *TestManager) StateManager() *StateManager {
return tm.stateManager return tm.stateManager
} }
func (tm *TestManager) Reactor() *ethutil.ReactorEngine { func (tm *TestManager) Reactor() *ethreact.ReactorEngine {
return tm.reactor return tm.reactor
} }
func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) { func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) {
fmt.Println("Broadcast not implemented") fmt.Println("Broadcast not implemented")
} }
func NewTestManager() *TestManager { func (tm *TestManager) ClientIdentity() ethwire.ClientIdentity {
return nil
}
func (tm *TestManager) KeyManager() *ethcrypto.KeyManager {
return nil
}
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH") func (tm *TestManager) Db() ethutil.Database { return nil }
func NewTestManager() *TestManager {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH")
db, err := ethdb.NewMemDatabase() db, err := ethdb.NewMemDatabase()
if err != nil { if err != nil {
@ -66,7 +77,7 @@ func NewTestManager() *TestManager {
ethutil.Config.Db = db ethutil.Config.Db = db
testManager := &TestManager{} testManager := &TestManager{}
testManager.reactor = ethutil.NewReactorEngine() testManager.reactor = ethreact.New()
testManager.txPool = NewTxPool(testManager) testManager.txPool = NewTxPool(testManager)
testManager.blockChain = NewBlockChain(testManager) testManager.blockChain = NewBlockChain(testManager)

47
ethchain/bloom.go Normal file
View File

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

20
ethchain/bloom_test.go Normal file
View File

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

View File

@ -3,6 +3,7 @@ package ethchain
import ( import (
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/sha3" "github.com/obscuren/sha3"
"hash" "hash"
@ -14,7 +15,7 @@ import (
var powlogger = ethlog.NewLogger("POW") var powlogger = ethlog.NewLogger("POW")
type PoW interface { type PoW interface {
Search(block *Block, reactChan chan ethutil.React) []byte Search(block *Block, reactChan chan ethreact.Event) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool Verify(hash []byte, diff *big.Int, nonce []byte) bool
GetHashrate() int64 GetHashrate() int64
} }
@ -28,7 +29,7 @@ func (pow *EasyPow) GetHashrate() int64 {
return pow.HashRate return pow.HashRate
} }
func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte { func (pow *EasyPow) Search(block *Block, reactChan chan ethreact.Event) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce() hash := block.HashNoNonce()
diff := block.Difficulty diff := block.Difficulty

304
ethchain/filter.go Normal file
View File

@ -0,0 +1,304 @@
package ethchain
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"gopkg.in/qml.v1"
)
type data struct {
id, address []byte
}
// Filtering interface
type Filter struct {
eth EthManager
earliest []byte
latest []byte
skip int
from, to [][]byte
max int
altered []data
}
// 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 NewFilterFromMap(object map[string]interface{}, eth EthManager) *Filter {
filter := NewFilter(eth)
if object["earliest"] != nil {
earliest := object["earliest"]
if e, ok := earliest.(string); ok {
filter.SetEarliestBlock(ethutil.Hex2Bytes(e))
} else {
filter.SetEarliestBlock(earliest)
}
}
if object["latest"] != nil {
latest := object["latest"]
if l, ok := latest.(string); ok {
filter.SetLatestBlock(ethutil.Hex2Bytes(l))
} else {
filter.SetLatestBlock(latest)
}
}
if object["to"] != nil {
filter.AddTo(ethutil.Hex2Bytes(object["to"].(string)))
}
if object["from"] != nil {
filter.AddFrom(ethutil.Hex2Bytes(object["from"].(string)))
}
if object["max"] != nil {
filter.SetMax(object["max"].(int))
}
if object["skip"] != nil {
filter.SetSkip(object["skip"].(int))
}
if object["altered"] != nil {
filter.altered = makeAltered(object["altered"])
}
return filter
}
func (self *Filter) AddAltered(id, address []byte) {
self.altered = append(self.altered, data{id, address})
}
// 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 interface{}) {
e := ethutil.NewValue(earliest)
// Check for -1 (latest) otherwise assume bytes
if e.Int() == -1 {
self.earliest = self.eth.BlockChain().CurrentBlock.Hash()
} else if e.Len() > 0 {
self.earliest = e.Bytes()
} else {
panic(fmt.Sprintf("earliest has to be either -1 or a valid hash: %v (%T)", e, e.Val))
}
}
func (self *Filter) SetLatestBlock(latest interface{}) {
l := ethutil.NewValue(latest)
// Check for -1 (latest) otherwise assume bytes
if l.Int() == -1 {
self.latest = self.eth.BlockChain().CurrentBlock.Hash()
} else if l.Len() > 0 {
self.latest = l.Bytes()
} else {
panic(fmt.Sprintf("latest has to be either -1 or a valid hash: %v", l))
}
}
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 messages []*ethstate.Message
block := self.eth.BlockChain().GetBlock(self.latest)
// skip N blocks (useful for pagination)
if self.skip > 0 {
for i := 0; i < i; i++ {
block = self.eth.BlockChain().GetBlock(block.PrevHash)
}
}
// Start block filtering
quit := false
for i := 1; !quit && block != nil; i++ {
// Mark last check
if self.max == i || (len(self.earliest) > 0 && bytes.Compare(block.Hash(), self.earliest) == 0) {
quit = true
}
// 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.BlockChain().GetBlock(block.PrevHash)
}
return messages
}
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 _, item := range self.altered {
if len(item.id) > 0 && bytes.Compare(message.To, item.id) != 0 {
continue
}
if len(item.address) > 0 && !includes(message.ChangedAddresses, item.address) {
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 {
panic(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
}
// Conversion methodn
func mapToData(m map[string]interface{}) (d data) {
if str, ok := m["id"].(string); ok {
d.id = ethutil.Hex2Bytes(str)
}
if str, ok := m["at"].(string); ok {
d.address = ethutil.Hex2Bytes(str)
}
return
}
// data can come in in the following formats:
// ["aabbccdd", {id: "ccddee", at: "11223344"}], "aabbcc", {id: "ccddee", at: "1122"}
func makeAltered(v interface{}) (d []data) {
if str, ok := v.(string); ok {
d = append(d, data{ethutil.Hex2Bytes(str), nil})
} else if obj, ok := v.(map[string]interface{}); ok {
d = append(d, mapToData(obj))
} else if slice, ok := v.([]interface{}); ok {
for _, item := range slice {
d = append(d, makeAltered(item)...)
}
} else if qList, ok := v.(*qml.List); ok {
var s []interface{}
qList.Convert(&s)
fmt.Println(s)
d = makeAltered(s)
} else if qMap, ok := v.(*qml.Map); ok {
var m map[string]interface{}
qMap.Convert(&m)
fmt.Println(m)
d = makeAltered(m)
} else {
panic(fmt.Sprintf("makeAltered err (unknown conversion): %T\n", v))
}
return
}

7
ethchain/filter_test.go Normal file
View File

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

View File

@ -1,9 +1,10 @@
package ethchain package ethchain
import ( import (
"math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big"
) )
/* /*
@ -26,7 +27,8 @@ var GenesisHeader = []interface{}{
// tx sha // tx sha
"", "",
// Difficulty // Difficulty
ethutil.BigPow(2, 22), //ethutil.BigPow(2, 22),
big.NewInt(4096),
// Number // Number
ethutil.Big0, ethutil.Big0,
// Block minimum gas price // Block minimum gas price

View File

@ -4,14 +4,16 @@ import (
"bytes" "bytes"
"container/list" "container/list"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"math/big" "math/big"
"sync" "sync"
"time" "time"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
) )
var statelogger = ethlog.NewLogger("STATE") var statelogger = ethlog.NewLogger("STATE")
@ -36,13 +38,14 @@ type EthManager interface {
BlockChain() *BlockChain BlockChain() *BlockChain
TxPool() *TxPool TxPool() *TxPool
Broadcast(msgType ethwire.MsgType, data []interface{}) Broadcast(msgType ethwire.MsgType, data []interface{})
Reactor() *ethutil.ReactorEngine Reactor() *ethreact.ReactorEngine
PeerCount() int PeerCount() int
IsMining() bool IsMining() bool
IsListening() bool IsListening() bool
Peers() *list.List Peers() *list.List
KeyManager() *ethcrypto.KeyManager KeyManager() *ethcrypto.KeyManager
ClientIdentity() ethwire.ClientIdentity ClientIdentity() ethwire.ClientIdentity
Db() ethutil.Database
} }
type StateManager struct { type StateManager struct {
@ -233,7 +236,12 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
// Add the block to the chain // Add the block to the chain
sm.bc.Add(block) sm.bc.Add(block)
sm.notifyChanges(state)
// Create a bloom bin for this block
filter := sm.createBloomFilter(state)
// Persist the data
fk := append([]byte("bloom"), block.Hash()...)
sm.Ethereum.Db().Put(fk, filter.Bin())
statelogger.Infof("Added block #%d (%x)\n", block.Number, block.Hash()) statelogger.Infof("Added block #%d (%x)\n", block.Number, block.Hash())
if dontReact == false { if dontReact == false {
@ -361,14 +369,56 @@ func (sm *StateManager) Stop() {
sm.bc.Stop() sm.bc.Stop()
} }
func (sm *StateManager) notifyChanges(state *ethstate.State) { // Manifest will handle both creating notifications and generating bloom bin data
for addr, stateObject := range state.Manifest().ObjectChanges { func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter {
sm.Ethereum.Reactor().Post("object:"+addr, stateObject) bloomf := NewBloomFilter(nil)
/*
for addr, stateObject := range state.Manifest().ObjectChanges {
// Set the bloom filter's bin
bloomf.Set([]byte(addr))
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
}
*/
for _, msg := range state.Manifest().Messages {
bloomf.Set(msg.To)
bloomf.Set(msg.From)
} }
for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges { sm.Ethereum.Reactor().Post("messages", state.Manifest().Messages)
for addr, value := range mappedObjects {
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &ethstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value}) /*
for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges {
for addr, value := range mappedObjects {
// Set the bloom filter's bin
bloomf.Set(ethcrypto.Sha3Bin([]byte(stateObjectAddr + addr)))
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &ethstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value})
}
} }
} */
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)
return state.Manifest().Messages, nil
} }

View File

@ -2,11 +2,12 @@ package ethchain
import ( import (
"fmt" "fmt"
"math/big"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethvm" "github.com/ethereum/eth-go/ethvm"
"math/big"
) )
/* /*
@ -94,8 +95,8 @@ func (self *StateTransition) BuyGas() error {
var err error var err error
sender := self.Sender() sender := self.Sender()
if sender.Amount.Cmp(self.tx.GasValue()) < 0 { 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.Amount) return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance)
} }
coinbase := self.Coinbase() coinbase := self.Coinbase()
@ -178,8 +179,8 @@ func (self *StateTransition) TransitionState() (err error) {
return return
} }
if sender.Amount.Cmp(self.value) < 0 { if sender.Balance.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
} }
var snapshot *ethstate.State var snapshot *ethstate.State
@ -210,6 +211,14 @@ func (self *StateTransition) TransitionState() (err error) {
snapshot = self.state.Copy() snapshot = self.state.Copy()
} }
msg := self.state.Manifest().AddMessage(&ethstate.Message{
To: receiver.Address(), From: sender.Address(),
Input: self.tx.Data,
Origin: sender.Address(),
Block: self.block.Hash(), Timestamp: self.block.Time, Coinbase: self.block.Coinbase, Number: self.block.Number,
Value: self.value,
})
// Process the init code and create 'valid' contract // Process the init code and create 'valid' contract
if IsContractAddr(self.receiver) { if IsContractAddr(self.receiver) {
// Evaluate the initialization script // Evaluate the initialization script
@ -217,7 +226,7 @@ func (self *StateTransition) TransitionState() (err error) {
// script section for the state object. // script section for the state object.
self.data = nil self.data = nil
code, err := self.Eval(receiver.Init(), receiver, "init") code, err := self.Eval(msg, receiver.Init(), receiver, "init")
if err != nil { if err != nil {
self.state.Set(snapshot) self.state.Set(snapshot)
@ -225,14 +234,17 @@ func (self *StateTransition) TransitionState() (err error) {
} }
receiver.Code = code receiver.Code = code
msg.Output = code
} else { } else {
if len(receiver.Code) > 0 { if len(receiver.Code) > 0 {
_, err = self.Eval(receiver.Code, receiver, "code") ret, err := self.Eval(msg, receiver.Code, receiver, "code")
if err != nil { if err != nil {
self.state.Set(snapshot) self.state.Set(snapshot)
return fmt.Errorf("Error during code execution %v", err) return fmt.Errorf("Error during code execution %v", err)
} }
msg.Output = ret
} }
} }
@ -240,8 +252,8 @@ func (self *StateTransition) TransitionState() (err error) {
} }
func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObject) error { func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObject) error {
if sender.Amount.Cmp(self.value) < 0 { if sender.Balance.Cmp(self.value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
} }
// Subtract the amount from the senders account // Subtract the amount from the senders account
@ -252,12 +264,12 @@ func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObjec
return nil return nil
} }
func (self *StateTransition) Eval(script []byte, context *ethstate.StateObject, typ string) (ret []byte, err error) { func (self *StateTransition) Eval(msg *ethstate.Message, script []byte, context *ethstate.StateObject, typ string) (ret []byte, err error) {
var ( var (
transactor = self.Sender() transactor = self.Sender()
state = self.state state = self.state
env = NewEnv(state, self.tx, self.block) env = NewEnv(state, self.tx, self.block)
callerClosure = ethvm.NewClosure(transactor, context, script, self.gas, self.gasPrice) callerClosure = ethvm.NewClosure(msg, transactor, context, script, self.gas, self.gasPrice)
) )
vm := ethvm.New(env) vm := ethvm.New(env)
@ -277,7 +289,7 @@ func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject
contract := state.NewStateObject(addr) contract := state.NewStateObject(addr)
contract.InitCode = tx.Data contract.InitCode = tx.Data
contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) contract.State = ethstate.New(ethtrie.New(ethutil.Config.Db, ""))
return contract return contract
} }

View File

@ -3,10 +3,11 @@ package ethchain
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
"math/big"
) )
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@ -130,7 +131,7 @@ func (tx *Transaction) RlpData() interface{} {
// TODO Remove prefixing zero's // TODO Remove prefixing zero's
return append(data, tx.v, tx.r, tx.s) return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
} }
func (tx *Transaction) RlpValue() *ethutil.Value { func (tx *Transaction) RlpValue() *ethutil.Value {

View File

@ -4,11 +4,12 @@ import (
"bytes" "bytes"
"container/list" "container/list"
"fmt" "fmt"
"math/big"
"sync"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"math/big"
"sync"
) )
var txplogger = ethlog.NewLogger("TXP") var txplogger = ethlog.NewLogger("TXP")
@ -91,78 +92,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()}) pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
} }
/*
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) {
fmt.Printf("state root before update %x\n", state.Root())
defer func() {
if r := recover(); r != nil {
txplogger.Infoln(r)
err = fmt.Errorf("%v", r)
}
}()
gas = new(big.Int)
addGas := func(g *big.Int) { gas.Add(gas, g) }
addGas(GasTx)
// Get the sender
sender := state.GetAccount(tx.Sender())
if sender.Nonce != tx.Nonce {
err = NonceError(tx.Nonce, sender.Nonce)
return
}
sender.Nonce += 1
defer func() {
//state.UpdateStateObject(sender)
// Notify all subscribers
pool.Ethereum.Reactor().Post("newTx:post", tx)
}()
txTotalBytes := big.NewInt(int64(len(tx.Data)))
txTotalBytes.Div(txTotalBytes, ethutil.Big32)
addGas(new(big.Int).Mul(txTotalBytes, GasSStore))
rGas := new(big.Int).Set(gas)
rGas.Mul(gas, tx.GasPrice)
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
totAmount := new(big.Int).Add(tx.Value, rGas)
if sender.Amount.Cmp(totAmount) < 0 {
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return
}
state.UpdateStateObject(sender)
fmt.Printf("state root after sender update %x\n", state.Root())
// Get the receiver
receiver := state.GetAccount(tx.Recipient)
// Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
// Subtract the fee
sender.SubAmount(rGas)
} else {
// Subtract the amount from the senders account
sender.SubAmount(totAmount)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver)
fmt.Printf("state root after receiver update %x\n", state.Root())
}
txplogger.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
return
}
*/
func (pool *TxPool) ValidateTransaction(tx *Transaction) error { func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// Get the last block so we can retrieve the sender and receiver from // Get the last block so we can retrieve the sender and receiver from
// the merkle trie // the merkle trie
@ -183,7 +112,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
totAmount := new(big.Int).Set(tx.Value) totAmount := new(big.Int).Set(tx.Value)
// Make sure there's enough in the sender's account. Having insufficient // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 { if sender.Balance.Cmp(totAmount) < 0 {
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
} }

View File

@ -1,8 +1,9 @@
package ethchain package ethchain
import ( import (
"github.com/ethereum/eth-go/ethstate"
"math/big" "math/big"
"github.com/ethereum/eth-go/ethstate"
) )
type VMEnv struct { type VMEnv struct {
@ -25,5 +26,6 @@ func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
func (self *VMEnv) Time() int64 { return self.block.Time } func (self *VMEnv) Time() int64 { return self.block.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.tx.Value } func (self *VMEnv) Value() *big.Int { return self.tx.Value }
func (self *VMEnv) State() *ethstate.State { return self.state } func (self *VMEnv) State() *ethstate.State { return self.state }

View File

@ -2,40 +2,9 @@ package ethcrypto
import ( import (
"fmt" "fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv" "strconv"
"strings"
) )
func InitWords(wordsPath string) {
filename := path.Join(wordsPath, "mnemonic.words.lst")
if _, err := os.Stat(filename); os.IsNotExist(err) {
fmt.Printf("reading mnemonic word list file from supplied path not found. Looked in %s. Trying next option.\n", filename)
dir := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "eth-go", "ethcrypto")
filename = path.Join(dir, "mnemonic.words.lst")
if _, err := os.Stat(filename); os.IsNotExist(err) {
fmt.Printf("reading mnemonic word list file 'mnemonic.words.lst' from source folder failed: %s.\n", filename)
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(fmt.Errorf("problem getting current folder: ", err))
}
filename = path.Join(dir, "mnemonic.words.lst")
}
}
content, err := ioutil.ReadFile(filename)
if err != nil {
panic(fmt.Errorf("All options for finding the mnemonic word list file 'mnemonic.words.lst' failed: ", err))
}
words = strings.Split(string(content), "\n")
}
var words []string
// TODO: See if we can refactor this into a shared util lib if we need it multiple times // TODO: See if we can refactor this into a shared util lib if we need it multiple times
func IndexOf(slice []string, value string) int64 { func IndexOf(slice []string, value string) int64 {
for p, v := range slice { for p, v := range slice {
@ -48,7 +17,7 @@ func IndexOf(slice []string, value string) int64 {
func MnemonicEncode(message string) []string { func MnemonicEncode(message string) []string {
var out []string var out []string
n := int64(len(words)) n := int64(len(MnemonicWords))
for i := 0; i < len(message); i += (len(message) / 8) { for i := 0; i < len(message); i += (len(message) / 8) {
x := message[i : i+8] x := message[i : i+8]
@ -56,22 +25,22 @@ func MnemonicEncode(message string) []string {
w1 := (bit % n) w1 := (bit % n)
w2 := ((bit / n) + w1) % n w2 := ((bit / n) + w1) % n
w3 := ((bit / n / n) + w2) % n w3 := ((bit / n / n) + w2) % n
out = append(out, words[w1], words[w2], words[w3]) out = append(out, MnemonicWords[w1], MnemonicWords[w2], MnemonicWords[w3])
} }
return out return out
} }
func MnemonicDecode(wordsar []string) string { func MnemonicDecode(wordsar []string) string {
var out string var out string
n := int64(len(words)) n := int64(len(MnemonicWords))
for i := 0; i < len(wordsar); i += 3 { for i := 0; i < len(wordsar); i += 3 {
word1 := wordsar[i] word1 := wordsar[i]
word2 := wordsar[i+1] word2 := wordsar[i+1]
word3 := wordsar[i+2] word3 := wordsar[i+2]
w1 := IndexOf(words, word1) w1 := IndexOf(MnemonicWords, word1)
w2 := IndexOf(words, word2) w2 := IndexOf(MnemonicWords, word2)
w3 := IndexOf(words, word3) w3 := IndexOf(MnemonicWords, word3)
y := (w2 - w1) % n y := (w2 - w1) % n
z := (w3 - w2) % n z := (w3 - w2) % n

File diff suppressed because it is too large Load Diff

1630
ethcrypto/mnemonic_words.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,24 +3,27 @@ package eth
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"io/ioutil"
"math/rand" "math/rand"
"net" "net"
"net/http"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
) )
const seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt" const (
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
seedNodeAddress = "54.76.56.74:30303"
)
var ethlogger = ethlog.NewLogger("SERV") var ethlogger = ethlog.NewLogger("SERV")
@ -41,8 +44,8 @@ type Ethereum struct {
// Channel for shutting down the ethereum // Channel for shutting down the ethereum
shutdownChan chan bool shutdownChan chan bool
quit chan bool quit chan bool
// DB interface // DB interface
//db *ethdb.LDBDatabase
db ethutil.Database db ethutil.Database
// State manager for processing new blocks and managing the over all states // State manager for processing new blocks and managing the over all states
stateManager *ethchain.StateManager stateManager *ethchain.StateManager
@ -51,6 +54,8 @@ type Ethereum struct {
txPool *ethchain.TxPool txPool *ethchain.TxPool
// The canonical chain // The canonical chain
blockChain *ethchain.BlockChain blockChain *ethchain.BlockChain
// The block pool
blockPool *BlockPool
// Peers (NYI) // Peers (NYI)
peers *list.List peers *list.List
// Nonce // Nonce
@ -73,7 +78,7 @@ type Ethereum struct {
listening bool listening bool
reactor *ethutil.ReactorEngine reactor *ethreact.ReactorEngine
RpcServer *ethrpc.JsonRpcServer RpcServer *ethrpc.JsonRpcServer
@ -111,8 +116,9 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
clientIdentity: clientIdentity, clientIdentity: clientIdentity,
isUpToDate: true, isUpToDate: true,
} }
ethereum.reactor = ethutil.NewReactorEngine() ethereum.reactor = ethreact.New()
ethereum.blockPool = NewBlockPool(ethereum)
ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.txPool = ethchain.NewTxPool(ethereum)
ethereum.blockChain = ethchain.NewBlockChain(ethereum) ethereum.blockChain = ethchain.NewBlockChain(ethereum)
ethereum.stateManager = ethchain.NewStateManager(ethereum) ethereum.stateManager = ethchain.NewStateManager(ethereum)
@ -123,7 +129,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
return ethereum, nil return ethereum, nil
} }
func (s *Ethereum) Reactor() *ethutil.ReactorEngine { func (s *Ethereum) Reactor() *ethreact.ReactorEngine {
return s.reactor return s.reactor
} }
@ -146,6 +152,9 @@ func (s *Ethereum) StateManager() *ethchain.StateManager {
func (s *Ethereum) TxPool() *ethchain.TxPool { func (s *Ethereum) TxPool() *ethchain.TxPool {
return s.txPool return s.txPool
} }
func (self *Ethereum) Db() ethutil.Database {
return self.db
}
func (s *Ethereum) ServerCaps() Caps { func (s *Ethereum) ServerCaps() Caps {
return s.serverCaps return s.serverCaps
@ -355,6 +364,7 @@ func (s *Ethereum) ReapDeadPeerHandler() {
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start(seed bool) { func (s *Ethereum) Start(seed bool) {
s.reactor.Start()
// Bind to addr and port // Bind to addr and port
ln, err := net.Listen("tcp", ":"+s.Port) ln, err := net.Listen("tcp", ":"+s.Port)
if err != nil { if err != nil {
@ -420,22 +430,10 @@ func (s *Ethereum) Seed() {
} }
// Connect to Peer list // Connect to Peer list
s.ProcessPeerList(peers) s.ProcessPeerList(peers)
} else {
// Fallback to servers.poc3.txt
resp, err := http.Get(seedTextFileUri)
if err != nil {
ethlogger.Warnln("Fetching seed failed:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
ethlogger.Warnln("Reading seed failed:", err)
return
}
s.ConnectToPeer(string(body))
} }
// XXX tmp
s.ConnectToPeer(seedNodeAddress)
} }
func (s *Ethereum) peerHandler(listener net.Listener) { func (s *Ethereum) peerHandler(listener net.Listener) {
@ -466,6 +464,8 @@ func (s *Ethereum) Stop() {
} }
s.txPool.Stop() s.txPool.Stop()
s.stateManager.Stop() s.stateManager.Stop()
s.reactor.Flush()
s.reactor.Stop()
ethlogger.Infoln("Server stopped") ethlogger.Infoln("Server stopped")
close(s.shutdownChan) close(s.shutdownChan)

View File

@ -39,7 +39,9 @@ func (msg *logMessage) send(logger LogSystem) {
var logMessages chan (*logMessage) var logMessages chan (*logMessage)
var logSystems []LogSystem var logSystems []LogSystem
var quit chan bool var quit chan chan error
var drained chan bool
var mutex = sync.Mutex{}
type LogLevel uint8 type LogLevel uint8
@ -52,34 +54,55 @@ const (
DebugDetailLevel DebugDetailLevel
) )
func dispatch(msg *logMessage) {
for _, logSystem := range logSystems {
if logSystem.GetLogLevel() >= msg.LogLevel {
msg.send(logSystem)
}
}
}
// log messages are dispatched to log writers // log messages are dispatched to log writers
func start() { func start() {
out:
for { for {
select { select {
case status := <-quit:
status <- nil
return
case msg := <-logMessages: case msg := <-logMessages:
for _, logSystem := range logSystems { dispatch(msg)
if logSystem.GetLogLevel() >= msg.LogLevel { default:
msg.send(logSystem) drained <- true // this blocks until a message is sent to the queue
}
}
case <-quit:
break out
} }
} }
} }
func send(msg *logMessage) {
logMessages <- msg
select {
case <-drained:
default:
}
}
func Reset() {
mutex.Lock()
defer mutex.Unlock()
if logSystems != nil {
status := make(chan error)
quit <- status
select {
case <-drained:
default:
}
<-status
}
}
// waits until log messages are drained (dispatched to log writers) // waits until log messages are drained (dispatched to log writers)
func Flush() { func Flush() {
quit <- true if logSystems != nil {
<-drained
done:
for {
select {
case <-logMessages:
default:
break done
}
} }
} }
@ -97,7 +120,8 @@ func AddLogSystem(logSystem LogSystem) {
defer mutex.Unlock() defer mutex.Unlock()
if logSystems == nil { if logSystems == nil {
logMessages = make(chan *logMessage, 10) logMessages = make(chan *logMessage, 10)
quit = make(chan bool, 1) quit = make(chan chan error, 1)
drained = make(chan bool, 1)
go start() go start()
} }
logSystems = append(logSystems, logSystem) logSystems = append(logSystems, logSystem)
@ -106,14 +130,14 @@ func AddLogSystem(logSystem LogSystem) {
func (logger *Logger) sendln(level LogLevel, v ...interface{}) { func (logger *Logger) sendln(level LogLevel, v ...interface{}) {
if logMessages != nil { if logMessages != nil {
msg := newPrintlnLogMessage(level, logger.tag, v...) msg := newPrintlnLogMessage(level, logger.tag, v...)
logMessages <- msg send(msg)
} }
} }
func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) { func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) {
if logMessages != nil { if logMessages != nil {
msg := newPrintfLogMessage(level, logger.tag, format, v...) msg := newPrintfLogMessage(level, logger.tag, format, v...)
logMessages <- msg send(msg)
} }
} }

View File

@ -28,8 +28,19 @@ func (t *TestLogSystem) GetLogLevel() LogLevel {
return t.level return t.level
} }
func quote(s string) string { func TestLoggerFlush(t *testing.T) {
return fmt.Sprintf("'%s'", s) logger := NewLogger("TEST")
testLogSystem := &TestLogSystem{level: WarnLevel}
AddLogSystem(testLogSystem)
for i := 0; i < 5; i++ {
logger.Errorf(".")
}
Flush()
Reset()
output := testLogSystem.Output
if output != "[TEST] .[TEST] .[TEST] .[TEST] .[TEST] ." {
t.Error("Expected complete logger output '[TEST] .[TEST] .[TEST] .[TEST] .[TEST] .', got ", output)
}
} }
func TestLoggerPrintln(t *testing.T) { func TestLoggerPrintln(t *testing.T) {
@ -41,10 +52,11 @@ func TestLoggerPrintln(t *testing.T) {
logger.Infoln("info") logger.Infoln("info")
logger.Debugln("debug") logger.Debugln("debug")
Flush() Flush()
Reset()
output := testLogSystem.Output output := testLogSystem.Output
fmt.Println(quote(output)) fmt.Println(quote(output))
if output != "[TEST] error\n[TEST] warn\n" { if output != "[TEST] error\n[TEST] warn\n" {
t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", quote(testLogSystem.Output)) t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", output)
} }
} }
@ -57,10 +69,10 @@ func TestLoggerPrintf(t *testing.T) {
logger.Infof("info") logger.Infof("info")
logger.Debugf("debug") logger.Debugf("debug")
Flush() Flush()
Reset()
output := testLogSystem.Output output := testLogSystem.Output
fmt.Println(quote(output))
if output != "[TEST] error to { 2}\n[TEST] warn" { if output != "[TEST] error to { 2}\n[TEST] warn" {
t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", quote(testLogSystem.Output)) t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", output)
} }
} }
@ -73,13 +85,14 @@ func TestMultipleLogSystems(t *testing.T) {
logger.Errorln("error") logger.Errorln("error")
logger.Warnln("warn") logger.Warnln("warn")
Flush() Flush()
Reset()
output0 := testLogSystem0.Output output0 := testLogSystem0.Output
output1 := testLogSystem1.Output output1 := testLogSystem1.Output
if output0 != "[TEST] error\n" { if output0 != "[TEST] error\n" {
t.Error("Expected logger 0 output '[TEST] error\\n', got ", quote(testLogSystem0.Output)) t.Error("Expected logger 0 output '[TEST] error\\n', got ", output0)
} }
if output1 != "[TEST] error\n[TEST] warn\n" { if output1 != "[TEST] error\n[TEST] warn\n" {
t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", quote(testLogSystem1.Output)) t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", output1)
} }
} }
@ -94,9 +107,8 @@ func TestFileLogSystem(t *testing.T) {
Flush() Flush()
contents, _ := ioutil.ReadFile(filename) contents, _ := ioutil.ReadFile(filename)
output := string(contents) output := string(contents)
fmt.Println(quote(output))
if output != "[TEST] error to test.log\n[TEST] warn\n" { if output != "[TEST] error to test.log\n[TEST] warn\n" {
t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", quote(output)) t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", output)
} else { } else {
os.Remove(filename) os.Remove(filename)
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"sort" "sort"
) )
@ -15,13 +15,13 @@ type Miner struct {
pow ethchain.PoW pow ethchain.PoW
ethereum ethchain.EthManager ethereum ethchain.EthManager
coinbase []byte coinbase []byte
reactChan chan ethutil.React reactChan chan ethreact.Event
txs ethchain.Transactions txs ethchain.Transactions
uncles []*ethchain.Block uncles []*ethchain.Block
block *ethchain.Block block *ethchain.Block
powChan chan []byte powChan chan []byte
powQuitChan chan ethutil.React powQuitChan chan ethreact.Event
quitChan chan bool quitChan chan chan error
} }
func (self *Miner) GetPow() ethchain.PoW { func (self *Miner) GetPow() ethchain.PoW {
@ -29,55 +29,53 @@ func (self *Miner) GetPow() ethchain.PoW {
} }
func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) *Miner { func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) *Miner {
reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in miner := Miner{
powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block pow: &ethchain.EasyPow{},
powQuitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread ethereum: ethereum,
quitChan := make(chan bool, 1) coinbase: coinbase,
}
ethereum.Reactor().Subscribe("newBlock", reactChan) return &miner
ethereum.Reactor().Subscribe("newTx:pre", reactChan) }
func (miner *Miner) Start() {
miner.reactChan = make(chan ethreact.Event, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
miner.powChan = make(chan []byte, 1) // This is the channel that receives valid sha hashes for a given block
miner.powQuitChan = make(chan ethreact.Event, 1) // This is the channel that can exit the miner thread
miner.quitChan = make(chan chan error, 1)
// Insert initial TXs in our little miner 'pool'
miner.txs = miner.ethereum.TxPool().Flush()
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase)
// Prepare inital block
//miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
go miner.listener()
reactor := miner.ethereum.Reactor()
reactor.Subscribe("newBlock", miner.reactChan)
reactor.Subscribe("newTx:pre", miner.reactChan)
// We need the quit chan to be a Reactor event. // We need the quit chan to be a Reactor event.
// The POW search method is actually blocking and if we don't // The POW search method is actually blocking and if we don't
// listen to the reactor events inside of the pow itself // listen to the reactor events inside of the pow itself
// The miner overseer will never get the reactor events themselves // The miner overseer will never get the reactor events themselves
// Only after the miner will find the sha // Only after the miner will find the sha
ethereum.Reactor().Subscribe("newBlock", powQuitChan) reactor.Subscribe("newBlock", miner.powQuitChan)
ethereum.Reactor().Subscribe("newTx:pre", powQuitChan) reactor.Subscribe("newTx:pre", miner.powQuitChan)
miner := Miner{
pow: &ethchain.EasyPow{},
ethereum: ethereum,
coinbase: coinbase,
reactChan: reactChan,
powChan: powChan,
powQuitChan: powQuitChan,
quitChan: quitChan,
}
// Insert initial TXs in our little miner 'pool'
miner.txs = ethereum.TxPool().Flush()
miner.block = ethereum.BlockChain().NewBlock(miner.coinbase)
return &miner
}
func (miner *Miner) Start() {
// Prepare inital block
//miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
go miner.listener()
logger.Infoln("Started") logger.Infoln("Started")
miner.ethereum.Reactor().Post("miner:start", miner) reactor.Post("miner:start", miner)
} }
func (miner *Miner) listener() { func (miner *Miner) listener() {
out:
for { for {
select { select {
case <-miner.quitChan: case status := <-miner.quitChan:
logger.Infoln("Stopped") logger.Infoln("Stopped")
break out status <- nil
return
case chanMessage := <-miner.reactChan: case chanMessage := <-miner.reactChan:
if block, ok := chanMessage.Resource.(*ethchain.Block); ok { if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
@ -133,13 +131,22 @@ out:
} }
} }
func (self *Miner) Stop() { func (miner *Miner) Stop() {
logger.Infoln("Stopping...") logger.Infoln("Stopping...")
self.quitChan <- true miner.powQuitChan <- ethreact.Event{}
self.powQuitChan <- ethutil.React{}
self.ethereum.Reactor().Post("miner:stop", self) status := make(chan error)
miner.quitChan <- status
<-status
reactor := miner.ethereum.Reactor()
reactor.Unsubscribe("newBlock", miner.powQuitChan)
reactor.Unsubscribe("newTx:pre", miner.powQuitChan)
reactor.Unsubscribe("newBlock", miner.reactChan)
reactor.Unsubscribe("newTx:pre", miner.reactChan)
reactor.Post("miner:stop", miner)
} }
func (self *Miner) mineNewBlock() { func (self *Miner) mineNewBlock() {

33
ethpipe/config.go Normal file
View File

@ -0,0 +1,33 @@
package ethpipe
import "github.com/ethereum/eth-go/ethutil"
var cnfCtr = ethutil.Hex2Bytes("661005d2720d855f1d9976f88bb10c1a3398c77f")
type Config struct {
pipe *Pipe
}
func (self *Config) Get(name string) *Object {
configCtrl := self.pipe.World().safeGet(cnfCtr)
var addr []byte
switch name {
case "NameReg":
addr = []byte{0}
case "DnsReg":
objectAddr := configCtrl.GetStorage(ethutil.BigD([]byte{0}))
domainAddr := (&Object{self.pipe.World().safeGet(objectAddr.Bytes())}).StorageString("DnsReg").Bytes()
return &Object{self.pipe.World().safeGet(domainAddr)}
default:
addr = ethutil.RightPadBytes([]byte(name), 32)
}
objectAddr := configCtrl.GetStorage(ethutil.BigD(addr))
return &Object{self.pipe.World().safeGet(objectAddr.Bytes())}
}
func (self *Config) Exist() bool {
return self.pipe.World().Get(cnfCtr) != nil
}

334
ethpipe/js_pipe.go Normal file
View File

@ -0,0 +1,334 @@
package ethpipe
import (
"bytes"
"encoding/json"
"fmt"
"sync/atomic"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
)
type JSPipe struct {
*Pipe
}
func NewJSPipe(eth ethchain.EthManager) *JSPipe {
return &JSPipe{New(eth)}
}
func (self *JSPipe) BlockByHash(strHash string) *JSBlock {
hash := ethutil.Hex2Bytes(strHash)
block := self.obj.BlockChain().GetBlock(hash)
return NewJSBlock(block)
}
func (self *JSPipe) BlockByNumber(num int32) *JSBlock {
if num == -1 {
return NewJSBlock(self.obj.BlockChain().CurrentBlock)
}
return NewJSBlock(self.obj.BlockChain().GetBlockByNumber(uint64(num)))
}
func (self *JSPipe) Block(v interface{}) *JSBlock {
if n, ok := v.(int32); ok {
return self.BlockByNumber(n)
} else if str, ok := v.(string); ok {
return self.BlockByHash(str)
} else if f, ok := v.(float64); ok { // Don't ask ...
return self.BlockByNumber(int32(f))
}
return nil
}
func (self *JSPipe) Key() *JSKey {
return NewJSKey(self.obj.KeyManager().KeyPair())
}
func (self *JSPipe) StateObject(addr string) *JSObject {
object := &Object{self.World().safeGet(ethutil.Hex2Bytes(addr))}
return NewJSObject(object)
}
func (self *JSPipe) PeerCount() int {
return self.obj.PeerCount()
}
func (self *JSPipe) Peers() []JSPeer {
var peers []JSPeer
for peer := self.obj.Peers().Front(); peer != nil; peer = peer.Next() {
p := peer.Value.(ethchain.Peer)
// we only want connected peers
if atomic.LoadInt32(p.Connected()) != 0 {
peers = append(peers, *NewJSPeer(p))
}
}
return peers
}
func (self *JSPipe) IsMining() bool {
return self.obj.IsMining()
}
func (self *JSPipe) IsListening() bool {
return self.obj.IsListening()
}
func (self *JSPipe) CoinBase() string {
return ethutil.Bytes2Hex(self.obj.KeyManager().Address())
}
func (self *JSPipe) BalanceAt(addr string) string {
return self.World().SafeGet(ethutil.Hex2Bytes(addr)).Balance.String()
}
func (self *JSPipe) NumberToHuman(balance string) string {
b := ethutil.Big(balance)
return ethutil.CurrencyToString(b)
}
func (self *JSPipe) StorageAt(addr, storageAddr string) string {
storage := self.World().SafeGet(ethutil.Hex2Bytes(addr)).Storage(ethutil.Hex2Bytes(storageAddr))
return storage.BigInt().String()
}
func (self *JSPipe) TxCountAt(address string) int {
return int(self.World().SafeGet(ethutil.Hex2Bytes(address)).Nonce)
}
func (self *JSPipe) IsContract(address string) bool {
return len(self.World().SafeGet(ethutil.Hex2Bytes(address)).Code) > 0
}
func (self *JSPipe) SecretToAddress(key string) string {
pair, err := ethcrypto.NewKeyPairFromSec(ethutil.Hex2Bytes(key))
if err != nil {
return ""
}
return ethutil.Bytes2Hex(pair.Address())
}
type KeyVal struct {
Key string `json:"key"`
Value string `json:"value"`
}
func (self *JSPipe) EachStorage(addr string) string {
var values []KeyVal
object := self.World().SafeGet(ethutil.Hex2Bytes(addr))
object.EachStorage(func(name string, value *ethutil.Value) {
value.Decode()
values = append(values, KeyVal{ethutil.Bytes2Hex([]byte(name)), ethutil.Bytes2Hex(value.Bytes())})
})
valuesJson, err := json.Marshal(values)
if err != nil {
return ""
}
return string(valuesJson)
}
func (self *JSPipe) ToAscii(str string) string {
padded := ethutil.RightPadBytes([]byte(str), 32)
return "0x" + ethutil.Bytes2Hex(padded)
}
func (self *JSPipe) FromAscii(str string) string {
if ethutil.IsHex(str) {
str = str[2:]
}
return string(bytes.Trim(ethutil.Hex2Bytes(str), "\x00"))
}
func (self *JSPipe) FromNumber(str string) string {
if ethutil.IsHex(str) {
str = str[2:]
}
return ethutil.BigD(ethutil.Hex2Bytes(str)).String()
}
func (self *JSPipe) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (*JSReceipt, error) {
var hash []byte
var contractCreation bool
if len(toStr) == 0 {
contractCreation = true
} else {
// Check if an address is stored by this address
addr := self.World().Config().Get("NameReg").StorageString(toStr).Bytes()
if len(addr) > 0 {
hash = addr
} else {
hash = ethutil.Hex2Bytes(toStr)
}
}
var keyPair *ethcrypto.KeyPair
var err error
if ethutil.IsHex(key) {
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key[2:])))
} else {
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key)))
}
if err != nil {
return nil, err
}
var (
value = ethutil.Big(valueStr)
gas = ethutil.Big(gasStr)
gasPrice = ethutil.Big(gasPriceStr)
data []byte
tx *ethchain.Transaction
)
if ethutil.IsHex(codeStr) {
data = ethutil.Hex2Bytes(codeStr[2:])
} else {
data = ethutil.Hex2Bytes(codeStr)
}
if contractCreation {
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, data)
} else {
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
}
acc := self.obj.StateManager().TransState().GetOrNewStateObject(keyPair.Address())
tx.Nonce = acc.Nonce
acc.Nonce += 1
self.obj.StateManager().TransState().UpdateStateObject(acc)
tx.Sign(keyPair.PrivateKey)
self.obj.TxPool().QueueTransaction(tx)
if contractCreation {
logger.Infof("Contract addr %x", tx.CreationAddress())
}
return NewJSReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil
}
func (self *JSPipe) CompileMutan(code string) string {
data, err := self.Pipe.CompileMutan(code)
if err != nil {
return err.Error()
}
return ethutil.Bytes2Hex(data)
}
func (self *JSPipe) Watch(object map[string]interface{}) *JSFilter {
return NewJSFilterFromMap(object, self.Pipe.obj)
/*} else if str, ok := object.(string); ok {
println("str")
return NewJSFilterFromString(str, self.Pipe.obj)
*/
}
func (self *JSPipe) Messages(object map[string]interface{}) string {
filter := self.Watch(object)
filter.Uninstall()
return filter.Messages()
}
type JSFilter struct {
eth ethchain.EthManager
*ethchain.Filter
quit chan bool
BlockCallback func(*ethchain.Block)
MessageCallback func(ethstate.Messages)
}
func NewJSFilterFromMap(object map[string]interface{}, eth ethchain.EthManager) *JSFilter {
filter := &JSFilter{eth, ethchain.NewFilterFromMap(object, eth), make(chan bool), nil, nil}
go filter.mainLoop()
return filter
}
func NewJSFilterFromString(str string, eth ethchain.EthManager) *JSFilter {
return nil
}
func (self *JSFilter) MessagesToJson(messages ethstate.Messages) string {
var msgs []JSMessage
for _, m := range messages {
msgs = append(msgs, NewJSMessage(m))
}
// Return an empty array instead of "null"
if len(msgs) == 0 {
return "[]"
}
b, err := json.Marshal(msgs)
if err != nil {
return "{\"error\":" + err.Error() + "}"
}
return string(b)
}
func (self *JSFilter) Messages() string {
return self.MessagesToJson(self.Find())
}
func (self *JSFilter) mainLoop() {
blockChan := make(chan ethreact.Event, 5)
messageChan := make(chan ethreact.Event, 5)
// Subscribe to events
reactor := self.eth.Reactor()
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("messages", messageChan)
out:
for {
select {
case <-self.quit:
break out
case block := <-blockChan:
if block, ok := block.Resource.(*ethchain.Block); ok {
if self.BlockCallback != nil {
self.BlockCallback(block)
}
}
case msg := <-messageChan:
if messages, ok := msg.Resource.(ethstate.Messages); ok {
if self.MessageCallback != nil {
println("messages!")
msgs := self.FilterMessages(messages)
if len(msgs) > 0 {
self.MessageCallback(msgs)
}
}
}
}
}
}
func (self *JSFilter) Changed(object interface{}) {
fmt.Printf("%T\n", object)
}
func (self *JSFilter) Uninstall() {
self.quit <- true
}

208
ethpipe/js_types.go Normal file
View File

@ -0,0 +1,208 @@
package ethpipe
import (
"encoding/json"
"strconv"
"strings"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
)
// Block interface exposed to QML
type JSBlock struct {
ref *ethchain.Block
Number int `json:"number"`
Hash string `json:"hash"`
Transactions string `json:"transactions"`
Time int64 `json:"time"`
Coinbase string `json:"coinbase"`
Name string `json:"name"`
GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"`
}
// Creates a new QML Block from a chain block
func NewJSBlock(block *ethchain.Block) *JSBlock {
if block == nil {
return nil
}
var ptxs []JSTransaction
for _, tx := range block.Transactions() {
ptxs = append(ptxs, *NewJSTx(tx))
}
txJson, err := json.Marshal(ptxs)
if err != nil {
return nil
}
return &JSBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
}
func (self *JSBlock) ToString() string {
if self.ref != nil {
return self.ref.String()
}
return ""
}
func (self *JSBlock) GetTransaction(hash string) *JSTransaction {
tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash))
if tx == nil {
return nil
}
return NewJSTx(tx)
}
type JSTransaction struct {
ref *ethchain.Transaction
Value string `json:"value"`
Gas string `json:"gas"`
GasPrice string `json:"gasPrice"`
Hash string `json:"hash"`
Address string `json:"address"`
Sender string `json:"sender"`
RawData string `json:"rawData"`
Data string `json:"data"`
Contract bool `json:"isContract"`
CreatesContract bool `json:"createsContract"`
Confirmations int `json:"confirmations"`
}
func NewJSTx(tx *ethchain.Transaction) *JSTransaction {
hash := ethutil.Bytes2Hex(tx.Hash())
receiver := ethutil.Bytes2Hex(tx.Recipient)
if receiver == "0000000000000000000000000000000000000000" {
receiver = ethutil.Bytes2Hex(tx.CreationAddress())
}
sender := ethutil.Bytes2Hex(tx.Sender())
createsContract := tx.CreatesContract()
var data string
if tx.CreatesContract() {
data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
} else {
data = ethutil.Bytes2Hex(tx.Data)
}
return &JSTransaction{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)}
}
func (self *JSTransaction) ToString() string {
return self.ref.String()
}
type JSKey struct {
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
}
func NewJSKey(key *ethcrypto.KeyPair) *JSKey {
return &JSKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)}
}
type JSObject struct {
*Object
}
func NewJSObject(object *Object) *JSObject {
return &JSObject{object}
}
type PReceipt struct {
CreatedContract bool `json:"createdContract"`
Address string `json:"address"`
Hash string `json:"hash"`
Sender string `json:"sender"`
}
func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
return &PReceipt{
contractCreation,
ethutil.Bytes2Hex(creationAddress),
ethutil.Bytes2Hex(hash),
ethutil.Bytes2Hex(address),
}
}
// Peer interface exposed to QML
type JSPeer struct {
ref *ethchain.Peer
Inbound bool `json:"isInbound"`
LastSend int64 `json:"lastSend"`
LastPong int64 `json:"lastPong"`
Ip string `json:"ip"`
Port int `json:"port"`
Version string `json:"version"`
LastResponse string `json:"lastResponse"`
Latency string `json:"latency"`
}
func NewJSPeer(peer ethchain.Peer) *JSPeer {
if peer == nil {
return nil
}
var ip []string
for _, i := range peer.Host() {
ip = append(ip, strconv.Itoa(int(i)))
}
ipAddress := strings.Join(ip, ".")
return &JSPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()}
}
type JSReceipt struct {
CreatedContract bool `json:"createdContract"`
Address string `json:"address"`
Hash string `json:"hash"`
Sender string `json:"sender"`
}
func NewJSReciept(contractCreation bool, creationAddress, hash, address []byte) *JSReceipt {
return &JSReceipt{
contractCreation,
ethutil.Bytes2Hex(creationAddress),
ethutil.Bytes2Hex(hash),
ethutil.Bytes2Hex(address),
}
}
type JSMessage struct {
To string `json:"to"`
From string `json:"from"`
Input string `json:"input"`
Output string `json:"output"`
Path int32 `json:"path"`
Origin string `json:"origin"`
Timestamp int32 `json:"timestamp"`
Coinbase string `json:"coinbase"`
Block string `json:"block"`
Number int32 `json:"number"`
Value string `json:"value"`
}
func NewJSMessage(message *ethstate.Message) JSMessage {
return JSMessage{
To: ethutil.Bytes2Hex(message.To),
From: ethutil.Bytes2Hex(message.From),
Input: ethutil.Bytes2Hex(message.Input),
Output: ethutil.Bytes2Hex(message.Output),
Path: int32(message.Path),
Origin: ethutil.Bytes2Hex(message.Origin),
Timestamp: int32(message.Timestamp),
Coinbase: ethutil.Bytes2Hex(message.Origin),
Block: ethutil.Bytes2Hex(message.Block),
Number: int32(message.Number.Int64()),
Value: message.Value.String(),
}
}

26
ethpipe/object.go Normal file
View File

@ -0,0 +1,26 @@
package ethpipe
import (
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
)
type Object struct {
*ethstate.StateObject
}
func (self *Object) StorageString(str string) *ethutil.Value {
if ethutil.IsHex(str) {
return self.Storage(ethutil.Hex2Bytes(str[2:]))
} else {
return self.Storage(ethutil.RightPadBytes([]byte(str), 32))
}
}
func (self *Object) StorageValue(addr *ethutil.Value) *ethutil.Value {
return self.Storage(addr.Bytes())
}
func (self *Object) Storage(addr []byte) *ethutil.Value {
return self.StateObject.GetStorage(ethutil.BigD(addr))
}

159
ethpipe/pipe.go Normal file
View File

@ -0,0 +1,159 @@
package ethpipe
import (
"strings"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethvm"
)
var logger = ethlog.NewLogger("PIPE")
type VmVars struct {
State *ethstate.State
}
type Pipe struct {
obj ethchain.EthManager
stateManager *ethchain.StateManager
blockChain *ethchain.BlockChain
world *World
Vm VmVars
}
func New(obj ethchain.EthManager) *Pipe {
pipe := &Pipe{
obj: obj,
stateManager: obj.StateManager(),
blockChain: obj.BlockChain(),
}
pipe.world = NewWorld(pipe)
return pipe
}
func (self *Pipe) Balance(addr []byte) *ethutil.Value {
return ethutil.NewValue(self.World().safeGet(addr).Balance)
}
func (self *Pipe) Nonce(addr []byte) uint64 {
return self.World().safeGet(addr).Nonce
}
func (self *Pipe) Execute(addr []byte, data []byte, value, gas, price *ethutil.Value) ([]byte, error) {
return self.ExecuteObject(&Object{self.World().safeGet(addr)}, data, value, gas, price)
}
func (self *Pipe) ExecuteObject(object *Object, data []byte, value, gas, price *ethutil.Value) ([]byte, error) {
var (
initiator = ethstate.NewStateObject([]byte{0})
block = self.blockChain.CurrentBlock
stateObject = object.StateObject
)
if self.Vm.State == nil {
self.Vm.State = self.World().State().Copy()
}
vm := ethvm.New(NewEnv(self.Vm.State, block, value.BigInt(), initiator.Address()))
closure := ethvm.NewClosure(&ethstate.Message{}, initiator, stateObject, object.Code, gas.BigInt(), price.BigInt())
ret, _, err := closure.Call(vm, data)
return ret, err
}
func (self *Pipe) Block(hash []byte) *ethchain.Block {
return self.blockChain.GetBlock(hash)
}
func (self *Pipe) Storage(addr, storageAddr []byte) *ethutil.Value {
return self.World().safeGet(addr).GetStorage(ethutil.BigD(storageAddr))
}
func (self *Pipe) ToAddress(priv []byte) []byte {
pair, err := ethcrypto.NewKeyPairFromSec(priv)
if err != nil {
return nil
}
return pair.Address()
}
func (self *Pipe) Exists(addr []byte) bool {
return self.World().Get(addr) != nil
}
func (self *Pipe) TransactString(key *ethcrypto.KeyPair, rec string, value, gas, price *ethutil.Value, data []byte) ([]byte, error) {
// Check if an address is stored by this address
var hash []byte
addr := self.World().Config().Get("NameReg").StorageString(rec).Bytes()
if len(addr) > 0 {
hash = addr
} else if ethutil.IsHex(rec) {
hash = ethutil.Hex2Bytes(rec[2:])
} else {
hash = ethutil.Hex2Bytes(rec)
}
return self.Transact(key, hash, value, gas, price, data)
}
func (self *Pipe) Transact(key *ethcrypto.KeyPair, rec []byte, value, gas, price *ethutil.Value, data []byte) ([]byte, error) {
var hash []byte
var contractCreation bool
if rec == nil {
contractCreation = true
}
var tx *ethchain.Transaction
// Compile and assemble the given data
if contractCreation {
script, err := ethutil.Compile(string(data), false)
if err != nil {
return nil, err
}
tx = ethchain.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), script)
} else {
data := ethutil.StringToByteFunc(string(data), func(s string) (ret []byte) {
slice := strings.Split(s, "\n")
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
return
})
tx = ethchain.NewTransactionMessage(hash, value.BigInt(), gas.BigInt(), price.BigInt(), data)
}
acc := self.stateManager.TransState().GetOrNewStateObject(key.Address())
tx.Nonce = acc.Nonce
acc.Nonce += 1
self.stateManager.TransState().UpdateStateObject(acc)
tx.Sign(key.PrivateKey)
self.obj.TxPool().QueueTransaction(tx)
if contractCreation {
logger.Infof("Contract addr %x", tx.CreationAddress())
return tx.CreationAddress(), nil
}
return tx.Hash(), nil
}
func (self *Pipe) CompileMutan(code string) ([]byte, error) {
data, err := ethutil.Compile(code, false)
if err != nil {
return nil, err
}
return data, nil
}

58
ethpipe/pipe_test.go Normal file
View File

@ -0,0 +1,58 @@
package ethpipe
import (
"testing"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
)
func Val(v interface{}) *ethutil.Value {
return ethutil.NewValue(v)
}
func TestNew(t *testing.T) {
pipe := New(nil)
var addr, privy, recp, data []byte
var object *ethstate.StateObject
var key *ethcrypto.KeyPair
world := pipe.World()
world.Get(addr)
world.Coinbase()
world.IsMining()
world.IsListening()
world.State()
peers := world.Peers()
peers.Len()
// Shortcut functions
pipe.Balance(addr)
pipe.Nonce(addr)
pipe.Block(addr)
pipe.Storage(addr, addr)
pipe.ToAddress(privy)
pipe.Exists(addr)
// Doesn't change state
pipe.Execute(addr, nil, Val(0), Val(1000000), Val(10))
// Doesn't change state
pipe.ExecuteObject(object, nil, Val(0), Val(1000000), Val(10))
conf := world.Config()
namereg := conf.Get("NameReg")
namereg.Storage(addr)
var err error
// Transact
err = pipe.Transact(key, recp, ethutil.NewValue(0), ethutil.NewValue(0), ethutil.NewValue(0), nil)
if err != nil {
t.Error(err)
}
// Create
err = pipe.Transact(key, nil, ethutil.NewValue(0), ethutil.NewValue(0), ethutil.NewValue(0), data)
if err != nil {
t.Error(err)
}
}

34
ethpipe/vm_env.go Normal file
View File

@ -0,0 +1,34 @@
package ethpipe
import (
"math/big"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethstate"
)
type VMEnv struct {
state *ethstate.State
block *ethchain.Block
value *big.Int
sender []byte
}
func NewEnv(state *ethstate.State, block *ethchain.Block, value *big.Int, sender []byte) *VMEnv {
return &VMEnv{
state: state,
block: block,
value: value,
sender: sender,
}
}
func (self *VMEnv) Origin() []byte { return self.sender }
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
func (self *VMEnv) Time() int64 { return self.block.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) State() *ethstate.State { return self.state }

64
ethpipe/world.go Normal file
View File

@ -0,0 +1,64 @@
package ethpipe
import (
"container/list"
"github.com/ethereum/eth-go/ethstate"
)
type World struct {
pipe *Pipe
cfg *Config
}
func NewWorld(pipe *Pipe) *World {
world := &World{pipe, nil}
world.cfg = &Config{pipe}
return world
}
func (self *Pipe) World() *World {
return self.world
}
func (self *World) State() *ethstate.State {
return self.pipe.stateManager.CurrentState()
}
func (self *World) Get(addr []byte) *Object {
return &Object{self.State().GetStateObject(addr)}
}
func (self *World) SafeGet(addr []byte) *Object {
return &Object{self.safeGet(addr)}
}
func (self *World) safeGet(addr []byte) *ethstate.StateObject {
object := self.State().GetStateObject(addr)
if object == nil {
object = ethstate.NewStateObject(addr)
}
return object
}
func (self *World) Coinbase() *ethstate.StateObject {
return nil
}
func (self *World) IsMining() bool {
return self.pipe.obj.IsMining()
}
func (self *World) IsListening() bool {
return self.pipe.obj.IsListening()
}
func (self *World) Peers() *list.List {
return self.pipe.obj.Peers()
}
func (self *World) Config() *Config {
return self.cfg
}

View File

@ -1,273 +0,0 @@
package ethpub
import (
"bytes"
"encoding/json"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"strings"
"sync/atomic"
)
var logger = ethlog.NewLogger("PUB")
// TODO this has to move elsewhere
var cnfCtr = ethutil.Hex2Bytes("661005d2720d855f1d9976f88bb10c1a3398c77f")
type helper struct {
sm *ethchain.StateManager
}
func EthereumConfig(stateManager *ethchain.StateManager) helper {
return helper{stateManager}
}
func (self helper) obj() *ethstate.StateObject {
return self.sm.CurrentState().GetStateObject(cnfCtr)
}
func (self helper) NameReg() *ethstate.StateObject {
if self.obj() != nil {
addr := self.obj().GetStorage(big.NewInt(0))
if len(addr.Bytes()) > 0 {
return self.sm.CurrentState().GetStateObject(addr.Bytes())
}
}
return nil
}
type PEthereum struct {
manager ethchain.EthManager
stateManager *ethchain.StateManager
blockChain *ethchain.BlockChain
txPool *ethchain.TxPool
keyManager *ethcrypto.KeyManager
}
func NewPEthereum(manager ethchain.EthManager) *PEthereum {
logger.Warnln("DEPRECATED: ethpub.New should be used in favour of ethpub.NewPEthereum")
return New(manager)
}
func New(manager ethchain.EthManager) *PEthereum {
return &PEthereum{
manager,
manager.StateManager(),
manager.BlockChain(),
manager.TxPool(),
manager.KeyManager(),
}
}
func (lib *PEthereum) GetBlock(hexHash string) *PBlock {
hash := ethutil.Hex2Bytes(hexHash)
block := lib.blockChain.GetBlock(hash)
return NewPBlock(block)
}
func (lib *PEthereum) GetKey() *PKey {
return NewPKey(lib.keyManager.KeyPair())
}
func (lib *PEthereum) GetStateObject(address string) *PStateObject {
stateObject := lib.stateManager.CurrentState().GetStateObject(ethutil.Hex2Bytes(address))
if stateObject != nil {
return NewPStateObject(stateObject)
}
// See GetStorage for explanation on "nil"
return NewPStateObject(nil)
}
func (lib *PEthereum) GetPeerCount() int {
return lib.manager.PeerCount()
}
func (lib *PEthereum) GetPeers() []PPeer {
var peers []PPeer
for peer := lib.manager.Peers().Front(); peer != nil; peer = peer.Next() {
p := peer.Value.(ethchain.Peer)
// we only want connected peers
if atomic.LoadInt32(p.Connected()) != 0 {
peers = append(peers, *NewPPeer(p))
}
}
return peers
}
func (lib *PEthereum) GetIsMining() bool {
return lib.manager.IsMining()
}
func (lib *PEthereum) GetIsListening() bool {
return lib.manager.IsListening()
}
func (lib *PEthereum) GetCoinBase() string {
return ethutil.Bytes2Hex(lib.keyManager.Address())
}
func (lib *PEthereum) GetTransactionsFor(address string, asJson bool) interface{} {
sBlk := lib.manager.BlockChain().LastBlockHash
blk := lib.manager.BlockChain().GetBlock(sBlk)
addr := []byte(ethutil.Hex2Bytes(address))
var txs []*PTx
for ; blk != nil; blk = lib.manager.BlockChain().GetBlock(sBlk) {
sBlk = blk.PrevHash
// Loop through all transactions to see if we missed any while being offline
for _, tx := range blk.Transactions() {
if bytes.Compare(tx.Sender(), addr) == 0 || bytes.Compare(tx.Recipient, addr) == 0 {
ptx := NewPTx(tx)
//TODO: somehow move this to NewPTx
ptx.Confirmations = int(lib.manager.BlockChain().LastBlockNumber - blk.BlockInfo().Number)
txs = append(txs, ptx)
}
}
}
if asJson {
txJson, err := json.Marshal(txs)
if err != nil {
return nil
}
return string(txJson)
}
return txs
}
func (lib *PEthereum) GetStorage(address, storageAddress string) string {
return lib.GetStateObject(address).GetStorage(storageAddress)
}
func (lib *PEthereum) GetTxCountAt(address string) int {
return lib.GetStateObject(address).Nonce()
}
func (lib *PEthereum) IsContract(address string) bool {
return lib.GetStateObject(address).IsContract()
}
func (lib *PEthereum) SecretToAddress(key string) string {
pair, err := ethcrypto.NewKeyPairFromSec(ethutil.Hex2Bytes(key))
if err != nil {
return ""
}
return ethutil.Bytes2Hex(pair.Address())
}
func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) {
return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
}
func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, script string) (*PReceipt, error) {
return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, script)
}
func FindAddressInNameReg(stateManager *ethchain.StateManager, name string) []byte {
nameReg := EthereumConfig(stateManager).NameReg()
if nameReg != nil {
addr := ethutil.RightPadBytes([]byte(name), 32)
reg := nameReg.GetStorage(ethutil.BigD(addr))
return reg.Bytes()
}
return nil
}
func FindNameInNameReg(stateManager *ethchain.StateManager, addr []byte) string {
nameReg := EthereumConfig(stateManager).NameReg()
if nameReg != nil {
addr = ethutil.LeftPadBytes(addr, 32)
reg := nameReg.GetStorage(ethutil.BigD(addr))
return strings.TrimRight(reg.Str(), "\x00")
}
return ""
}
func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) {
var hash []byte
var contractCreation bool
if len(recipient) == 0 {
contractCreation = true
} else {
// Check if an address is stored by this address
addr := FindAddressInNameReg(lib.stateManager, recipient)
if len(addr) > 0 {
hash = addr
} else {
hash = ethutil.Hex2Bytes(recipient)
}
}
var keyPair *ethcrypto.KeyPair
var err error
if ethutil.IsHex(key) {
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key[2:])))
} else {
keyPair, err = ethcrypto.NewKeyPairFromSec([]byte(ethutil.Hex2Bytes(key)))
}
if err != nil {
return nil, err
}
value := ethutil.Big(valueStr)
gas := ethutil.Big(gasStr)
gasPrice := ethutil.Big(gasPriceStr)
var tx *ethchain.Transaction
// Compile and assemble the given data
if contractCreation {
var script []byte
var err error
if ethutil.IsHex(scriptStr) {
script = ethutil.Hex2Bytes(scriptStr[2:])
} else {
script, err = ethutil.Compile(scriptStr, false)
if err != nil {
return nil, err
}
}
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script)
} else {
data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) {
slice := strings.Split(s, "\n")
for _, dataItem := range slice {
d := ethutil.FormatData(dataItem)
ret = append(ret, d...)
}
return
})
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
}
acc := lib.stateManager.TransState().GetOrNewStateObject(keyPair.Address())
tx.Nonce = acc.Nonce
acc.Nonce += 1
lib.stateManager.TransState().UpdateStateObject(acc)
tx.Sign(keyPair.PrivateKey)
lib.txPool.QueueTransaction(tx)
if contractCreation {
logger.Infof("Contract addr %x", tx.CreationAddress())
}
return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil
}

View File

@ -1,271 +0,0 @@
package ethpub
import (
"encoding/json"
"fmt"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil"
"strings"
)
// Peer interface exposed to QML
type PPeer struct {
ref *ethchain.Peer
Inbound bool `json:"isInbound"`
LastSend int64 `json:"lastSend"`
LastPong int64 `json:"lastPong"`
Ip string `json:"ip"`
Port int `json:"port"`
Version string `json:"version"`
LastResponse string `json:"lastResponse"`
Latency string `json:"latency"`
}
func NewPPeer(peer ethchain.Peer) *PPeer {
if peer == nil {
return nil
}
// TODO: There must be something build in to do this?
var ip []string
for _, i := range peer.Host() {
ip = append(ip, fmt.Sprintf("%d", i))
}
ipAddress := strings.Join(ip, ".")
return &PPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()}
}
// Block interface exposed to QML
type PBlock struct {
ref *ethchain.Block
Number int `json:"number"`
Hash string `json:"hash"`
Transactions string `json:"transactions"`
Time int64 `json:"time"`
Coinbase string `json:"coinbase"`
Name string `json:"name"`
GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"`
}
// Creates a new QML Block from a chain block
func NewPBlock(block *ethchain.Block) *PBlock {
if block == nil {
return nil
}
var ptxs []PTx
for _, tx := range block.Transactions() {
ptxs = append(ptxs, *NewPTx(tx))
}
txJson, err := json.Marshal(ptxs)
if err != nil {
return nil
}
return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
}
func (self *PBlock) ToString() string {
if self.ref != nil {
return self.ref.String()
}
return ""
}
func (self *PBlock) GetTransaction(hash string) *PTx {
tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash))
if tx == nil {
return nil
}
return NewPTx(tx)
}
type PTx struct {
ref *ethchain.Transaction
Value string `json:"value"`
Gas string `json:"gas"`
GasPrice string `json:"gasPrice"`
Hash string `json:"hash"`
Address string `json:"address"`
Sender string `json:"sender"`
RawData string `json:"rawData"`
Data string `json:"data"`
Contract bool `json:"isContract"`
CreatesContract bool `json:"createsContract"`
Confirmations int `json:"confirmations"`
}
func NewPTx(tx *ethchain.Transaction) *PTx {
hash := ethutil.Bytes2Hex(tx.Hash())
receiver := ethutil.Bytes2Hex(tx.Recipient)
if receiver == "0000000000000000000000000000000000000000" {
receiver = ethutil.Bytes2Hex(tx.CreationAddress())
}
sender := ethutil.Bytes2Hex(tx.Sender())
createsContract := tx.CreatesContract()
var data string
if tx.CreatesContract() {
data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
} else {
data = ethutil.Bytes2Hex(tx.Data)
}
return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)}
}
func (self *PTx) ToString() string {
return self.ref.String()
}
type PKey struct {
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
}
func NewPKey(key *ethcrypto.KeyPair) *PKey {
return &PKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)}
}
type PReceipt struct {
CreatedContract bool `json:"createdContract"`
Address string `json:"address"`
Hash string `json:"hash"`
Sender string `json:"sender"`
}
func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
return &PReceipt{
contractCreation,
ethutil.Bytes2Hex(creationAddress),
ethutil.Bytes2Hex(hash),
ethutil.Bytes2Hex(address),
}
}
type PStateObject struct {
object *ethstate.StateObject
}
func NewPStateObject(object *ethstate.StateObject) *PStateObject {
return &PStateObject{object: object}
}
func (c *PStateObject) GetStorage(address string) string {
// Because somehow, even if you return nil to QML it
// still has some magical object so we can't rely on
// undefined or null at the QML side
if c.object != nil {
val := c.object.GetStorage(ethutil.Big("0x" + address))
return val.BigInt().String()
}
return ""
}
func (c *PStateObject) Value() string {
if c.object != nil {
return c.object.Amount.String()
}
return ""
}
func (c *PStateObject) Address() string {
if c.object != nil {
return ethutil.Bytes2Hex(c.object.Address())
}
return ""
}
func (c *PStateObject) Nonce() int {
if c.object != nil {
return int(c.object.Nonce)
}
return 0
}
func (c *PStateObject) Root() string {
if c.object != nil {
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State.Root()).Bytes())
}
return "<err>"
}
func (c *PStateObject) IsContract() bool {
if c.object != nil {
return len(c.object.Code) > 0
}
return false
}
func (self *PStateObject) EachStorage(cb ethtrie.EachCallback) {
self.object.EachStorage(cb)
}
type KeyVal struct {
Key string
Value string
}
func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
var values []KeyVal
if c.object != nil {
c.object.EachStorage(func(name string, value *ethutil.Value) {
values = append(values, KeyVal{name, ethutil.Bytes2Hex(value.Bytes())})
})
}
if asJson {
valuesJson, err := json.Marshal(values)
if err != nil {
return nil
}
fmt.Println(string(valuesJson))
return string(valuesJson)
}
return values
}
func (c *PStateObject) Script() string {
if c.object != nil {
return strings.Join(ethchain.Disassemble(c.object.Code), " ")
}
return ""
}
func (c *PStateObject) HexScript() string {
if c.object != nil {
return ethutil.Bytes2Hex(c.object.Code)
}
return ""
}
type PStorageState struct {
StateAddress string
Address string
Value string
}
func NewPStorageState(storageObject *ethstate.StorageState) *PStorageState {
return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
}

28
ethreact/README.md Normal file
View File

@ -0,0 +1,28 @@
## Reactor
Reactor is the internal broadcast engine that allows components to be notified of ethereum stack events such as finding new blocks or change in state.
Event notification is handled via subscription:
var blockChan = make(chan ethreact.Event, 10)
reactor.Subscribe("newBlock", blockChan)
ethreact.Event broadcast on the channel are
type Event struct {
Resource interface{}
Name string
}
Resource is polimorphic depending on the event type and should be typecast before use, e.g:
b := <-blockChan:
block := b.Resource.(*ethchain.Block)
Events are guaranteed to be broadcast in order but the broadcast never blocks or leaks which means while the subscribing event channel is blocked (e.g., full if buffered) further messages will be skipped.
The engine allows arbitrary events to be posted and subscribed to.
ethereum.Reactor().Post("newBlock", newBlock)

182
ethreact/reactor.go Normal file
View File

@ -0,0 +1,182 @@
package ethreact
import (
"github.com/ethereum/eth-go/ethlog"
"sync"
)
var logger = ethlog.NewLogger("REACTOR")
const (
eventBufferSize int = 10
)
type EventHandler struct {
lock sync.RWMutex
name string
chans []chan Event
}
// Post the Event with the reactor resource on the channels
// currently subscribed to the event
func (e *EventHandler) Post(event Event) {
e.lock.RLock()
defer e.lock.RUnlock()
// if we want to preserve order pushing to subscibed channels
// dispatching should be syncrounous
// this means if subscribed event channel is blocked
// the reactor dispatch will be blocked, so we need to mitigate by skipping
// rogue blocking subscribers
for i, ch := range e.chans {
select {
case ch <- event:
default:
logger.Warnf("subscribing channel %d to event %s blocked. skipping\n", i, event.Name)
}
}
}
// Add a subscriber to this event
func (e *EventHandler) Add(ch chan Event) {
e.lock.Lock()
defer e.lock.Unlock()
e.chans = append(e.chans, ch)
}
// Remove a subscriber
func (e *EventHandler) Remove(ch chan Event) int {
e.lock.Lock()
defer e.lock.Unlock()
for i, c := range e.chans {
if c == ch {
e.chans = append(e.chans[:i], e.chans[i+1:]...)
}
}
return len(e.chans)
}
// Basic reactor event
type Event struct {
Resource interface{}
Name string
}
// The reactor basic engine. Acts as bridge
// between the events and the subscribers/posters
type ReactorEngine struct {
lock sync.RWMutex
eventChannel chan Event
eventHandlers map[string]*EventHandler
quit chan chan error
running bool
drained chan bool
}
func New() *ReactorEngine {
return &ReactorEngine{
eventHandlers: make(map[string]*EventHandler),
eventChannel: make(chan Event, eventBufferSize),
quit: make(chan chan error, 1),
drained: make(chan bool, 1),
}
}
func (reactor *ReactorEngine) Start() {
reactor.lock.Lock()
defer reactor.lock.Unlock()
if !reactor.running {
go func() {
for {
select {
case status := <-reactor.quit:
reactor.lock.Lock()
defer reactor.lock.Unlock()
reactor.running = false
logger.Infoln("stopped")
status <- nil
return
case event := <-reactor.eventChannel:
// needs to be called syncronously to keep order of events
reactor.dispatch(event)
default:
reactor.drained <- true // blocking till message is coming in
}
}
}()
reactor.running = true
logger.Infoln("started")
}
}
func (reactor *ReactorEngine) Stop() {
if reactor.running {
status := make(chan error)
reactor.quit <- status
select {
case <-reactor.drained:
default:
}
<-status
}
}
func (reactor *ReactorEngine) Flush() {
<-reactor.drained
}
// Subscribe a channel to the specified event
func (reactor *ReactorEngine) Subscribe(event string, eventChannel chan Event) {
reactor.lock.Lock()
defer reactor.lock.Unlock()
eventHandler := reactor.eventHandlers[event]
// Create a new event handler if one isn't available
if eventHandler == nil {
eventHandler = &EventHandler{name: event}
reactor.eventHandlers[event] = eventHandler
}
// Add the events channel to reactor event handler
eventHandler.Add(eventChannel)
logger.Debugf("added new subscription to %s", event)
}
func (reactor *ReactorEngine) Unsubscribe(event string, eventChannel chan Event) {
reactor.lock.Lock()
defer reactor.lock.Unlock()
eventHandler := reactor.eventHandlers[event]
if eventHandler != nil {
len := eventHandler.Remove(eventChannel)
if len == 0 {
reactor.eventHandlers[event] = nil
}
logger.Debugf("removed subscription to %s", event)
}
}
func (reactor *ReactorEngine) Post(event string, resource interface{}) {
reactor.lock.Lock()
defer reactor.lock.Unlock()
if reactor.running {
reactor.eventChannel <- Event{Resource: resource, Name: event}
select {
case <-reactor.drained:
default:
}
}
}
func (reactor *ReactorEngine) dispatch(event Event) {
name := event.Name
eventHandler := reactor.eventHandlers[name]
// if no subscriptions to this event type - no event handler created
// then noone to notify
if eventHandler != nil {
// needs to be called syncronously
eventHandler.Post(event)
}
}

63
ethreact/reactor_test.go Normal file
View File

@ -0,0 +1,63 @@
package ethreact
import (
"fmt"
"testing"
)
func TestReactorAdd(t *testing.T) {
reactor := New()
ch := make(chan Event)
reactor.Subscribe("test", ch)
if reactor.eventHandlers["test"] == nil {
t.Error("Expected new eventHandler to be created")
}
reactor.Unsubscribe("test", ch)
if reactor.eventHandlers["test"] != nil {
t.Error("Expected eventHandler to be removed")
}
}
func TestReactorEvent(t *testing.T) {
var name string
reactor := New()
// Buffer the channel, so it doesn't block for this test
cap := 20
ch := make(chan Event, cap)
reactor.Subscribe("even", ch)
reactor.Subscribe("odd", ch)
reactor.Post("even", "disappears") // should not broadcast if engine not started
reactor.Start()
for i := 0; i < cap; i++ {
if i%2 == 0 {
name = "even"
} else {
name = "odd"
}
reactor.Post(name, i)
}
reactor.Post("test", cap) // this should not block
i := 0
reactor.Flush()
close(ch)
for event := range ch {
fmt.Printf("%d: %v", i, event)
if i%2 == 0 {
name = "even"
} else {
name = "odd"
}
if val, ok := event.Resource.(int); ok {
if i != val || event.Name != name {
t.Error("Expected event %d to be of type %s and resource %d, got ", i, name, i, val)
}
} else {
t.Error("Unable to cast")
}
i++
}
if i != cap {
t.Error("excpected exactly %d events, got ", i)
}
reactor.Stop()
}

View File

@ -3,14 +3,15 @@ package ethrpc
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
"math/big" "math/big"
"strings" "strings"
"github.com/ethereum/eth-go/ethpipe"
"github.com/ethereum/eth-go/ethutil"
) )
type EthereumApi struct { type EthereumApi struct {
ethp *ethpub.PEthereum pipe *ethpipe.JSPipe
} }
type JsonArgs interface { type JsonArgs interface {
@ -72,8 +73,8 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
// Do something
block := p.ethp.GetBlock(args.Hash) block := p.pipe.BlockByHash(args.Hash)
*reply = NewSuccessRes(block) *reply = NewSuccessRes(block)
return nil return nil
} }
@ -128,7 +129,7 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) result, _ := p.pipe.Transact(p.pipe.Key().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body)
*reply = NewSuccessRes(result) *reply = NewSuccessRes(result)
return nil return nil
} }
@ -138,13 +139,14 @@ func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Body)
result, _ := p.pipe.Transact(p.pipe.Key().PrivateKey, "", args.Value, args.Gas, args.GasPrice, args.Body)
*reply = NewSuccessRes(result) *reply = NewSuccessRes(result)
return nil return nil
} }
func (p *EthereumApi) GetKey(args interface{}, reply *string) error { func (p *EthereumApi) GetKey(args interface{}, reply *string) error {
*reply = NewSuccessRes(p.ethp.GetKey()) *reply = NewSuccessRes(p.pipe.Key())
return nil return nil
} }
@ -174,7 +176,8 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
state := p.ethp.GetStateObject(args.Address)
state := p.pipe.World().SafeGet(ethutil.Hex2Bytes(args.Address))
var hx string var hx string
if strings.Index(args.Key, "0x") == 0 { if strings.Index(args.Key, "0x") == 0 {
@ -185,8 +188,8 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error {
hx = ethutil.Bytes2Hex(i.Bytes()) hx = ethutil.Bytes2Hex(i.Bytes())
} }
logger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx) logger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx)
value := state.GetStorage(hx) value := state.Storage(ethutil.Hex2Bytes(hx))
*reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value.Str()})
return nil return nil
} }
@ -209,7 +212,7 @@ type GetPeerCountRes struct {
} }
func (p *EthereumApi) GetPeerCount(args *interface{}, reply *string) error { func (p *EthereumApi) GetPeerCount(args *interface{}, reply *string) error {
*reply = NewSuccessRes(GetPeerCountRes{PeerCount: p.ethp.GetPeerCount()}) *reply = NewSuccessRes(GetPeerCountRes{PeerCount: p.pipe.PeerCount()})
return nil return nil
} }
@ -218,7 +221,7 @@ type GetListeningRes struct {
} }
func (p *EthereumApi) GetIsListening(args *interface{}, reply *string) error { func (p *EthereumApi) GetIsListening(args *interface{}, reply *string) error {
*reply = NewSuccessRes(GetListeningRes{IsListening: p.ethp.GetIsListening()}) *reply = NewSuccessRes(GetListeningRes{IsListening: p.pipe.IsListening()})
return nil return nil
} }
@ -227,7 +230,7 @@ type GetCoinbaseRes struct {
} }
func (p *EthereumApi) GetCoinbase(args *interface{}, reply *string) error { func (p *EthereumApi) GetCoinbase(args *interface{}, reply *string) error {
*reply = NewSuccessRes(GetCoinbaseRes{Coinbase: p.ethp.GetCoinBase()}) *reply = NewSuccessRes(GetCoinbaseRes{Coinbase: p.pipe.CoinBase()})
return nil return nil
} }
@ -236,7 +239,7 @@ type GetMiningRes struct {
} }
func (p *EthereumApi) GetIsMining(args *interface{}, reply *string) error { func (p *EthereumApi) GetIsMining(args *interface{}, reply *string) error {
*reply = NewSuccessRes(GetMiningRes{IsMining: p.ethp.GetIsMining()}) *reply = NewSuccessRes(GetMiningRes{IsMining: p.pipe.IsMining()})
return nil return nil
} }
@ -245,7 +248,7 @@ func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
state := p.ethp.GetTxCountAt(args.Address) state := p.pipe.TxCountAt(args.Address)
*reply = NewSuccessRes(GetTxCountRes{Nonce: state}) *reply = NewSuccessRes(GetTxCountRes{Nonce: state})
return nil return nil
} }
@ -271,8 +274,8 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error {
if err != nil { if err != nil {
return err return err
} }
state := p.ethp.GetStateObject(args.Address) state := p.pipe.World().SafeGet(ethutil.Hex2Bytes(args.Address))
*reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) *reply = NewSuccessRes(BalanceRes{Balance: state.Balance.String(), Address: args.Address})
return nil return nil
} }

View File

@ -2,11 +2,12 @@ package ethrpc
import ( import (
"fmt" "fmt"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpub"
"net" "net"
"net/rpc" "net/rpc"
"net/rpc/jsonrpc" "net/rpc/jsonrpc"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethpipe"
) )
var logger = ethlog.NewLogger("JSON") var logger = ethlog.NewLogger("JSON")
@ -14,7 +15,7 @@ var logger = ethlog.NewLogger("JSON")
type JsonRpcServer struct { type JsonRpcServer struct {
quit chan bool quit chan bool
listener net.Listener listener net.Listener
ethp *ethpub.PEthereum pipe *ethpipe.JSPipe
} }
func (s *JsonRpcServer) exitHandler() { func (s *JsonRpcServer) exitHandler() {
@ -37,7 +38,7 @@ func (s *JsonRpcServer) Stop() {
func (s *JsonRpcServer) Start() { func (s *JsonRpcServer) Start() {
logger.Infoln("Starting JSON-RPC server") logger.Infoln("Starting JSON-RPC server")
go s.exitHandler() go s.exitHandler()
rpc.Register(&EthereumApi{ethp: s.ethp}) rpc.Register(&EthereumApi{pipe: s.pipe})
rpc.HandleHTTP() rpc.HandleHTTP()
for { for {
@ -51,7 +52,7 @@ func (s *JsonRpcServer) Start() {
} }
} }
func NewJsonRpcServer(ethp *ethpub.PEthereum, port int) (*JsonRpcServer, error) { func NewJsonRpcServer(pipe *ethpipe.JSPipe, port int) (*JsonRpcServer, error) {
sport := fmt.Sprintf(":%d", port) sport := fmt.Sprintf(":%d", port)
l, err := net.Listen("tcp", sport) l, err := net.Listen("tcp", sport)
if err != nil { if err != nil {
@ -61,6 +62,6 @@ func NewJsonRpcServer(ethp *ethpub.PEthereum, port int) (*JsonRpcServer, error)
return &JsonRpcServer{ return &JsonRpcServer{
listener: l, listener: l,
quit: make(chan bool), quit: make(chan bool),
ethp: ethp, pipe: pipe,
}, nil }, nil
} }

47
ethstate/dump.go Normal file
View File

@ -0,0 +1,47 @@
package ethstate
import (
"encoding/json"
"fmt"
"github.com/ethereum/eth-go/ethutil"
)
type Account struct {
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
CodeHash string `json:"codeHash"`
Storage map[string]string `json:"storage"`
}
type World struct {
Root string `json:"root"`
Accounts map[string]Account `json:"accounts"`
}
func (self *State) Dump() []byte {
world := World{
Root: ethutil.Bytes2Hex(self.Trie.Root.([]byte)),
Accounts: make(map[string]Account),
}
self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) {
stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes())
account := Account{Balance: stateObject.Balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.CodeHash)}
account.Storage = make(map[string]string)
stateObject.EachStorage(func(key string, value *ethutil.Value) {
value.Decode()
account.Storage[ethutil.Bytes2Hex([]byte(key))] = ethutil.Bytes2Hex(value.Bytes())
})
world.Accounts[ethutil.Bytes2Hex([]byte(key))] = account
})
json, err := json.MarshalIndent(world, "", " ")
if err != nil {
fmt.Println("dump err", err)
}
return json
}

55
ethstate/manifest.go Normal file
View File

@ -0,0 +1,55 @@
package ethstate
import (
"fmt"
"math/big"
)
// Object manifest
//
// The object manifest is used to keep changes to the state so we can keep track of the changes
// that occurred during a state transitioning phase.
type Manifest struct {
Messages Messages
}
func NewManifest() *Manifest {
m := &Manifest{}
m.Reset()
return m
}
func (m *Manifest) Reset() {
m.Messages = nil
}
func (self *Manifest) AddMessage(msg *Message) *Message {
self.Messages = append(self.Messages, msg)
return msg
}
type Messages []*Message
type Message struct {
To, From []byte
Input []byte
Output []byte
Path int
Origin []byte
Timestamp int64
Coinbase []byte
Block []byte
Number *big.Int
Value *big.Int
ChangedAddresses [][]byte
}
func (self *Message) AddStorageChange(addr []byte) {
self.ChangedAddresses = append(self.ChangedAddresses, addr)
}
func (self *Message) String() string {
return fmt.Sprintf("Message{to: %x from: %x input: %x output: %x origin: %x coinbase: %x block: %x number: %v timestamp: %d path: %d value: %v", self.To, self.From, self.Input, self.Output, self.Origin, self.Coinbase, self.Block, self.Number, self.Timestamp, self.Path, self.Value)
}

View File

@ -1,11 +1,12 @@
package ethstate package ethstate
import ( import (
"math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big"
) )
var statelogger = ethlog.NewLogger("STATE") var statelogger = ethlog.NewLogger("STATE")
@ -25,7 +26,7 @@ type State struct {
} }
// Create a new state from a given trie // Create a new state from a given trie
func NewState(trie *ethtrie.Trie) *State { func New(trie *ethtrie.Trie) *State {
return &State{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} return &State{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
} }
@ -33,7 +34,7 @@ func NewState(trie *ethtrie.Trie) *State {
func (self *State) GetBalance(addr []byte) *big.Int { func (self *State) GetBalance(addr []byte) *big.Int {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.Amount return stateObject.Balance
} }
return ethutil.Big0 return ethutil.Big0
@ -59,8 +60,6 @@ func (self *State) UpdateStateObject(stateObject *StateObject) {
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code) ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code)
self.Trie.Update(string(addr), string(stateObject.RlpEncode())) self.Trie.Update(string(addr), string(stateObject.RlpEncode()))
self.manifest.AddObjectChange(stateObject)
} }
// Delete the given state object and delete it from the state trie // Delete the given state object and delete it from the state trie
@ -127,7 +126,7 @@ func (s *State) Cmp(other *State) bool {
func (self *State) Copy() *State { func (self *State) Copy() *State {
if self.Trie != nil { if self.Trie != nil {
state := NewState(self.Trie.Copy()) state := New(self.Trie.Copy())
for k, stateObject := range self.stateObjects { for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy() state.stateObjects[k] = stateObject.Copy()
} }
@ -210,50 +209,13 @@ func (self *State) Update() {
} }
} }
func (self *State) Manifest() *Manifest {
return self.manifest
}
// Debug stuff // Debug stuff
func (self *State) CreateOutputForDiff() { func (self *State) CreateOutputForDiff() {
for _, stateObject := range self.stateObjects { for _, stateObject := range self.stateObjects {
stateObject.CreateOutputForDiff() stateObject.CreateOutputForDiff()
} }
} }
func (self *State) Manifest() *Manifest {
return self.manifest
}
// Object manifest
//
// The object manifest is used to keep changes to the state so we can keep track of the changes
// that occurred during a state transitioning phase.
type Manifest struct {
// XXX These will be handy in the future. Not important for now.
objectAddresses map[string]bool
storageAddresses map[string]map[string]bool
ObjectChanges map[string]*StateObject
StorageChanges map[string]map[string]*big.Int
}
func NewManifest() *Manifest {
m := &Manifest{objectAddresses: make(map[string]bool), storageAddresses: make(map[string]map[string]bool)}
m.Reset()
return m
}
func (m *Manifest) Reset() {
m.ObjectChanges = make(map[string]*StateObject)
m.StorageChanges = make(map[string]map[string]*big.Int)
}
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
m.ObjectChanges[string(stateObject.Address())] = stateObject
}
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
if m.StorageChanges[string(stateObject.Address())] == nil {
m.StorageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
}
m.StorageChanges[string(stateObject.Address())][string(storageAddr)] = storage
}

View File

@ -2,10 +2,11 @@ package ethstate
import ( import (
"fmt" "fmt"
"math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big"
) )
type Code []byte type Code []byte
@ -30,7 +31,7 @@ type StateObject struct {
// Address of the object // Address of the object
address []byte address []byte
// Shared attributes // Shared attributes
Amount *big.Int Balance *big.Int
CodeHash []byte CodeHash []byte
Nonce uint64 Nonce uint64
// Contract related attributes // Contract related attributes
@ -56,40 +57,22 @@ func (self *StateObject) Reset() {
self.State.Reset() self.State.Reset()
} }
/*
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *State) *StateObject {
// Create contract if there's no recipient
if tx.IsContract() {
addr := tx.CreationAddress()
contract := state.NewStateObject(addr)
contract.initCode = tx.Data
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
return contract
}
return nil
}
*/
func NewStateObject(addr []byte) *StateObject { func NewStateObject(addr []byte) *StateObject {
// This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter. // This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
address := ethutil.Address(addr) address := ethutil.Address(addr)
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)} object := &StateObject{address: address, Balance: new(big.Int), gasPool: new(big.Int)}
object.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) object.State = New(ethtrie.New(ethutil.Config.Db, ""))
object.storage = make(Storage) object.storage = make(Storage)
object.gasPool = new(big.Int) object.gasPool = new(big.Int)
return object return object
} }
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { func NewContract(address []byte, balance *big.Int, root []byte) *StateObject {
contract := NewStateObject(address) contract := NewStateObject(address)
contract.Amount = Amount contract.Balance = balance
contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) contract.State = New(ethtrie.New(ethutil.Config.Db, string(root)))
return contract return contract
} }
@ -103,7 +86,7 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject {
func (self *StateObject) MarkForDeletion() { func (self *StateObject) MarkForDeletion() {
self.remove = true self.remove = true
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount) statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Balance)
} }
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
@ -190,19 +173,19 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
} }
func (c *StateObject) AddAmount(amount *big.Int) { func (c *StateObject) AddAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Add(c.Amount, amount)) c.SetBalance(new(big.Int).Add(c.Balance, amount))
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Balance, amount)
} }
func (c *StateObject) SubAmount(amount *big.Int) { func (c *StateObject) SubAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Sub(c.Amount, amount)) c.SetBalance(new(big.Int).Sub(c.Balance, amount))
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Balance, amount)
} }
func (c *StateObject) SetAmount(amount *big.Int) { func (c *StateObject) SetBalance(amount *big.Int) {
c.Amount = amount c.Balance = amount
} }
// //
@ -213,8 +196,8 @@ func (c *StateObject) SetAmount(amount *big.Int) {
func (c *StateObject) ReturnGas(gas, price *big.Int) {} func (c *StateObject) ReturnGas(gas, price *big.Int) {}
func (c *StateObject) ConvertGas(gas, price *big.Int) error { func (c *StateObject) ConvertGas(gas, price *big.Int) error {
total := new(big.Int).Mul(gas, price) total := new(big.Int).Mul(gas, price)
if total.Cmp(c.Amount) > 0 { if total.Cmp(c.Balance) > 0 {
return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total) return fmt.Errorf("insufficient amount: %v, %v", c.Balance, total)
} }
c.SubAmount(total) c.SubAmount(total)
@ -247,12 +230,12 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
rGas := new(big.Int).Set(gas) rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price) rGas.Mul(rGas, price)
self.Amount.Sub(self.Amount, rGas) self.Balance.Sub(self.Balance, rGas)
} }
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address()) stateObject := NewStateObject(self.Address())
stateObject.Amount.Set(self.Amount) stateObject.Balance.Set(self.Balance)
stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash) stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash)
stateObject.Nonce = self.Nonce stateObject.Nonce = self.Nonce
if self.State != nil { if self.State != nil {
@ -290,7 +273,7 @@ func (c *StateObject) Init() Code {
// Debug stuff // Debug stuff
func (self *StateObject) CreateOutputForDiff() { func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Amount.Bytes(), self.Nonce) fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce)
self.EachStorage(func(addr string, value *ethutil.Value) { self.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("%x %x\n", addr, value.Bytes()) fmt.Printf("%x %x\n", addr, value.Bytes())
}) })
@ -309,15 +292,15 @@ func (c *StateObject) RlpEncode() []byte {
root = "" root = ""
} }
return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethcrypto.Sha3Bin(c.Code)}) return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, ethcrypto.Sha3Bin(c.Code)})
} }
func (c *StateObject) RlpDecode(data []byte) { func (c *StateObject) RlpDecode(data []byte) {
decoder := ethutil.NewValueFromBytes(data) decoder := ethutil.NewValueFromBytes(data)
c.Nonce = decoder.Get(0).Uint() c.Nonce = decoder.Get(0).Uint()
c.Amount = decoder.Get(1).BigInt() c.Balance = decoder.Get(1).BigInt()
c.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.State = New(ethtrie.New(ethutil.Config.Db, decoder.Get(2).Interface()))
c.storage = make(map[string]*ethutil.Value) c.storage = make(map[string]*ethutil.Value)
c.gasPool = new(big.Int) c.gasPool = new(big.Int)

View File

@ -1,10 +1,11 @@
package ethstate package ethstate
import ( import (
"testing"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"testing"
) )
var ZeroHash256 = make([]byte, 32) var ZeroHash256 = make([]byte, 32)
@ -14,7 +15,7 @@ func TestSnapshot(t *testing.T) {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db = db ethutil.Config.Db = db
state := NewState(ethtrie.NewTrie(db, "")) state := New(ethtrie.New(db, ""))
stateObject := state.GetOrNewStateObject([]byte("aa")) stateObject := state.GetOrNewStateObject([]byte("aa"))

View File

@ -1,6 +1,6 @@
package ethtrie package ethtrie
import () import "math"
// Helper function for comparing slices // Helper function for comparing slices
func CompareIntSlice(a, b []int) bool { func CompareIntSlice(a, b []int) bool {
@ -17,9 +17,13 @@ func CompareIntSlice(a, b []int) bool {
// Returns the amount of nibbles that match each other from 0 ... // Returns the amount of nibbles that match each other from 0 ...
func MatchingNibbleLength(a, b []int) int { func MatchingNibbleLength(a, b []int) int {
i := 0 var i, length = 0, int(math.Min(float64(len(a)), float64(len(b))))
for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) {
i += 1 for i < length {
if a[i] != b[i] {
break
}
i++
} }
return i return i

View File

@ -3,16 +3,17 @@ package ethtrie
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethutil"
_ "reflect" _ "reflect"
"sync" "sync"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethutil"
) )
func __ignore() { fmt.Println("") } func __ignore() { fmt.Println("") }
func ParanoiaCheck(t1 *Trie) (bool, *Trie) { func ParanoiaCheck(t1 *Trie) (bool, *Trie) {
t2 := NewTrie(ethutil.Config.Db, "") t2 := New(ethutil.Config.Db, "")
t1.NewIterator().Each(func(key string, v *ethutil.Value) { t1.NewIterator().Each(func(key string, v *ethutil.Value) {
t2.Update(key, v.Str()) t2.Update(key, v.Str())
@ -158,7 +159,7 @@ func copyRoot(root interface{}) interface{} {
return prevRootCopy return prevRootCopy
} }
func NewTrie(db ethutil.Database, Root interface{}) *Trie { func New(db ethutil.Database, Root interface{}) *Trie {
// Make absolute sure the root is copied // Make absolute sure the root is copied
r := copyRoot(Root) r := copyRoot(Root)
p := copyRoot(Root) p := copyRoot(Root)
@ -221,7 +222,7 @@ func (t *Trie) Cmp(trie *Trie) bool {
// Returns a copy of this trie // Returns a copy of this trie
func (t *Trie) Copy() *Trie { func (t *Trie) Copy() *Trie {
trie := NewTrie(t.cache.db, t.Root) trie := New(t.cache.db, t.Root)
for key, node := range t.cache.nodes { for key, node := range t.cache.nodes {
trie.cache.nodes[key] = node.Copy() trie.cache.nodes[key] = node.Copy()
} }

View File

@ -5,13 +5,14 @@ import (
_ "encoding/hex" _ "encoding/hex"
_ "encoding/json" _ "encoding/json"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil"
_ "io/ioutil" _ "io/ioutil"
_ "math/rand" _ "math/rand"
_ "net/http" _ "net/http"
_ "reflect" _ "reflect"
"testing" "testing"
_ "time" _ "time"
"github.com/ethereum/eth-go/ethutil"
) )
const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ" const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ"
@ -38,14 +39,14 @@ func (db *MemDatabase) Print() {}
func (db *MemDatabase) Close() {} func (db *MemDatabase) Close() {}
func (db *MemDatabase) LastKnownTD() []byte { return nil } func (db *MemDatabase) LastKnownTD() []byte { return nil }
func New() (*MemDatabase, *Trie) { func NewTrie() (*MemDatabase, *Trie) {
db, _ := NewMemDatabase() db, _ := NewMemDatabase()
return db, NewTrie(db, "") return db, New(db, "")
} }
/* /*
func TestTrieSync(t *testing.T) { func TestTrieSync(t *testing.T) {
db, trie := New() db, trie := NewTrie()
trie.Update("dog", LONG_WORD) trie.Update("dog", LONG_WORD)
if len(db.db) != 0 { if len(db.db) != 0 {
@ -59,7 +60,7 @@ func TestTrieSync(t *testing.T) {
} }
func TestTrieDirtyTracking(t *testing.T) { func TestTrieDirtyTracking(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("dog", LONG_WORD) trie.Update("dog", LONG_WORD)
if !trie.cache.IsDirty { if !trie.cache.IsDirty {
t.Error("Expected trie to be dirty") t.Error("Expected trie to be dirty")
@ -79,7 +80,7 @@ func TestTrieDirtyTracking(t *testing.T) {
} }
func TestTrieReset(t *testing.T) { func TestTrieReset(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD)
if len(trie.cache.nodes) == 0 { if len(trie.cache.nodes) == 0 {
@ -94,7 +95,7 @@ func TestTrieReset(t *testing.T) {
} }
func TestTrieGet(t *testing.T) { func TestTrieGet(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD)
x := trie.Get("cat") x := trie.Get("cat")
@ -104,7 +105,7 @@ func TestTrieGet(t *testing.T) {
} }
func TestTrieUpdating(t *testing.T) { func TestTrieUpdating(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD)
trie.Update("cat", LONG_WORD+"1") trie.Update("cat", LONG_WORD+"1")
x := trie.Get("cat") x := trie.Get("cat")
@ -114,8 +115,8 @@ func TestTrieUpdating(t *testing.T) {
} }
func TestTrieCmp(t *testing.T) { func TestTrieCmp(t *testing.T) {
_, trie1 := New() _, trie1 := NewTrie()
_, trie2 := New() _, trie2 := NewTrie()
trie1.Update("doge", LONG_WORD) trie1.Update("doge", LONG_WORD)
trie2.Update("doge", LONG_WORD) trie2.Update("doge", LONG_WORD)
@ -131,7 +132,7 @@ func TestTrieCmp(t *testing.T) {
} }
func TestTrieDelete(t *testing.T) { func TestTrieDelete(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD)
exp := trie.Root exp := trie.Root
trie.Update("dog", LONG_WORD) trie.Update("dog", LONG_WORD)
@ -150,7 +151,7 @@ func TestTrieDelete(t *testing.T) {
} }
func TestTrieDeleteWithValue(t *testing.T) { func TestTrieDeleteWithValue(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("c", LONG_WORD) trie.Update("c", LONG_WORD)
exp := trie.Root exp := trie.Root
trie.Update("ca", LONG_WORD) trie.Update("ca", LONG_WORD)
@ -164,7 +165,7 @@ func TestTrieDeleteWithValue(t *testing.T) {
} }
func TestTriePurge(t *testing.T) { func TestTriePurge(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("c", LONG_WORD) trie.Update("c", LONG_WORD)
trie.Update("ca", LONG_WORD) trie.Update("ca", LONG_WORD)
trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD)
@ -248,7 +249,7 @@ func CreateTests(uri string, cb func(Test)) map[string]Test {
func TestRemote(t *testing.T) { func TestRemote(t *testing.T) {
CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) { CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) {
_, trie := New() _, trie := NewTrie()
for key, value := range test.In { for key, value := range test.In {
trie.Update(get(key), get(value)) trie.Update(get(key), get(value))
} }
@ -263,12 +264,12 @@ func TestRemote(t *testing.T) {
func TestTrieReplay(t *testing.T) { func TestTrieReplay(t *testing.T) {
CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) { CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) {
_, trie := New() _, trie := NewTrie()
for key, value := range test.In { for key, value := range test.In {
trie.Update(get(key), get(value)) trie.Update(get(key), get(value))
} }
_, trie2 := New() _, trie2 := NewTrie()
trie.NewIterator().Each(func(key string, v *ethutil.Value) { trie.NewIterator().Each(func(key string, v *ethutil.Value) {
trie2.Update(key, v.Str()) trie2.Update(key, v.Str())
}) })
@ -314,7 +315,7 @@ func TestRegression(t *testing.T) {
roots := make(map[string]int) roots := make(map[string]int)
for i := 0; i < MaxTest; i++ { for i := 0; i < MaxTest; i++ {
_, trie := New() _, trie := NewTrie()
data := RandomData() data := RandomData()
for _, test := range data { for _, test := range data {
@ -333,7 +334,7 @@ func TestRegression(t *testing.T) {
} }
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
_, trie := New() _, trie := NewTrie()
trie.Update("a", "jeffreytestlongstring") trie.Update("a", "jeffreytestlongstring")
trie.Update("aa", "otherstring") trie.Update("aa", "otherstring")
@ -352,7 +353,7 @@ func TestDelete(t *testing.T) {
trie.Update("aaaa", "testmegood") trie.Update("aaaa", "testmegood")
fmt.Println("aa =>", trie.Get("aa")) fmt.Println("aa =>", trie.Get("aa"))
_, t2 := New() _, t2 := NewTrie()
trie.NewIterator().Each(func(key string, v *ethutil.Value) { trie.NewIterator().Each(func(key string, v *ethutil.Value) {
if key == "aaaa" { if key == "aaaa" {
t2.Update(key, v.Str()) t2.Update(key, v.Str())
@ -369,7 +370,7 @@ func TestDelete(t *testing.T) {
*/ */
func TestRndCase(t *testing.T) { func TestRndCase(t *testing.T) {
_, trie := New() _, trie := NewTrie()
data := []struct{ k, v string }{ data := []struct{ k, v string }{
{"0000000000000000000000000000000000000000000000000000000000000001", "a07573657264617461000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "a07573657264617461000000000000000000000000000000000000000000000000"},

View File

@ -44,26 +44,28 @@ func BytesToNumber(b []byte) uint64 {
// Read variable int // Read variable int
// //
// Read a variable length number in big endian byte order // Read a variable length number in big endian byte order
func ReadVarint(reader *bytes.Reader) (ret uint64) { func ReadVarInt(buff []byte) (ret uint64) {
if reader.Len() == 8 { switch l := len(buff); {
var num uint64 case l > 4:
binary.Read(reader, binary.BigEndian, &num) d := LeftPadBytes(buff, 8)
ret = uint64(num) binary.Read(bytes.NewReader(d), binary.BigEndian, &ret)
} else if reader.Len() == 4 { case l > 2:
var num uint32 var num uint32
binary.Read(reader, binary.BigEndian, &num) d := LeftPadBytes(buff, 4)
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
ret = uint64(num) ret = uint64(num)
} else if reader.Len() == 2 { case l > 1:
var num uint16 var num uint16
binary.Read(reader, binary.BigEndian, &num) d := LeftPadBytes(buff, 2)
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
ret = uint64(num) ret = uint64(num)
} else { default:
var num uint8 var num uint8
binary.Read(reader, binary.BigEndian, &num) binary.Read(bytes.NewReader(buff), binary.BigEndian, &num)
ret = uint64(num) ret = uint64(num)
} }
return ret return
} }
// Binary length // Binary length
@ -98,6 +100,7 @@ func Bytes2Hex(d []byte) string {
func Hex2Bytes(str string) []byte { func Hex2Bytes(str string) []byte {
h, _ := hex.DecodeString(str) h, _ := hex.DecodeString(str)
return h return h
} }
@ -128,6 +131,26 @@ func FormatData(data string) []byte {
return BigToBytes(d, 256) return BigToBytes(d, 256)
} }
func ParseData(data ...interface{}) (ret []byte) {
for _, item := range data {
switch t := item.(type) {
case string:
var str []byte
if IsHex(t) {
str = Hex2Bytes(t[2:])
} else {
str = []byte(t)
}
ret = append(ret, RightPadBytes(str, 32)...)
case []byte:
ret = append(ret, LeftPadBytes(t, 32)...)
}
}
return
}
func RightPadBytes(slice []byte, l int) []byte { func RightPadBytes(slice []byte, l int) []byte {
if l < len(slice) { if l < len(slice) {
return slice return slice
@ -150,6 +173,28 @@ func LeftPadBytes(slice []byte, l int) []byte {
return padded return padded
} }
func LeftPadString(str string, l int) string {
if l < len(str) {
return str
}
zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
return zeros + str
}
func RightPadString(str string, l int) string {
if l < len(str) {
return str
}
zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
return str + zeros
}
func Address(slice []byte) (addr []byte) { func Address(slice []byte) (addr []byte) {
if len(slice) < 20 { if len(slice) < 20 {
addr = LeftPadBytes(slice, 20) addr = LeftPadBytes(slice, 20)
@ -163,3 +208,11 @@ func Address(slice []byte) (addr []byte) {
return return
} }
func ByteSliceToInterface(slice [][]byte) (ret []interface{}) {
for _, i := range slice {
ret = append(ret, i)
}
return
}

14
ethutil/bytes_test.go Normal file
View File

@ -0,0 +1,14 @@
package ethutil
import (
"bytes"
"testing"
)
func TestParseData(t *testing.T) {
data := ParseData("hello", "world", "0x0106")
exp := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000"
if bytes.Compare(data, Hex2Bytes(exp)) != 0 {
t.Error("Error parsing data")
}
}

View File

@ -34,26 +34,43 @@ var (
// Currency to string // Currency to string
// Returns a string representing a human readable format // Returns a string representing a human readable format
func CurrencyToString(num *big.Int) string { func CurrencyToString(num *big.Int) string {
var (
fin *big.Int = num
denom string = "Wei"
)
switch { switch {
case num.Cmp(Douglas) >= 0: case num.Cmp(Douglas) >= 0:
return fmt.Sprintf("%v Douglas", new(big.Int).Div(num, Douglas)) fin = new(big.Int).Div(num, Douglas)
denom = "Douglas"
case num.Cmp(Einstein) >= 0: case num.Cmp(Einstein) >= 0:
return fmt.Sprintf("%v Einstein", new(big.Int).Div(num, Einstein)) fin = new(big.Int).Div(num, Einstein)
denom = "Einstein"
case num.Cmp(Ether) >= 0: case num.Cmp(Ether) >= 0:
return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether)) fin = new(big.Int).Div(num, Ether)
denom = "Ether"
case num.Cmp(Finney) >= 0: case num.Cmp(Finney) >= 0:
return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney)) fin = new(big.Int).Div(num, Finney)
denom = "Finney"
case num.Cmp(Szabo) >= 0: case num.Cmp(Szabo) >= 0:
return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) fin = new(big.Int).Div(num, Szabo)
denom = "Szabo"
case num.Cmp(Shannon) >= 0: case num.Cmp(Shannon) >= 0:
return fmt.Sprintf("%v Shannon", new(big.Int).Div(num, Shannon)) fin = new(big.Int).Div(num, Shannon)
denom = "Shannon"
case num.Cmp(Babbage) >= 0: case num.Cmp(Babbage) >= 0:
return fmt.Sprintf("%v Babbage", new(big.Int).Div(num, Babbage)) fin = new(big.Int).Div(num, Babbage)
denom = "Babbage"
case num.Cmp(Ada) >= 0: case num.Cmp(Ada) >= 0:
return fmt.Sprintf("%v Ada", new(big.Int).Div(num, Ada)) fin = new(big.Int).Div(num, Ada)
denom = "Ada"
} }
return fmt.Sprintf("%v Wei", num) if len(fin.String()) > 5 {
return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom)
}
return fmt.Sprintf("%v %s", fin, denom)
} }
// Common big integers often used // Common big integers often used

View File

@ -3,8 +3,9 @@ package ethutil
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/rakyll/globalconf"
"os" "os"
"github.com/rakyll/globalconf"
) )
// Config struct // Config struct
@ -28,8 +29,7 @@ var Config *ConfigManager
func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager { func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager {
if Config == nil { if Config == nil {
// create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags // create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags
_, err := os.Stat(ConfigFile) if !FileExist(ConfigFile) {
if err != nil && os.IsNotExist(err) {
fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile) fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile)
os.Create(ConfigFile) os.Create(ConfigFile)
} }

View File

@ -1,6 +1,8 @@
package ethutil package ethutil
import ( import (
"io/ioutil"
"os"
"os/user" "os/user"
"strings" "strings"
) )
@ -18,3 +20,41 @@ func ExpandHomePath(p string) (path string) {
return return
} }
func FileExist(filePath string) bool {
_, err := os.Stat(filePath)
if err != nil && os.IsNotExist(err) {
return false
}
return true
}
func ReadAllFile(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
data, err := ioutil.ReadAll(file)
if err != nil {
return "", err
}
return string(data), nil
}
func WriteFile(filePath string, content []byte) error {
fh, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
_, err = fh.Write(content)
if err != nil {
return err
}
return nil
}

View File

@ -1,87 +0,0 @@
package ethutil
import (
"sync"
)
type ReactorEvent struct {
mut sync.Mutex
event string
chans []chan React
}
// Post the specified reactor resource on the channels
// currently subscribed
func (e *ReactorEvent) Post(react React) {
e.mut.Lock()
defer e.mut.Unlock()
for _, ch := range e.chans {
go func(ch chan React) {
ch <- react
}(ch)
}
}
// Add a subscriber to this event
func (e *ReactorEvent) Add(ch chan React) {
e.mut.Lock()
defer e.mut.Unlock()
e.chans = append(e.chans, ch)
}
// Remove a subscriber
func (e *ReactorEvent) Remove(ch chan React) {
e.mut.Lock()
defer e.mut.Unlock()
for i, c := range e.chans {
if c == ch {
e.chans = append(e.chans[:i], e.chans[i+1:]...)
}
}
}
// Basic reactor resource
type React struct {
Resource interface{}
Event string
}
// The reactor basic engine. Acts as bridge
// between the events and the subscribers/posters
type ReactorEngine struct {
patterns map[string]*ReactorEvent
}
func NewReactorEngine() *ReactorEngine {
return &ReactorEngine{patterns: make(map[string]*ReactorEvent)}
}
// Subscribe a channel to the specified event
func (reactor *ReactorEngine) Subscribe(event string, ch chan React) {
ev := reactor.patterns[event]
// Create a new event if one isn't available
if ev == nil {
ev = &ReactorEvent{event: event}
reactor.patterns[event] = ev
}
// Add the channel to reactor event handler
ev.Add(ch)
}
func (reactor *ReactorEngine) Unsubscribe(event string, ch chan React) {
ev := reactor.patterns[event]
if ev != nil {
ev.Remove(ch)
}
}
func (reactor *ReactorEngine) Post(event string, resource interface{}) {
ev := reactor.patterns[event]
if ev != nil {
ev.Post(React{Resource: resource, Event: event})
}
}

View File

@ -1,30 +0,0 @@
package ethutil
import "testing"
func TestReactorAdd(t *testing.T) {
engine := NewReactorEngine()
ch := make(chan React)
engine.Subscribe("test", ch)
if len(engine.patterns) != 1 {
t.Error("Expected patterns to be 1, got", len(engine.patterns))
}
}
func TestReactorEvent(t *testing.T) {
engine := NewReactorEngine()
// Buffer 1, so it doesn't block for this test
ch := make(chan React, 1)
engine.Subscribe("test", ch)
engine.Post("test", "hello")
value := <-ch
if val, ok := value.Resource.(string); ok {
if val != "hello" {
t.Error("Expected Resource to be 'hello', got", val)
}
} else {
t.Error("Unable to cast")
}
}

View File

@ -2,15 +2,16 @@ package ethutil
import ( import (
"bytes" "bytes"
_ "encoding/binary"
"fmt" "fmt"
_ "log"
_ "math"
"math/big" "math/big"
) )
type RlpEncodable interface { type RlpEncode interface {
RlpEncode() []byte RlpEncode() []byte
}
type RlpEncodeDecode interface {
RlpEncode
RlpValue() []interface{} RlpValue() []interface{}
} }
@ -32,12 +33,14 @@ const (
RlpEmptyStr = 0x40 RlpEmptyStr = 0x40
) )
const rlpEof = -1
func Char(c []byte) int { func Char(c []byte) int {
if len(c) > 0 { if len(c) > 0 {
return int(c[0]) return int(c[0])
} }
return 0 return rlpEof
} }
func DecodeWithReader(reader *bytes.Buffer) interface{} { func DecodeWithReader(reader *bytes.Buffer) interface{} {
@ -46,8 +49,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
// Read the next byte // Read the next byte
char := Char(reader.Next(1)) char := Char(reader.Next(1))
switch { switch {
case char == 0:
return nil
case char <= 0x7f: case char <= 0x7f:
return char return char
@ -55,8 +56,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
return reader.Next(int(char - 0x80)) return reader.Next(int(char - 0x80))
case char <= 0xbf: case char <= 0xbf:
buff := bytes.NewReader(reader.Next(int(char - 0xb8))) length := ReadVarInt(reader.Next(int(char - 0xb7)))
length := ReadVarint(buff)
return reader.Next(int(length)) return reader.Next(int(length))
@ -64,82 +64,23 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
length := int(char - 0xc0) length := int(char - 0xc0)
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
obj := DecodeWithReader(reader) obj := DecodeWithReader(reader)
if obj != nil { slice = append(slice, obj)
slice = append(slice, obj)
} else {
break
}
} }
return slice return slice
}
return slice
}
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff: case char <= 0xff:
l := uint64(data[pos]) - 0xf7 length := ReadVarInt(reader.Next(int(char - 0xf7)))
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l])) for i := uint64(0); i < length; i++ {
obj := DecodeWithReader(reader)
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj) slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
} }
return slice, pos
return slice
default: default:
panic(fmt.Sprintf("byte not supported: %q", char)) panic(fmt.Sprintf("byte not supported: %q", char))
} }
return slice, 0 return slice
} }
var ( var (
@ -223,3 +164,67 @@ func Encode(object interface{}) []byte {
return buff.Bytes() return buff.Bytes()
} }
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarInt(data[pos+1 : pos+1+b])
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
b := ReadVarInt(data[pos+1 : pos+1+l])
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
default:
panic(fmt.Sprintf("byte not supported: %q", char))
}
return slice, 0
}

View File

@ -44,6 +44,17 @@ func TestValueSlice(t *testing.T) {
} }
} }
func TestLargeData(t *testing.T) {
data := make([]byte, 100000)
enc := Encode(data)
value := NewValue(enc)
value.Decode()
if value.Len() != len(data) {
t.Error("Expected data to be", len(data), "got", value.Len())
}
}
func TestValue(t *testing.T) { func TestValue(t *testing.T) {
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01")) value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
if value.Get(0).Str() != "dog" { if value.Get(0).Str() != "dog" {

View File

@ -1,7 +1,6 @@
package ethutil package ethutil
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
@ -67,7 +66,7 @@ func (val *Value) Uint() uint64 {
} else if Val, ok := val.Val.(uint); ok { } else if Val, ok := val.Val.(uint); ok {
return uint64(Val) return uint64(Val)
} else if Val, ok := val.Val.([]byte); ok { } else if Val, ok := val.Val.([]byte); ok {
return ReadVarint(bytes.NewReader(Val)) return new(big.Int).SetBytes(Val).Uint64()
} else if Val, ok := val.Val.(*big.Int); ok { } else if Val, ok := val.Val.(*big.Int); ok {
return Val.Uint64() return Val.Uint64()
} }
@ -75,6 +74,30 @@ func (val *Value) Uint() uint64 {
return 0 return 0
} }
func (val *Value) Int() int64 {
if Val, ok := val.Val.(int8); ok {
return int64(Val)
} else if Val, ok := val.Val.(int16); ok {
return int64(Val)
} else if Val, ok := val.Val.(int32); ok {
return int64(Val)
} else if Val, ok := val.Val.(int64); ok {
return Val
} else if Val, ok := val.Val.(int); ok {
return int64(Val)
} else if Val, ok := val.Val.(float32); ok {
return int64(Val)
} else if Val, ok := val.Val.(float64); ok {
return int64(Val)
} else if Val, ok := val.Val.([]byte); ok {
return new(big.Int).SetBytes(Val).Int64()
} else if Val, ok := val.Val.(*big.Int); ok {
return Val.Int64()
}
return 0
}
func (val *Value) Byte() byte { func (val *Value) Byte() byte {
if Val, ok := val.Val.(byte); ok { if Val, ok := val.Val.(byte); ok {
return Val return Val
@ -123,6 +146,14 @@ func (val *Value) Bytes() []byte {
return []byte{} return []byte{}
} }
func (val *Value) Err() error {
if err, ok := val.Val.(error); ok {
return err
}
return nil
}
func (val *Value) Slice() []interface{} { func (val *Value) Slice() []interface{} {
if d, ok := val.Val.([]interface{}); ok { if d, ok := val.Val.([]interface{}); ok {
return d return d
@ -158,6 +189,11 @@ func (val *Value) IsStr() bool {
return val.Type() == reflect.String return val.Type() == reflect.String
} }
func (self *Value) IsErr() bool {
_, ok := self.Val.(error)
return ok
}
// Special list checking function. Something is considered // Special list checking function. Something is considered
// a list if it's of type []interface{}. The list is usually // a list if it's of type []interface{}. The list is usually
// used in conjunction with rlp decoded streams. // used in conjunction with rlp decoded streams.
@ -207,6 +243,13 @@ func (val *Value) Cmp(o *Value) bool {
return reflect.DeepEqual(val.Val, o.Val) return reflect.DeepEqual(val.Val, o.Val)
} }
func (self *Value) DeepCmp(o *Value) bool {
a := NewValue(self.BigInt())
b := NewValue(o.BigInt())
return a.Cmp(b)
}
func (val *Value) Encode() []byte { func (val *Value) Encode() []byte {
return Encode(val.Val) return Encode(val.Val)
} }
@ -215,12 +258,15 @@ func (val *Value) Encode() []byte {
func (self *Value) Decode() { func (self *Value) Decode() {
v, _ := Decode(self.Bytes(), 0) v, _ := Decode(self.Bytes(), 0)
self.Val = v self.Val = v
//self.Val = DecodeWithReader(bytes.NewBuffer(self.Bytes()))
} }
func NewValueFromBytes(data []byte) *Value { func NewValueFromBytes(data []byte) *Value {
if len(data) != 0 { if len(data) != 0 {
data, _ := Decode(data, 0) value := NewValue(data)
return NewValue(data) value.Decode()
return value
} }
return NewValue(nil) return NewValue(nil)
@ -262,6 +308,55 @@ func (val *Value) Append(v interface{}) *Value {
return val return val
} }
const (
valOpAdd = iota
valOpDiv
valOpMul
valOpPow
valOpSub
)
// Math stuff
func (self *Value) doOp(op int, other interface{}) *Value {
left := self.BigInt()
right := NewValue(other).BigInt()
switch op {
case valOpAdd:
self.Val = left.Add(left, right)
case valOpDiv:
self.Val = left.Div(left, right)
case valOpMul:
self.Val = left.Mul(left, right)
case valOpPow:
self.Val = left.Exp(left, right, Big0)
case valOpSub:
self.Val = left.Sub(left, right)
}
return self
}
func (self *Value) Add(other interface{}) *Value {
return self.doOp(valOpAdd, other)
}
func (self *Value) Sub(other interface{}) *Value {
return self.doOp(valOpSub, other)
}
func (self *Value) Div(other interface{}) *Value {
return self.doOp(valOpDiv, other)
}
func (self *Value) Mul(other interface{}) *Value {
return self.doOp(valOpMul, other)
}
func (self *Value) Pow(other interface{}) *Value {
return self.doOp(valOpPow, other)
}
type ValueIterator struct { type ValueIterator struct {
value *Value value *Value
currentValue *Value currentValue *Value

View File

@ -63,3 +63,18 @@ func TestIterator(t *testing.T) {
i++ i++
} }
} }
func TestMath(t *testing.T) {
a := NewValue(1)
a.Add(1).Add(1)
if !a.DeepCmp(NewValue(3)) {
t.Error("Expected 3, got", a)
}
a = NewValue(2)
a.Sub(1).Sub(1)
if !a.DeepCmp(NewValue(0)) {
t.Error("Expected 0, got", a)
}
}

View File

@ -3,9 +3,10 @@ package ethvm
// TODO Re write VM to use values instead of big integers? // TODO Re write VM to use values instead of big integers?
import ( import (
"math/big"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math/big"
) )
type ClosureRef interface { type ClosureRef interface {
@ -17,9 +18,10 @@ type ClosureRef interface {
// Basic inline closure object which implement the 'closure' interface // Basic inline closure object which implement the 'closure' interface
type Closure struct { type Closure struct {
caller ClosureRef caller ClosureRef
object *ethstate.StateObject object *ethstate.StateObject
Code []byte Code []byte
message *ethstate.Message
Gas, UsedGas, Price *big.Int Gas, UsedGas, Price *big.Int
@ -27,8 +29,8 @@ type Closure struct {
} }
// Create a new closure for the given data items // Create a new closure for the given data items
func NewClosure(caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure { func NewClosure(msg *ethstate.Message, caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure {
c := &Closure{caller: caller, object: object, Code: code, Args: nil} c := &Closure{message: msg, caller: caller, object: object, Code: code, Args: nil}
// Gas should be a pointer so it can safely be reduced through the run // Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition // This pointer will be off the state transition

View File

@ -59,7 +59,7 @@ func (st *Stack) Peek() *big.Int {
} }
func (st *Stack) Peekn() (*big.Int, *big.Int) { func (st *Stack) Peekn() (*big.Int, *big.Int) {
ints := st.data[:2] ints := st.data[len(st.data)-2:]
return ints[0], ints[1] return ints[0], ints[1]
} }

View File

@ -2,11 +2,12 @@ package ethvm
import ( import (
"fmt" "fmt"
"math"
"math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"math"
"math/big"
) )
type Debugger interface { type Debugger interface {
@ -51,6 +52,7 @@ type Environment interface {
Time() int64 Time() int64
Difficulty() *big.Int Difficulty() *big.Int
Value() *big.Int Value() *big.Int
BlockHash() []byte
} }
type Object interface { type Object interface {
@ -128,14 +130,14 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes()) fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
}) })
} }
b := pc.Bytes()
if len(b) == 0 {
b = []byte{0}
}
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
*/ */
b := pc.Bytes()
if len(b) == 0 {
b = []byte{0}
}
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
} }
gas := new(big.Int) gas := new(big.Int)
@ -244,6 +246,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Add(y, x) base.Add(y, x)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
@ -254,6 +258,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Sub(y, x) base.Sub(y, x)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
@ -264,6 +270,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Mul(y, x) base.Mul(y, x)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
@ -276,6 +284,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Div(y, x) base.Div(y, x)
} }
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
@ -288,6 +298,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Div(y, x) base.Div(y, x)
} }
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
@ -299,6 +311,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Mod(y, x) base.Mod(y, x)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
stack.Push(base) stack.Push(base)
case SMOD: case SMOD:
@ -309,6 +323,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Mod(y, x) base.Mod(y, x)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
stack.Push(base) stack.Push(base)
@ -320,6 +336,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
base.Exp(y, x, Pow256) base.Exp(y, x, Pow256)
ensure256(base)
self.Printf(" = %v", base) self.Printf(" = %v", base)
stack.Push(base) stack.Push(base)
@ -627,8 +645,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
val, loc := stack.Popn() val, loc := stack.Popn()
closure.SetStorage(loc, ethutil.NewValue(val)) closure.SetStorage(loc, ethutil.NewValue(val))
// Add the change to manifest closure.message.AddStorageChange(loc.Bytes())
self.env.State().Manifest().AddStorageChange(closure.Object(), loc.Bytes(), val)
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
case JUMP: case JUMP:
@ -679,27 +696,35 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" (*) %x", addr).Endl() self.Printf(" (*) %x", addr).Endl()
msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
To: addr, From: closure.Address(),
Origin: self.env.Origin(),
Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
Value: value,
})
// Create a new contract // Create a new contract
contract := self.env.State().NewStateObject(addr) contract := self.env.State().NewStateObject(addr)
if contract.Amount.Cmp(value) >= 0 { if contract.Balance.Cmp(value) >= 0 {
closure.object.SubAmount(value) closure.object.SubAmount(value)
contract.AddAmount(value) contract.AddAmount(value)
// Set the init script // Set the init script
initCode := mem.Get(offset.Int64(), size.Int64()) initCode := mem.Get(offset.Int64(), size.Int64())
//fmt.Printf("%x\n", initCode) msg.Input = initCode
// Transfer all remaining gas to the new // Transfer all remaining gas to the new
// contract so it may run the init script // contract so it may run the init script
gas := new(big.Int).Set(closure.Gas) gas := new(big.Int).Set(closure.Gas)
closure.UseGas(closure.Gas) closure.UseGas(closure.Gas)
// Create the closure // Create the closure
c := NewClosure(closure, contract, initCode, gas, closure.Price) c := NewClosure(msg, closure, contract, initCode, gas, closure.Price)
// Call the closure and set the return value as // Call the closure and set the return value as
// main script. // main script.
contract.Code, _, err = c.Call(self, nil) contract.Code, _, err = c.Call(self, nil)
} else { } else {
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
} }
if err != nil { if err != nil {
@ -711,7 +736,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf("CREATE err %v", err) self.Printf("CREATE err %v", err)
} else { } else {
stack.Push(ethutil.BigD(addr)) stack.Push(ethutil.BigD(addr))
self.Printf("CREATE success")
msg.Output = contract.Code
} }
self.Endl() self.Endl()
@ -735,8 +761,16 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Get the arguments from the memory // Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
if closure.object.Amount.Cmp(value) < 0 { msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) To: addr.Bytes(), From: closure.Address(),
Input: args,
Origin: self.env.Origin(),
Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
Value: value,
})
if closure.object.Balance.Cmp(value) < 0 {
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
closure.ReturnGas(gas, nil) closure.ReturnGas(gas, nil)
@ -750,7 +784,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
stateObject.AddAmount(value) stateObject.AddAmount(value)
// Create a new callable closure // Create a new callable closure
c := NewClosure(closure, stateObject, stateObject.Code, gas, closure.Price) c := NewClosure(msg, closure, stateObject, stateObject.Code, gas, closure.Price)
// Executer the closure and get the return value (if any) // Executer the closure and get the return value (if any)
ret, _, err := c.Call(self, args) ret, _, err := c.Call(self, args)
if err != nil { if err != nil {
@ -765,6 +799,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
mem.Set(retOffset.Int64(), retSize.Int64(), ret) mem.Set(retOffset.Int64(), retSize.Int64(), ret)
} }
msg.Output = ret
// Debug hook // Debug hook
if self.Dbg != nil { if self.Dbg != nil {
self.Dbg.SetCode(closure.Code) self.Dbg.SetCode(closure.Code)
@ -783,7 +819,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes()) receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes())
receiver.AddAmount(closure.object.Amount) receiver.AddAmount(closure.object.Balance)
closure.object.MarkForDeletion() closure.object.MarkForDeletion()
@ -837,3 +873,18 @@ func (self *Vm) Endl() *Vm {
return self return self
} }
func ensure256(x *big.Int) {
//max, _ := big.NewInt(0).SetString("115792089237316195423570985008687907853269984665640564039457584007913129639936", 0)
//if x.Cmp(max) >= 0 {
d := big.NewInt(1)
d.Lsh(d, 256).Sub(d, big.NewInt(1))
x.And(x, d)
//}
// Could have done this with an OR, but big ints are costly.
if x.Cmp(new(big.Int)) < 0 {
x.SetInt64(0)
}
}

View File

@ -2,14 +2,14 @@ package ethvm
import ( import (
"fmt" "fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
"log" "log"
"math/big" "math/big"
"os" "os"
"testing" "testing"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil"
) )
type TestEnv struct { type TestEnv struct {
@ -27,9 +27,7 @@ func (self TestEnv) State() *ethstate.State { return nil }
func TestVm(t *testing.T) { func TestVm(t *testing.T) {
ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4))) ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4)))
db, _ := ethdb.NewMemDatabase()
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db = db
stateObject := ethstate.NewStateObject([]byte{'j', 'e', 'f', 'f'}) stateObject := ethstate.NewStateObject([]byte{'j', 'e', 'f', 'f'})
callerClosure := NewClosure(stateObject, stateObject, []byte{0x60, 0x01}, big.NewInt(1000000), big.NewInt(0)) callerClosure := NewClosure(stateObject, stateObject, []byte{0x60, 0x01}, big.NewInt(1000000), big.NewInt(0))

View File

@ -6,9 +6,10 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil"
"net" "net"
"time" "time"
"github.com/ethereum/eth-go/ethutil"
) )
// Connection interface describing the methods required to implement the wire protocol. // Connection interface describing the methods required to implement the wire protocol.
@ -26,33 +27,41 @@ const (
// Values are given explicitly instead of by iota because these values are // Values are given explicitly instead of by iota because these values are
// defined by the wire protocol spec; it is easier for humans to ensure // defined by the wire protocol spec; it is easier for humans to ensure
// correctness when values are explicit. // correctness when values are explicit.
MsgHandshakeTy = 0x00 MsgHandshakeTy = 0x00
MsgDiscTy = 0x01 MsgDiscTy = 0x01
MsgPingTy = 0x02 MsgPingTy = 0x02
MsgPongTy = 0x03 MsgPongTy = 0x03
MsgGetPeersTy = 0x10 MsgGetPeersTy = 0x10
MsgPeersTy = 0x11 MsgPeersTy = 0x11
MsgTxTy = 0x12 MsgTxTy = 0x12
MsgBlockTy = 0x13 MsgGetChainTy = 0x14
MsgGetChainTy = 0x14 MsgNotInChainTy = 0x15
MsgNotInChainTy = 0x15 MsgGetTxsTy = 0x16
MsgGetTxsTy = 0x16 MsgGetBlockHashesTy = 0x17
MsgBlockHashesTy = 0x18
MsgGetBlocksTy = 0x19
MsgBlockTy = 0x13
MsgOldBlockTy = 0xbb
MsgTalkTy = 0xff MsgTalkTy = 0xff
) )
var msgTypeToString = map[MsgType]string{ var msgTypeToString = map[MsgType]string{
MsgHandshakeTy: "Handshake", MsgHandshakeTy: "Handshake",
MsgDiscTy: "Disconnect", MsgDiscTy: "Disconnect",
MsgPingTy: "Ping", MsgPingTy: "Ping",
MsgPongTy: "Pong", MsgPongTy: "Pong",
MsgGetPeersTy: "Get peers", MsgGetPeersTy: "Get peers",
MsgPeersTy: "Peers", MsgPeersTy: "Peers",
MsgTxTy: "Transactions", MsgTxTy: "Transactions",
MsgBlockTy: "Blocks", MsgBlockTy: "Blocks",
MsgGetChainTy: "Get chain", MsgGetChainTy: "Get chain",
MsgGetTxsTy: "Get Txs", MsgGetTxsTy: "Get Txs",
MsgNotInChainTy: "Not in chain", MsgNotInChainTy: "Not in chain",
MsgGetBlockHashesTy: "Get block hashes",
MsgBlockHashesTy: "Block hashes",
MsgGetBlocksTy: "Get blocks",
} }
func (mt MsgType) String() string { func (mt MsgType) String() string {
@ -109,7 +118,7 @@ func (self *Connection) Write(typ MsgType, v ...interface{}) error {
slice := [][]interface{}{[]interface{}{byte(typ)}} slice := [][]interface{}{[]interface{}{byte(typ)}}
for _, value := range v { for _, value := range v {
if encodable, ok := value.(ethutil.RlpEncodable); ok { if encodable, ok := value.(ethutil.RlpEncodeDecode); ok {
slice = append(slice, encodable.RlpValue()) slice = append(slice, encodable.RlpValue())
} else if raw, ok := value.([]interface{}); ok { } else if raw, ok := value.([]interface{}); ok {
slice = append(slice, raw) slice = append(slice, raw)

326
peer.go
View File

@ -4,15 +4,18 @@ import (
"bytes" "bytes"
"container/list" "container/list"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethchain" "math"
"github.com/ethereum/eth-go/ethlog" "math/big"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
) )
var peerlogger = ethlog.NewLogger("PEER") var peerlogger = ethlog.NewLogger("PEER")
@ -21,7 +24,7 @@ const (
// The size of the output buffer for writing messages // The size of the output buffer for writing messages
outputBufferSize = 50 outputBufferSize = 50
// Current protocol version // Current protocol version
ProtocolVersion = 23 ProtocolVersion = 27
// Interval for ping/pong message // Interval for ping/pong message
pingPongTimer = 2 * time.Second pingPongTimer = 2 * time.Second
) )
@ -124,9 +127,13 @@ type Peer struct {
lastPong int64 lastPong int64
lastBlockReceived time.Time lastBlockReceived time.Time
host []byte host []byte
port uint16 port uint16
caps Caps caps Caps
td *big.Int
bestHash []byte
lastReceivedHash []byte
requestedHashes [][]byte
// This peer's public key // This peer's public key
pubkey []byte pubkey []byte
@ -197,10 +204,12 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
} }
func (self *Peer) Connect(addr string) (conn net.Conn, err error) { func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
for attempts := 0; attempts < 5; attempts++ { const maxTries = 3
for attempts := 0; attempts < maxTries; attempts++ {
conn, err = net.DialTimeout("tcp", addr, 10*time.Second) conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil { if err != nil {
peerlogger.Debugf("Peer connection failed. Retrying (%d/5)\n", attempts+1) //peerlogger.Debugf("Peer connection failed. Retrying (%d/%d) (%s)\n", attempts+1, maxTries, addr)
time.Sleep(time.Duration(attempts*20) * time.Second)
continue continue
} }
@ -291,12 +300,14 @@ out:
// Ping timer // Ping timer
case <-pingTimer.C: case <-pingTimer.C:
timeSince := time.Since(time.Unix(p.lastPong, 0)) /*
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) { timeSince := time.Since(time.Unix(p.lastPong, 0))
peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince) if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
p.Stop() peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
return p.Stop()
} return
}
*/
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, "")) p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
p.pingStartTime = time.Now() p.pingStartTime = time.Now()
@ -340,7 +351,6 @@ func (p *Peer) HandleInbound() {
for _, msg := range msgs { for _, msg := range msgs {
peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data) peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data)
nextMsg:
switch msg.Type { switch msg.Type {
case ethwire.MsgHandshakeTy: case ethwire.MsgHandshakeTy:
// Version message // Version message
@ -349,9 +359,10 @@ func (p *Peer) HandleInbound() {
if p.caps.IsCap(CapPeerDiscTy) { if p.caps.IsCap(CapPeerDiscTy) {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, "")) p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
} }
case ethwire.MsgDiscTy: case ethwire.MsgDiscTy:
p.Stop() p.Stop()
peerlogger.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint())) peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
case ethwire.MsgPingTy: case ethwire.MsgPingTy:
// Respond back with pong // Respond back with pong
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, "")) p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
@ -360,111 +371,7 @@ func (p *Peer) HandleInbound() {
// last pong so the peer handler knows this peer is still // last pong so the peer handler knows this peer is still
// active. // active.
p.lastPong = time.Now().Unix() p.lastPong = time.Now().Unix()
p.pingTime = time.Now().Sub(p.pingStartTime) p.pingTime = time.Since(p.pingStartTime)
case ethwire.MsgBlockTy:
// Get all blocks and process them
var block, lastBlock *ethchain.Block
var err error
// Make sure we are actually receiving anything
if msg.Data.Len()-1 > 1 && p.diverted {
// We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find
// common ground to start syncing from
lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1))
if p.lastRequestedBlock != nil && bytes.Compare(lastBlock.Hash(), p.lastRequestedBlock.Hash()) == 0 {
p.catchingUp = false
continue
}
p.lastRequestedBlock = lastBlock
peerlogger.Infof("Last block: %x. Checking if we have it locally.\n", lastBlock.Hash())
for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
// Do we have this block on our chain? If so we can continue
if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) {
// We don't have this block, but we do have a block with the same prevHash, diversion time!
if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) {
p.diverted = false
if !p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
p.SyncWithPeerToLastKnown()
break nextMsg
}
break
}
}
}
if !p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) {
// If we can't find a common ancenstor we need to request more blocks.
// FIXME: At one point this won't scale anymore since we are not asking for an offset
// we just keep increasing the amount of blocks.
p.blocksRequested = p.blocksRequested * 2
peerlogger.Infof("No common ancestor found, requesting %d more blocks.\n", p.blocksRequested)
p.FindCommonParentBlock()
break nextMsg
}
p.catchingUp = false
}
for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
err = p.ethereum.StateManager().Process(block, false)
if err != nil {
if ethutil.Config.Debug {
peerlogger.Infof("Block %x failed\n", block.Hash())
peerlogger.Infof("%v\n", err)
peerlogger.Debugln(block)
}
break
} else {
lastBlock = block
}
p.lastBlockReceived = time.Now()
}
if msg.Data.Len() <= 1 {
// Set catching up to false if
// the peer has nothing left to give
p.catchingUp = false
}
if err != nil {
// If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) {
/*
b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
peerlogger.Infof("Attempting to catch (%x). Parent known\n", b.Hash())
p.catchingUp = false
p.CatchupWithPeer(b.Hash())
peerlogger.Infoln(b)
*/
peerlogger.Infoln("Attempting to catch. Parent known")
p.catchingUp = false
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} else if ethchain.IsValidationErr(err) {
fmt.Println("Err:", err)
p.catchingUp = false
}
} else {
// If we're catching up, try to catch up further.
if p.catchingUp && msg.Data.Len() > 1 {
if lastBlock != nil {
blockInfo := lastBlock.BlockInfo()
peerlogger.DebugDetailf("Synced chain to #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
}
p.catchingUp = false
hash := p.ethereum.BlockChain().CurrentBlock.Hash()
p.CatchupWithPeer(hash)
}
}
case ethwire.MsgTxTy: case ethwire.MsgTxTy:
// If the message was a transaction queue the transaction // If the message was a transaction queue the transaction
// in the TxPool where it will undergo validation and // in the TxPool where it will undergo validation and
@ -489,58 +396,6 @@ func (p *Peer) HandleInbound() {
// Connect to the list of peers // Connect to the list of peers
p.ethereum.ProcessPeerList(peers) p.ethereum.ProcessPeerList(peers)
case ethwire.MsgGetChainTy:
var parent *ethchain.Block
// Length minus one since the very last element in the array is a count
l := msg.Data.Len() - 1
// Ignore empty get chains
if l == 0 {
break
}
// Amount of parents in the canonical chain
//amountOfBlocks := msg.Data.Get(l).AsUint()
amountOfBlocks := uint64(100)
// Check each SHA block hash from the message and determine whether
// the SHA is in the database
for i := 0; i < l; i++ {
if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
parent = p.ethereum.BlockChain().GetBlock(data)
break
}
}
// If a parent is found send back a reply
if parent != nil {
peerlogger.DebugDetailf("Found canonical block, returning chain from: %x ", parent.Hash())
chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
if len(chain) > 0 {
//peerlogger.Debugf("Returning %d blocks: %x ", len(chain), parent.Hash())
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
} else {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, []interface{}{}))
}
} else {
//peerlogger.Debugf("Could not find a similar block")
// If no blocks are found we send back a reply with msg not in chain
// and the last hash from get chain
if l > 0 {
lastHash := msg.Data.Get(l - 1)
//log.Printf("Sending not in chain with hash %x\n", lastHash.AsRaw())
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
}
}
case ethwire.MsgNotInChainTy:
peerlogger.DebugDetailf("Not in chain: %x\n", msg.Data.Get(0).Bytes())
if p.diverted == true {
// If were already looking for a common parent and we get here again we need to go deeper
p.blocksRequested = p.blocksRequested * 2
}
p.diverted = true
p.catchingUp = false
p.FindCommonParentBlock()
case ethwire.MsgGetTxsTy: case ethwire.MsgGetTxsTy:
// Get the current transactions of the pool // Get the current transactions of the pool
txs := p.ethereum.TxPool().CurrentTransactions() txs := p.ethereum.TxPool().CurrentTransactions()
@ -552,15 +407,113 @@ func (p *Peer) HandleInbound() {
// Broadcast it back to the peer // Broadcast it back to the peer
p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface)) p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
// Unofficial but fun nonetheless case ethwire.MsgGetBlockHashesTy:
case ethwire.MsgTalkTy: if msg.Data.Len() < 2 {
peerlogger.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str()) peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
}
hash := msg.Data.Get(0).Bytes()
amount := msg.Data.Get(1).Uint()
hashes := p.ethereum.BlockChain().GetChainHashesFromHash(hash, amount)
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
case ethwire.MsgGetBlocksTy:
// Limit to max 300 blocks
max := int(math.Min(float64(msg.Data.Len()), 300.0))
var blocks []interface{}
for i := 0; i < max; i++ {
hash := msg.Data.Get(i).Bytes()
block := p.ethereum.BlockChain().GetBlock(hash)
if block != nil {
blocks = append(blocks, block.Value().Raw())
}
}
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, blocks))
case ethwire.MsgBlockHashesTy:
p.catchingUp = true
blockPool := p.ethereum.blockPool
foundCommonHash := false
it := msg.Data.NewIterator()
for it.Next() {
hash := it.Value().Bytes()
if blockPool.HasCommonHash(hash) {
foundCommonHash = true
break
}
blockPool.AddHash(hash)
p.lastReceivedHash = hash
p.lastBlockReceived = time.Now()
}
if foundCommonHash {
p.FetchBlocks()
} else {
p.FetchHashes()
}
case ethwire.MsgBlockTy:
p.catchingUp = true
blockPool := p.ethereum.blockPool
it := msg.Data.NewIterator()
for it.Next() {
block := ethchain.NewBlockFromRlpValue(it.Value())
blockPool.SetBlock(block)
p.lastBlockReceived = time.Now()
}
linked := blockPool.CheckLinkAndProcess(func(block *ethchain.Block) {
p.ethereum.StateManager().Process(block, false)
})
if !linked {
p.FetchBlocks()
}
} }
} }
} }
p.Stop() p.Stop()
} }
func (self *Peer) FetchBlocks() {
blockPool := self.ethereum.blockPool
hashes := blockPool.Take(100, self)
if len(hashes) > 0 {
self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
}
}
func (self *Peer) FetchHashes() {
blockPool := self.ethereum.blockPool
if self.td.Cmp(blockPool.td) >= 0 {
peerlogger.Debugf("Requesting hashes from %x\n", self.lastReceivedHash)
if !blockPool.HasLatestHash() {
self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{self.lastReceivedHash, uint32(200)}))
}
}
}
// General update method // General update method
func (self *Peer) update() { func (self *Peer) update() {
serviceTimer := time.NewTicker(5 * time.Second) serviceTimer := time.NewTicker(5 * time.Second)
@ -631,6 +584,7 @@ func (p *Peer) pushHandshake() error {
pubkey := p.ethereum.KeyManager().PublicKey() pubkey := p.ethereum.KeyManager().PublicKey()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:], uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:],
p.ethereum.BlockChain().TD.Uint64(), p.ethereum.BlockChain().CurrentBlock.Hash(),
}) })
p.QueueMessage(msg) p.QueueMessage(msg)
@ -716,10 +670,15 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
p.SetVersion(c.Get(2).Str()) p.SetVersion(c.Get(2).Str())
} }
// Get the td and last hash
p.td = c.Get(6).BigInt()
p.bestHash = c.Get(7).Bytes()
p.lastReceivedHash = p.bestHash
p.ethereum.PushPeer(p) p.ethereum.PushPeer(p)
p.ethereum.reactor.Post("peerList", p.ethereum.Peers()) p.ethereum.reactor.Post("peerList", p.ethereum.Peers())
ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers) ethlogger.Infof("Added peer (%s) %d / %d (TD = %v ~ %x)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, p.td, p.bestHash)
/* /*
// Catch up with the connected peer // Catch up with the connected peer
@ -728,7 +687,12 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
} }
*/ */
p.SyncWithPeerToLastKnown() //p.SyncWithPeerToLastKnown()
if p.td.Cmp(p.ethereum.BlockChain().TD) > 0 {
p.ethereum.blockPool.AddHash(p.lastReceivedHash)
p.FetchHashes()
}
peerlogger.Debugln(p) peerlogger.Debugln(p)
} }
@ -782,7 +746,7 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) {
if !p.catchingUp { if !p.catchingUp {
// Make sure nobody else is catching up when you want to do this // Make sure nobody else is catching up when you want to do this
p.catchingUp = true p.catchingUp = true
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(30)}) msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)})
p.QueueMessage(msg) p.QueueMessage(msg)
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr()) peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())