diff --git a/LICENSE b/LICENSE index b77f7909a..30f629158 100644 --- a/LICENSE +++ b/LICENSE @@ -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 -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -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. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301 USA diff --git a/README.md b/README.md index 5b0f6e07f..3ed340612 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Ethereum Ethereum Go Development package (C) Jeffrey Wilcke 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 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) 4. [ethdb](https://github.com/ethereum/eth-go/tree/master/ethdb) 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 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 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. Commits that are directly based on master are simply ignored. diff --git a/block_pool.go b/block_pool.go new file mode 100644 index 000000000..3225bdff2 --- /dev/null +++ b/block_pool.go @@ -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 +} diff --git a/ethchain/block.go b/ethchain/block.go index 437525e35..5765abd51 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -3,13 +3,14 @@ package ethchain import ( "bytes" "fmt" + "math/big" + _ "strconv" + "time" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethutil" - "math/big" - _ "strconv" - "time" ) type BlockInfo struct { @@ -63,12 +64,6 @@ type Block struct { TxSha []byte } -// New block takes a raw encoded string -// XXX DEPRICATED -func NewBlockFromData(raw []byte) *Block { - return NewBlockFromBytes(raw) -} - func NewBlockFromBytes(raw []byte) *Block { block := &Block{} block.RlpDecode(raw) @@ -105,7 +100,7 @@ func CreateBlock(root interface{}, } 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 } @@ -130,40 +125,15 @@ func (block *Block) Transactions() []*Transaction { 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 { if block.Number.Cmp(big.NewInt(0)) == 0 { return ethutil.BigPow(10, 6) } - previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit) - current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5)) + // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024 + + previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit) + current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5)) curInt := new(big.Int).Div(current.Num(), current.Denom()) result := new(big.Int).Add(previous, curInt) @@ -172,19 +142,6 @@ func (block *Block) CalcGasLimit(parent *Block) *big.Int { min := big.NewInt(125000) 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 { @@ -252,26 +209,10 @@ func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) { func (block *Block) setTransactions(txs []*Transaction) { 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) { - trie := ethtrie.NewTrie(ethutil.Config.Db, "") + trie := ethtrie.New(ethutil.Config.Db, "") for i, receipt := range receipts { 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.UncleSha = header.Get(1).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.Difficulty = header.Get(5).BigInt() block.Number = header.Get(6).BigInt() @@ -355,7 +296,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block { block.PrevHash = header.Get(0).Bytes() block.UncleSha = header.Get(1).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.Difficulty = header.Get(5).BigInt() block.Number = header.Get(6).BigInt() diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 1a2662787..3445bbb87 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -2,11 +2,12 @@ package ethchain import ( "bytes" + "math" + "math/big" + "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "math" - "math/big" ) 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 for _, block := range blocks { 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 } 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) { i++ 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 } anOtherBlock := bc.GetBlock(block.PrevHash) if anOtherBlock == nil { // 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 } curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) @@ -158,11 +159,11 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte chainlogger.Infoln("Current chain difficulty:", curChainDifficulty) 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) return false } 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 } } @@ -207,6 +208,26 @@ func (bc *BlockChain) GenesisBlock() *Block { 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 func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { var chain []interface{} @@ -280,7 +301,7 @@ func AddTestNetFunds(block *Block) { } { codedAddr := ethutil.Hex2Bytes(addr) 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) } } @@ -289,7 +310,6 @@ func (bc *BlockChain) setLastBlock() { data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) if len(data) != 0 { block := NewBlockFromBytes(data) - //info := bc.BlockInfo(block) bc.CurrentBlock = block bc.LastBlockHash = block.Hash() bc.LastBlockNumber = block.Number.Uint64() @@ -300,9 +320,8 @@ func (bc *BlockChain) setLastBlock() { bc.genesisBlock.state.Trie.Sync() // Prepare the genesis block bc.Add(bc.genesisBlock) - - //chainlogger.Infof("root %x\n", bm.bc.genesisBlock.State().Root) - //bm.bc.genesisBlock.PrintHash() + fk := append([]byte("bloom"), bc.genesisBlock.Hash()...) + bc.Ethereum.Db().Put(fk, make([]byte, 255)) } // 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) } +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 { bi := BlockInfo{} data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...)) diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go index bbc96c823..1edcf9c7b 100644 --- a/ethchain/block_chain_test.go +++ b/ethchain/block_chain_test.go @@ -3,16 +3,19 @@ package ethchain import ( "container/list" "fmt" + "testing" + + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "testing" ) // Implement our EthTest Manager type TestManager struct { stateManager *StateManager - reactor *ethutil.ReactorEngine + reactor *ethreact.ReactorEngine txPool *TxPool blockChain *BlockChain @@ -47,16 +50,24 @@ func (tm *TestManager) StateManager() *StateManager { return tm.stateManager } -func (tm *TestManager) Reactor() *ethutil.ReactorEngine { +func (tm *TestManager) Reactor() *ethreact.ReactorEngine { return tm.reactor } func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) { 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() if err != nil { @@ -66,7 +77,7 @@ func NewTestManager() *TestManager { ethutil.Config.Db = db testManager := &TestManager{} - testManager.reactor = ethutil.NewReactorEngine() + testManager.reactor = ethreact.New() testManager.txPool = NewTxPool(testManager) testManager.blockChain = NewBlockChain(testManager) diff --git a/ethchain/bloom.go b/ethchain/bloom.go new file mode 100644 index 000000000..5317ca0b1 --- /dev/null +++ b/ethchain/bloom.go @@ -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 +} diff --git a/ethchain/bloom_test.go b/ethchain/bloom_test.go new file mode 100644 index 000000000..ea53d539c --- /dev/null +++ b/ethchain/bloom_test.go @@ -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") + } +} diff --git a/ethchain/dagger.go b/ethchain/dagger.go index dccd2ff5b..917b3d722 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -3,6 +3,7 @@ package ethchain import ( "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/sha3" "hash" @@ -14,7 +15,7 @@ import ( var powlogger = ethlog.NewLogger("POW") 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 GetHashrate() int64 } @@ -28,7 +29,7 @@ func (pow *EasyPow) GetHashrate() int64 { 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())) hash := block.HashNoNonce() diff := block.Difficulty diff --git a/ethchain/filter.go b/ethchain/filter.go new file mode 100644 index 000000000..5ed9af977 --- /dev/null +++ b/ethchain/filter.go @@ -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 +} diff --git a/ethchain/filter_test.go b/ethchain/filter_test.go new file mode 100644 index 000000000..6dce51b3b --- /dev/null +++ b/ethchain/filter_test.go @@ -0,0 +1,7 @@ +package ethchain + +import "testing" + +func TestFilter(t *testing.T) { + filter := NewFilter() +} diff --git a/ethchain/genesis.go b/ethchain/genesis.go index 54a3bc766..0ce53a6ee 100644 --- a/ethchain/genesis.go +++ b/ethchain/genesis.go @@ -1,9 +1,10 @@ package ethchain import ( + "math/big" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethutil" - "math/big" ) /* @@ -26,7 +27,8 @@ var GenesisHeader = []interface{}{ // tx sha "", // Difficulty - ethutil.BigPow(2, 22), + //ethutil.BigPow(2, 22), + big.NewInt(4096), // Number ethutil.Big0, // Block minimum gas price diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 9408cf331..08bd22d29 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -4,14 +4,16 @@ import ( "bytes" "container/list" "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" "sync" "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") @@ -36,13 +38,14 @@ type EthManager interface { BlockChain() *BlockChain TxPool() *TxPool Broadcast(msgType ethwire.MsgType, data []interface{}) - Reactor() *ethutil.ReactorEngine + Reactor() *ethreact.ReactorEngine PeerCount() int IsMining() bool IsListening() bool Peers() *list.List KeyManager() *ethcrypto.KeyManager ClientIdentity() ethwire.ClientIdentity + Db() ethutil.Database } type StateManager struct { @@ -233,7 +236,12 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { // Add the block to the chain 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()) if dontReact == false { @@ -361,14 +369,56 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) notifyChanges(state *ethstate.State) { - for addr, stateObject := range state.Manifest().ObjectChanges { - sm.Ethereum.Reactor().Post("object:"+addr, stateObject) +// Manifest will handle both creating notifications and generating bloom bin data +func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter { + 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 { - for addr, value := range mappedObjects { - sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, ðstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value}) + sm.Ethereum.Reactor().Post("messages", state.Manifest().Messages) + + /* + 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, ðstate.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 } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 266328ce8..9fbc160a5 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -2,11 +2,12 @@ package ethchain import ( "fmt" + "math/big" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethvm" - "math/big" ) /* @@ -94,8 +95,8 @@ func (self *StateTransition) BuyGas() error { var err error sender := self.Sender() - if sender.Amount.Cmp(self.tx.GasValue()) < 0 { - return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount) + if sender.Balance.Cmp(self.tx.GasValue()) < 0 { + return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance) } coinbase := self.Coinbase() @@ -178,8 +179,8 @@ func (self *StateTransition) TransitionState() (err error) { return } - if sender.Amount.Cmp(self.value) < 0 { - return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) + if sender.Balance.Cmp(self.value) < 0 { + return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance) } var snapshot *ethstate.State @@ -210,6 +211,14 @@ func (self *StateTransition) TransitionState() (err error) { snapshot = self.state.Copy() } + msg := self.state.Manifest().AddMessage(ðstate.Message{ + To: receiver.Address(), From: sender.Address(), + Input: self.tx.Data, + Origin: sender.Address(), + Block: self.block.Hash(), Timestamp: self.block.Time, Coinbase: self.block.Coinbase, Number: self.block.Number, + Value: self.value, + }) + // Process the init code and create 'valid' contract if IsContractAddr(self.receiver) { // Evaluate the initialization script @@ -217,7 +226,7 @@ func (self *StateTransition) TransitionState() (err error) { // script section for the state object. self.data = nil - code, err := self.Eval(receiver.Init(), receiver, "init") + code, err := self.Eval(msg, receiver.Init(), receiver, "init") if err != nil { self.state.Set(snapshot) @@ -225,14 +234,17 @@ func (self *StateTransition) TransitionState() (err error) { } receiver.Code = code + msg.Output = code } else { if len(receiver.Code) > 0 { - _, err = self.Eval(receiver.Code, receiver, "code") + ret, err := self.Eval(msg, receiver.Code, receiver, "code") if err != nil { self.state.Set(snapshot) 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 { - if sender.Amount.Cmp(self.value) < 0 { - return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) + if sender.Balance.Cmp(self.value) < 0 { + return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance) } // Subtract the amount from the senders account @@ -252,12 +264,12 @@ func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObjec 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 ( transactor = self.Sender() state = self.state 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) @@ -277,7 +289,7 @@ func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject contract := state.NewStateObject(addr) contract.InitCode = tx.Data - contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) + contract.State = ethstate.New(ethtrie.New(ethutil.Config.Db, "")) return contract } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 5686a7edb..e1b48a3d3 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -3,10 +3,11 @@ package ethchain import ( "bytes" "fmt" + "math/big" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethutil" "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} @@ -130,7 +131,7 @@ func (tx *Transaction) RlpData() interface{} { // 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 { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 21c6ea3de..b0d62fd91 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -4,11 +4,12 @@ import ( "bytes" "container/list" "fmt" + "math/big" + "sync" + "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethwire" - "math/big" - "sync" ) var txplogger = ethlog.NewLogger("TXP") @@ -91,78 +92,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) { 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 { // Get the last block so we can retrieve the sender and receiver from // the merkle trie @@ -183,7 +112,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { totAmount := new(big.Int).Set(tx.Value) // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. - if sender.Amount.Cmp(totAmount) < 0 { + if sender.Balance.Cmp(totAmount) < 0 { return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } diff --git a/ethchain/vm_env.go b/ethchain/vm_env.go index ddead77fd..30f9497fa 100644 --- a/ethchain/vm_env.go +++ b/ethchain/vm_env.go @@ -1,8 +1,9 @@ package ethchain import ( - "github.com/ethereum/eth-go/ethstate" "math/big" + + "github.com/ethereum/eth-go/ethstate" ) 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) Time() int64 { return self.block.Time } func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } +func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) Value() *big.Int { return self.tx.Value } func (self *VMEnv) State() *ethstate.State { return self.state } diff --git a/ethcrypto/mnemonic.go b/ethcrypto/mnemonic.go index b8df2ad6f..5fb620219 100644 --- a/ethcrypto/mnemonic.go +++ b/ethcrypto/mnemonic.go @@ -2,40 +2,9 @@ package ethcrypto import ( "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" "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 func IndexOf(slice []string, value string) int64 { for p, v := range slice { @@ -48,7 +17,7 @@ func IndexOf(slice []string, value string) int64 { func MnemonicEncode(message string) []string { var out []string - n := int64(len(words)) + n := int64(len(MnemonicWords)) for i := 0; i < len(message); i += (len(message) / 8) { x := message[i : i+8] @@ -56,22 +25,22 @@ func MnemonicEncode(message string) []string { w1 := (bit % n) w2 := ((bit / n) + w1) % 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 } func MnemonicDecode(wordsar []string) string { var out string - n := int64(len(words)) + n := int64(len(MnemonicWords)) for i := 0; i < len(wordsar); i += 3 { word1 := wordsar[i] word2 := wordsar[i+1] word3 := wordsar[i+2] - w1 := IndexOf(words, word1) - w2 := IndexOf(words, word2) - w3 := IndexOf(words, word3) + w1 := IndexOf(MnemonicWords, word1) + w2 := IndexOf(MnemonicWords, word2) + w3 := IndexOf(MnemonicWords, word3) y := (w2 - w1) % n z := (w3 - w2) % n diff --git a/ethcrypto/mnemonic.words.lst b/ethcrypto/mnemonic.words.lst deleted file mode 100644 index 6bf412ce1..000000000 --- a/ethcrypto/mnemonic.words.lst +++ /dev/null @@ -1,1626 +0,0 @@ -like -just -love -know -never -want -time -out -there -make -look -eye -down -only -think -heart -back -then -into -about -more -away -still -them -take -thing -even -through -long -always -world -too -friend -tell -try -hand -thought -over -here -other -need -smile -again -much -cry -been -night -ever -little -said -end -some -those -around -mind -people -girl -leave -dream -left -turn -myself -give -nothing -really -off -before -something -find -walk -wish -good -once -place -ask -stop -keep -watch -seem -everything -wait -got -yet -made -remember -start -alone -run -hope -maybe -believe -body -hate -after -close -talk -stand -own -each -hurt -help -home -god -soul -new -many -two -inside -should -true -first -fear -mean -better -play -another -gone -change -use -wonder -someone -hair -cold -open -best -any -behind -happen -water -dark -laugh -stay -forever -name -work -show -sky -break -came -deep -door -put -black -together -upon -happy -such -great -white -matter -fill -past -please -burn -cause -enough -touch -moment -soon -voice -scream -anything -stare -sound -red -everyone -hide -kiss -truth -death -beautiful -mine -blood -broken -very -pass -next -forget -tree -wrong -air -mother -understand -lip -hit -wall -memory -sleep -free -high -realize -school -might -skin -sweet -perfect -blue -kill -breath -dance -against -fly -between -grow -strong -under -listen -bring -sometimes -speak -pull -person -become -family -begin -ground -real -small -father -sure -feet -rest -young -finally -land -across -today -different -guy -line -fire -reason -reach -second -slowly -write -eat -smell -mouth -step -learn -three -floor -promise -breathe -darkness -push -earth -guess -save -song -above -along -both -color -house -almost -sorry -anymore -brother -okay -dear -game -fade -already -apart -warm -beauty -heard -notice -question -shine -began -piece -whole -shadow -secret -street -within -finger -point -morning -whisper -child -moon -green -story -glass -kid -silence -since -soft -yourself -empty -shall -angel -answer -baby -bright -dad -path -worry -hour -drop -follow -power -war -half -flow -heaven -act -chance -fact -least -tired -children -near -quite -afraid -rise -sea -taste -window -cover -nice -trust -lot -sad -cool -force -peace -return -blind -easy -ready -roll -rose -drive -held -music -beneath -hang -mom -paint -emotion -quiet -clear -cloud -few -pretty -bird -outside -paper -picture -front -rock -simple -anyone -meant -reality -road -sense -waste -bit -leaf -thank -happiness -meet -men -smoke -truly -decide -self -age -book -form -alive -carry -escape -damn -instead -able -ice -minute -throw -catch -leg -ring -course -goodbye -lead -poem -sick -corner -desire -known -problem -remind -shoulder -suppose -toward -wave -drink -jump -woman -pretend -sister -week -human -joy -crack -grey -pray -surprise -dry -knee -less -search -bleed -caught -clean -embrace -future -king -son -sorrow -chest -hug -remain -sat -worth -blow -daddy -final -parent -tight -also -create -lonely -safe -cross -dress -evil -silent -bone -fate -perhaps -anger -class -scar -snow -tiny -tonight -continue -control -dog -edge -mirror -month -suddenly -comfort -given -loud -quickly -gaze -plan -rush -stone -town -battle -ignore -spirit -stood -stupid -yours -brown -build -dust -hey -kept -pay -phone -twist -although -ball -beyond -hidden -nose -taken -fail -float -pure -somehow -wash -wrap -angry -cheek -creature -forgotten -heat -rip -single -space -special -weak -whatever -yell -anyway -blame -job -choose -country -curse -drift -echo -figure -grew -laughter -neck -suffer -worse -yeah -disappear -foot -forward -knife -mess -somewhere -stomach -storm -beg -idea -lift -offer -breeze -field -five -often -simply -stuck -win -allow -confuse -enjoy -except -flower -seek -strength -calm -grin -gun -heavy -hill -large -ocean -shoe -sigh -straight -summer -tongue -accept -crazy -everyday -exist -grass -mistake -sent -shut -surround -table -ache -brain -destroy -heal -nature -shout -sign -stain -choice -doubt -glance -glow -mountain -queen -stranger -throat -tomorrow -city -either -fish -flame -rather -shape -spin -spread -ash -distance -finish -image -imagine -important -nobody -shatter -warmth -became -feed -flesh -funny -lust -shirt -trouble -yellow -attention -bare -bite -money -protect -amaze -appear -born -choke -completely -daughter -fresh -friendship -gentle -probably -six -deserve -expect -grab -middle -nightmare -river -thousand -weight -worst -wound -barely -bottle -cream -regret -relationship -stick -test -crush -endless -fault -itself -rule -spill -art -circle -join -kick -mask -master -passion -quick -raise -smooth -unless -wander -actually -broke -chair -deal -favorite -gift -note -number -sweat -box -chill -clothes -lady -mark -park -poor -sadness -tie -animal -belong -brush -consume -dawn -forest -innocent -pen -pride -stream -thick -clay -complete -count -draw -faith -press -silver -struggle -surface -taught -teach -wet -bless -chase -climb -enter -letter -melt -metal -movie -stretch -swing -vision -wife -beside -crash -forgot -guide -haunt -joke -knock -plant -pour -prove -reveal -steal -stuff -trip -wood -wrist -bother -bottom -crawl -crowd -fix -forgive -frown -grace -loose -lucky -party -release -surely -survive -teacher -gently -grip -speed -suicide -travel -treat -vein -written -cage -chain -conversation -date -enemy -however -interest -million -page -pink -proud -sway -themselves -winter -church -cruel -cup -demon -experience -freedom -pair -pop -purpose -respect -shoot -softly -state -strange -bar -birth -curl -dirt -excuse -lord -lovely -monster -order -pack -pants -pool -scene -seven -shame -slide -ugly -among -blade -blonde -closet -creek -deny -drug -eternity -gain -grade -handle -key -linger -pale -prepare -swallow -swim -tremble -wheel -won -cast -cigarette -claim -college -direction -dirty -gather -ghost -hundred -loss -lung -orange -present -swear -swirl -twice -wild -bitter -blanket -doctor -everywhere -flash -grown -knowledge -numb -pressure -radio -repeat -ruin -spend -unknown -buy -clock -devil -early -false -fantasy -pound -precious -refuse -sheet -teeth -welcome -add -ahead -block -bury -caress -content -depth -despite -distant -marry -purple -threw -whenever -bomb -dull -easily -grasp -hospital -innocence -normal -receive -reply -rhyme -shade -someday -sword -toe -visit -asleep -bought -center -consider -flat -hero -history -ink -insane -muscle -mystery -pocket -reflection -shove -silently -smart -soldier -spot -stress -train -type -view -whether -bus -energy -explain -holy -hunger -inch -magic -mix -noise -nowhere -prayer -presence -shock -snap -spider -study -thunder -trail -admit -agree -bag -bang -bound -butterfly -cute -exactly -explode -familiar -fold -further -pierce -reflect -scent -selfish -sharp -sink -spring -stumble -universe -weep -women -wonderful -action -ancient -attempt -avoid -birthday -branch -chocolate -core -depress -drunk -especially -focus -fruit -honest -match -palm -perfectly -pillow -pity -poison -roar -shift -slightly -thump -truck -tune -twenty -unable -wipe -wrote -coat -constant -dinner -drove -egg -eternal -flight -flood -frame -freak -gasp -glad -hollow -motion -peer -plastic -root -screen -season -sting -strike -team -unlike -victim -volume -warn -weird -attack -await -awake -built -charm -crave -despair -fought -grant -grief -horse -limit -message -ripple -sanity -scatter -serve -split -string -trick -annoy -blur -boat -brave -clearly -cling -connect -fist -forth -imagination -iron -jock -judge -lesson -milk -misery -nail -naked -ourselves -poet -possible -princess -sail -size -snake -society -stroke -torture -toss -trace -wise -bloom -bullet -cell -check -cost -darling -during -footstep -fragile -hallway -hardly -horizon -invisible -journey -midnight -mud -nod -pause -relax -shiver -sudden -value -youth -abuse -admire -blink -breast -bruise -constantly -couple -creep -curve -difference -dumb -emptiness -gotta -honor -plain -planet -recall -rub -ship -slam -soar -somebody -tightly -weather -adore -approach -bond -bread -burst -candle -coffee -cousin -crime -desert -flutter -frozen -grand -heel -hello -language -level -movement -pleasure -powerful -random -rhythm -settle -silly -slap -sort -spoken -steel -threaten -tumble -upset -aside -awkward -bee -blank -board -button -card -carefully -complain -crap -deeply -discover -drag -dread -effort -entire -fairy -giant -gotten -greet -illusion -jeans -leap -liquid -march -mend -nervous -nine -replace -rope -spine -stole -terror -accident -apple -balance -boom -childhood -collect -demand -depression -eventually -faint -glare -goal -group -honey -kitchen -laid -limb -machine -mere -mold -murder -nerve -painful -poetry -prince -rabbit -shelter -shore -shower -soothe -stair -steady -sunlight -tangle -tease -treasure -uncle -begun -bliss -canvas -cheer -claw -clutch -commit -crimson -crystal -delight -doll -existence -express -fog -football -gay -goose -guard -hatred -illuminate -mass -math -mourn -rich -rough -skip -stir -student -style -support -thorn -tough -yard -yearn -yesterday -advice -appreciate -autumn -bank -beam -bowl -capture -carve -collapse -confusion -creation -dove -feather -girlfriend -glory -government -harsh -hop -inner -loser -moonlight -neighbor -neither -peach -pig -praise -screw -shield -shimmer -sneak -stab -subject -throughout -thrown -tower -twirl -wow -army -arrive -bathroom -bump -cease -cookie -couch -courage -dim -guilt -howl -hum -husband -insult -led -lunch -mock -mostly -natural -nearly -needle -nerd -peaceful -perfection -pile -price -remove -roam -sanctuary -serious -shiny -shook -sob -stolen -tap -vain -void -warrior -wrinkle -affection -apologize -blossom -bounce -bridge -cheap -crumble -decision -descend -desperately -dig -dot -flip -frighten -heartbeat -huge -lazy -lick -odd -opinion -process -puzzle -quietly -retreat -score -sentence -separate -situation -skill -soak -square -stray -taint -task -tide -underneath -veil -whistle -anywhere -bedroom -bid -bloody -burden -careful -compare -concern -curtain -decay -defeat -describe -double -dreamer -driver -dwell -evening -flare -flicker -grandma -guitar -harm -horrible -hungry -indeed -lace -melody -monkey -nation -object -obviously -rainbow -salt -scratch -shown -shy -stage -stun -third -tickle -useless -weakness -worship -worthless -afternoon -beard -boyfriend -bubble -busy -certain -chin -concrete -desk -diamond -doom -drawn -due -felicity -freeze -frost -garden -glide -harmony -hopefully -hunt -jealous -lightning -mama -mercy -peel -physical -position -pulse -punch -quit -rant -respond -salty -sane -satisfy -savior -sheep -slept -social -sport -tuck -utter -valley -wolf -aim -alas -alter -arrow -awaken -beaten -belief -brand -ceiling -cheese -clue -confidence -connection -daily -disguise -eager -erase -essence -everytime -expression -fan -flag -flirt -foul -fur -giggle -glorious -ignorance -law -lifeless -measure -mighty -muse -north -opposite -paradise -patience -patient -pencil -petal -plate -ponder -possibly -practice -slice -spell -stock -strife -strip -suffocate -suit -tender -tool -trade -velvet -verse -waist -witch -aunt -bench -bold -cap -certainly -click -companion -creator -dart -delicate -determine -dish -dragon -drama -drum -dude -everybody -feast -forehead -former -fright -fully -gas -hook -hurl -invite -juice -manage -moral -possess -raw -rebel -royal -scale -scary -several -slight -stubborn -swell -talent -tea -terrible -thread -torment -trickle -usually -vast -violence -weave -acid -agony -ashamed -awe -belly -blend -blush -character -cheat -common -company -coward -creak -danger -deadly -defense -define -depend -desperate -destination -dew -duck -dusty -embarrass -engine -example -explore -foe -freely -frustrate -generation -glove -guilty -health -hurry -idiot -impossible -inhale -jaw -kingdom -mention -mist -moan -mumble -mutter -observe -ode -pathetic -pattern -pie -prefer -puff -rape -rare -revenge -rude -scrape -spiral -squeeze -strain -sunset -suspend -sympathy -thigh -throne -total -unseen -weapon -weary \ No newline at end of file diff --git a/ethcrypto/mnemonic_words.go b/ethcrypto/mnemonic_words.go new file mode 100644 index 000000000..72f0ad48f --- /dev/null +++ b/ethcrypto/mnemonic_words.go @@ -0,0 +1,1630 @@ +package ethcrypto + +var MnemonicWords []string = []string{ + "like", + "just", + "love", + "know", + "never", + "want", + "time", + "out", + "there", + "make", + "look", + "eye", + "down", + "only", + "think", + "heart", + "back", + "then", + "into", + "about", + "more", + "away", + "still", + "them", + "take", + "thing", + "even", + "through", + "long", + "always", + "world", + "too", + "friend", + "tell", + "try", + "hand", + "thought", + "over", + "here", + "other", + "need", + "smile", + "again", + "much", + "cry", + "been", + "night", + "ever", + "little", + "said", + "end", + "some", + "those", + "around", + "mind", + "people", + "girl", + "leave", + "dream", + "left", + "turn", + "myself", + "give", + "nothing", + "really", + "off", + "before", + "something", + "find", + "walk", + "wish", + "good", + "once", + "place", + "ask", + "stop", + "keep", + "watch", + "seem", + "everything", + "wait", + "got", + "yet", + "made", + "remember", + "start", + "alone", + "run", + "hope", + "maybe", + "believe", + "body", + "hate", + "after", + "close", + "talk", + "stand", + "own", + "each", + "hurt", + "help", + "home", + "god", + "soul", + "new", + "many", + "two", + "inside", + "should", + "true", + "first", + "fear", + "mean", + "better", + "play", + "another", + "gone", + "change", + "use", + "wonder", + "someone", + "hair", + "cold", + "open", + "best", + "any", + "behind", + "happen", + "water", + "dark", + "laugh", + "stay", + "forever", + "name", + "work", + "show", + "sky", + "break", + "came", + "deep", + "door", + "put", + "black", + "together", + "upon", + "happy", + "such", + "great", + "white", + "matter", + "fill", + "past", + "please", + "burn", + "cause", + "enough", + "touch", + "moment", + "soon", + "voice", + "scream", + "anything", + "stare", + "sound", + "red", + "everyone", + "hide", + "kiss", + "truth", + "death", + "beautiful", + "mine", + "blood", + "broken", + "very", + "pass", + "next", + "forget", + "tree", + "wrong", + "air", + "mother", + "understand", + "lip", + "hit", + "wall", + "memory", + "sleep", + "free", + "high", + "realize", + "school", + "might", + "skin", + "sweet", + "perfect", + "blue", + "kill", + "breath", + "dance", + "against", + "fly", + "between", + "grow", + "strong", + "under", + "listen", + "bring", + "sometimes", + "speak", + "pull", + "person", + "become", + "family", + "begin", + "ground", + "real", + "small", + "father", + "sure", + "feet", + "rest", + "young", + "finally", + "land", + "across", + "today", + "different", + "guy", + "line", + "fire", + "reason", + "reach", + "second", + "slowly", + "write", + "eat", + "smell", + "mouth", + "step", + "learn", + "three", + "floor", + "promise", + "breathe", + "darkness", + "push", + "earth", + "guess", + "save", + "song", + "above", + "along", + "both", + "color", + "house", + "almost", + "sorry", + "anymore", + "brother", + "okay", + "dear", + "game", + "fade", + "already", + "apart", + "warm", + "beauty", + "heard", + "notice", + "question", + "shine", + "began", + "piece", + "whole", + "shadow", + "secret", + "street", + "within", + "finger", + "point", + "morning", + "whisper", + "child", + "moon", + "green", + "story", + "glass", + "kid", + "silence", + "since", + "soft", + "yourself", + "empty", + "shall", + "angel", + "answer", + "baby", + "bright", + "dad", + "path", + "worry", + "hour", + "drop", + "follow", + "power", + "war", + "half", + "flow", + "heaven", + "act", + "chance", + "fact", + "least", + "tired", + "children", + "near", + "quite", + "afraid", + "rise", + "sea", + "taste", + "window", + "cover", + "nice", + "trust", + "lot", + "sad", + "cool", + "force", + "peace", + "return", + "blind", + "easy", + "ready", + "roll", + "rose", + "drive", + "held", + "music", + "beneath", + "hang", + "mom", + "paint", + "emotion", + "quiet", + "clear", + "cloud", + "few", + "pretty", + "bird", + "outside", + "paper", + "picture", + "front", + "rock", + "simple", + "anyone", + "meant", + "reality", + "road", + "sense", + "waste", + "bit", + "leaf", + "thank", + "happiness", + "meet", + "men", + "smoke", + "truly", + "decide", + "self", + "age", + "book", + "form", + "alive", + "carry", + "escape", + "damn", + "instead", + "able", + "ice", + "minute", + "throw", + "catch", + "leg", + "ring", + "course", + "goodbye", + "lead", + "poem", + "sick", + "corner", + "desire", + "known", + "problem", + "remind", + "shoulder", + "suppose", + "toward", + "wave", + "drink", + "jump", + "woman", + "pretend", + "sister", + "week", + "human", + "joy", + "crack", + "grey", + "pray", + "surprise", + "dry", + "knee", + "less", + "search", + "bleed", + "caught", + "clean", + "embrace", + "future", + "king", + "son", + "sorrow", + "chest", + "hug", + "remain", + "sat", + "worth", + "blow", + "daddy", + "final", + "parent", + "tight", + "also", + "create", + "lonely", + "safe", + "cross", + "dress", + "evil", + "silent", + "bone", + "fate", + "perhaps", + "anger", + "class", + "scar", + "snow", + "tiny", + "tonight", + "continue", + "control", + "dog", + "edge", + "mirror", + "month", + "suddenly", + "comfort", + "given", + "loud", + "quickly", + "gaze", + "plan", + "rush", + "stone", + "town", + "battle", + "ignore", + "spirit", + "stood", + "stupid", + "yours", + "brown", + "build", + "dust", + "hey", + "kept", + "pay", + "phone", + "twist", + "although", + "ball", + "beyond", + "hidden", + "nose", + "taken", + "fail", + "float", + "pure", + "somehow", + "wash", + "wrap", + "angry", + "cheek", + "creature", + "forgotten", + "heat", + "rip", + "single", + "space", + "special", + "weak", + "whatever", + "yell", + "anyway", + "blame", + "job", + "choose", + "country", + "curse", + "drift", + "echo", + "figure", + "grew", + "laughter", + "neck", + "suffer", + "worse", + "yeah", + "disappear", + "foot", + "forward", + "knife", + "mess", + "somewhere", + "stomach", + "storm", + "beg", + "idea", + "lift", + "offer", + "breeze", + "field", + "five", + "often", + "simply", + "stuck", + "win", + "allow", + "confuse", + "enjoy", + "except", + "flower", + "seek", + "strength", + "calm", + "grin", + "gun", + "heavy", + "hill", + "large", + "ocean", + "shoe", + "sigh", + "straight", + "summer", + "tongue", + "accept", + "crazy", + "everyday", + "exist", + "grass", + "mistake", + "sent", + "shut", + "surround", + "table", + "ache", + "brain", + "destroy", + "heal", + "nature", + "shout", + "sign", + "stain", + "choice", + "doubt", + "glance", + "glow", + "mountain", + "queen", + "stranger", + "throat", + "tomorrow", + "city", + "either", + "fish", + "flame", + "rather", + "shape", + "spin", + "spread", + "ash", + "distance", + "finish", + "image", + "imagine", + "important", + "nobody", + "shatter", + "warmth", + "became", + "feed", + "flesh", + "funny", + "lust", + "shirt", + "trouble", + "yellow", + "attention", + "bare", + "bite", + "money", + "protect", + "amaze", + "appear", + "born", + "choke", + "completely", + "daughter", + "fresh", + "friendship", + "gentle", + "probably", + "six", + "deserve", + "expect", + "grab", + "middle", + "nightmare", + "river", + "thousand", + "weight", + "worst", + "wound", + "barely", + "bottle", + "cream", + "regret", + "relationship", + "stick", + "test", + "crush", + "endless", + "fault", + "itself", + "rule", + "spill", + "art", + "circle", + "join", + "kick", + "mask", + "master", + "passion", + "quick", + "raise", + "smooth", + "unless", + "wander", + "actually", + "broke", + "chair", + "deal", + "favorite", + "gift", + "note", + "number", + "sweat", + "box", + "chill", + "clothes", + "lady", + "mark", + "park", + "poor", + "sadness", + "tie", + "animal", + "belong", + "brush", + "consume", + "dawn", + "forest", + "innocent", + "pen", + "pride", + "stream", + "thick", + "clay", + "complete", + "count", + "draw", + "faith", + "press", + "silver", + "struggle", + "surface", + "taught", + "teach", + "wet", + "bless", + "chase", + "climb", + "enter", + "letter", + "melt", + "metal", + "movie", + "stretch", + "swing", + "vision", + "wife", + "beside", + "crash", + "forgot", + "guide", + "haunt", + "joke", + "knock", + "plant", + "pour", + "prove", + "reveal", + "steal", + "stuff", + "trip", + "wood", + "wrist", + "bother", + "bottom", + "crawl", + "crowd", + "fix", + "forgive", + "frown", + "grace", + "loose", + "lucky", + "party", + "release", + "surely", + "survive", + "teacher", + "gently", + "grip", + "speed", + "suicide", + "travel", + "treat", + "vein", + "written", + "cage", + "chain", + "conversation", + "date", + "enemy", + "however", + "interest", + "million", + "page", + "pink", + "proud", + "sway", + "themselves", + "winter", + "church", + "cruel", + "cup", + "demon", + "experience", + "freedom", + "pair", + "pop", + "purpose", + "respect", + "shoot", + "softly", + "state", + "strange", + "bar", + "birth", + "curl", + "dirt", + "excuse", + "lord", + "lovely", + "monster", + "order", + "pack", + "pants", + "pool", + "scene", + "seven", + "shame", + "slide", + "ugly", + "among", + "blade", + "blonde", + "closet", + "creek", + "deny", + "drug", + "eternity", + "gain", + "grade", + "handle", + "key", + "linger", + "pale", + "prepare", + "swallow", + "swim", + "tremble", + "wheel", + "won", + "cast", + "cigarette", + "claim", + "college", + "direction", + "dirty", + "gather", + "ghost", + "hundred", + "loss", + "lung", + "orange", + "present", + "swear", + "swirl", + "twice", + "wild", + "bitter", + "blanket", + "doctor", + "everywhere", + "flash", + "grown", + "knowledge", + "numb", + "pressure", + "radio", + "repeat", + "ruin", + "spend", + "unknown", + "buy", + "clock", + "devil", + "early", + "false", + "fantasy", + "pound", + "precious", + "refuse", + "sheet", + "teeth", + "welcome", + "add", + "ahead", + "block", + "bury", + "caress", + "content", + "depth", + "despite", + "distant", + "marry", + "purple", + "threw", + "whenever", + "bomb", + "dull", + "easily", + "grasp", + "hospital", + "innocence", + "normal", + "receive", + "reply", + "rhyme", + "shade", + "someday", + "sword", + "toe", + "visit", + "asleep", + "bought", + "center", + "consider", + "flat", + "hero", + "history", + "ink", + "insane", + "muscle", + "mystery", + "pocket", + "reflection", + "shove", + "silently", + "smart", + "soldier", + "spot", + "stress", + "train", + "type", + "view", + "whether", + "bus", + "energy", + "explain", + "holy", + "hunger", + "inch", + "magic", + "mix", + "noise", + "nowhere", + "prayer", + "presence", + "shock", + "snap", + "spider", + "study", + "thunder", + "trail", + "admit", + "agree", + "bag", + "bang", + "bound", + "butterfly", + "cute", + "exactly", + "explode", + "familiar", + "fold", + "further", + "pierce", + "reflect", + "scent", + "selfish", + "sharp", + "sink", + "spring", + "stumble", + "universe", + "weep", + "women", + "wonderful", + "action", + "ancient", + "attempt", + "avoid", + "birthday", + "branch", + "chocolate", + "core", + "depress", + "drunk", + "especially", + "focus", + "fruit", + "honest", + "match", + "palm", + "perfectly", + "pillow", + "pity", + "poison", + "roar", + "shift", + "slightly", + "thump", + "truck", + "tune", + "twenty", + "unable", + "wipe", + "wrote", + "coat", + "constant", + "dinner", + "drove", + "egg", + "eternal", + "flight", + "flood", + "frame", + "freak", + "gasp", + "glad", + "hollow", + "motion", + "peer", + "plastic", + "root", + "screen", + "season", + "sting", + "strike", + "team", + "unlike", + "victim", + "volume", + "warn", + "weird", + "attack", + "await", + "awake", + "built", + "charm", + "crave", + "despair", + "fought", + "grant", + "grief", + "horse", + "limit", + "message", + "ripple", + "sanity", + "scatter", + "serve", + "split", + "string", + "trick", + "annoy", + "blur", + "boat", + "brave", + "clearly", + "cling", + "connect", + "fist", + "forth", + "imagination", + "iron", + "jock", + "judge", + "lesson", + "milk", + "misery", + "nail", + "naked", + "ourselves", + "poet", + "possible", + "princess", + "sail", + "size", + "snake", + "society", + "stroke", + "torture", + "toss", + "trace", + "wise", + "bloom", + "bullet", + "cell", + "check", + "cost", + "darling", + "during", + "footstep", + "fragile", + "hallway", + "hardly", + "horizon", + "invisible", + "journey", + "midnight", + "mud", + "nod", + "pause", + "relax", + "shiver", + "sudden", + "value", + "youth", + "abuse", + "admire", + "blink", + "breast", + "bruise", + "constantly", + "couple", + "creep", + "curve", + "difference", + "dumb", + "emptiness", + "gotta", + "honor", + "plain", + "planet", + "recall", + "rub", + "ship", + "slam", + "soar", + "somebody", + "tightly", + "weather", + "adore", + "approach", + "bond", + "bread", + "burst", + "candle", + "coffee", + "cousin", + "crime", + "desert", + "flutter", + "frozen", + "grand", + "heel", + "hello", + "language", + "level", + "movement", + "pleasure", + "powerful", + "random", + "rhythm", + "settle", + "silly", + "slap", + "sort", + "spoken", + "steel", + "threaten", + "tumble", + "upset", + "aside", + "awkward", + "bee", + "blank", + "board", + "button", + "card", + "carefully", + "complain", + "crap", + "deeply", + "discover", + "drag", + "dread", + "effort", + "entire", + "fairy", + "giant", + "gotten", + "greet", + "illusion", + "jeans", + "leap", + "liquid", + "march", + "mend", + "nervous", + "nine", + "replace", + "rope", + "spine", + "stole", + "terror", + "accident", + "apple", + "balance", + "boom", + "childhood", + "collect", + "demand", + "depression", + "eventually", + "faint", + "glare", + "goal", + "group", + "honey", + "kitchen", + "laid", + "limb", + "machine", + "mere", + "mold", + "murder", + "nerve", + "painful", + "poetry", + "prince", + "rabbit", + "shelter", + "shore", + "shower", + "soothe", + "stair", + "steady", + "sunlight", + "tangle", + "tease", + "treasure", + "uncle", + "begun", + "bliss", + "canvas", + "cheer", + "claw", + "clutch", + "commit", + "crimson", + "crystal", + "delight", + "doll", + "existence", + "express", + "fog", + "football", + "gay", + "goose", + "guard", + "hatred", + "illuminate", + "mass", + "math", + "mourn", + "rich", + "rough", + "skip", + "stir", + "student", + "style", + "support", + "thorn", + "tough", + "yard", + "yearn", + "yesterday", + "advice", + "appreciate", + "autumn", + "bank", + "beam", + "bowl", + "capture", + "carve", + "collapse", + "confusion", + "creation", + "dove", + "feather", + "girlfriend", + "glory", + "government", + "harsh", + "hop", + "inner", + "loser", + "moonlight", + "neighbor", + "neither", + "peach", + "pig", + "praise", + "screw", + "shield", + "shimmer", + "sneak", + "stab", + "subject", + "throughout", + "thrown", + "tower", + "twirl", + "wow", + "army", + "arrive", + "bathroom", + "bump", + "cease", + "cookie", + "couch", + "courage", + "dim", + "guilt", + "howl", + "hum", + "husband", + "insult", + "led", + "lunch", + "mock", + "mostly", + "natural", + "nearly", + "needle", + "nerd", + "peaceful", + "perfection", + "pile", + "price", + "remove", + "roam", + "sanctuary", + "serious", + "shiny", + "shook", + "sob", + "stolen", + "tap", + "vain", + "void", + "warrior", + "wrinkle", + "affection", + "apologize", + "blossom", + "bounce", + "bridge", + "cheap", + "crumble", + "decision", + "descend", + "desperately", + "dig", + "dot", + "flip", + "frighten", + "heartbeat", + "huge", + "lazy", + "lick", + "odd", + "opinion", + "process", + "puzzle", + "quietly", + "retreat", + "score", + "sentence", + "separate", + "situation", + "skill", + "soak", + "square", + "stray", + "taint", + "task", + "tide", + "underneath", + "veil", + "whistle", + "anywhere", + "bedroom", + "bid", + "bloody", + "burden", + "careful", + "compare", + "concern", + "curtain", + "decay", + "defeat", + "describe", + "double", + "dreamer", + "driver", + "dwell", + "evening", + "flare", + "flicker", + "grandma", + "guitar", + "harm", + "horrible", + "hungry", + "indeed", + "lace", + "melody", + "monkey", + "nation", + "object", + "obviously", + "rainbow", + "salt", + "scratch", + "shown", + "shy", + "stage", + "stun", + "third", + "tickle", + "useless", + "weakness", + "worship", + "worthless", + "afternoon", + "beard", + "boyfriend", + "bubble", + "busy", + "certain", + "chin", + "concrete", + "desk", + "diamond", + "doom", + "drawn", + "due", + "felicity", + "freeze", + "frost", + "garden", + "glide", + "harmony", + "hopefully", + "hunt", + "jealous", + "lightning", + "mama", + "mercy", + "peel", + "physical", + "position", + "pulse", + "punch", + "quit", + "rant", + "respond", + "salty", + "sane", + "satisfy", + "savior", + "sheep", + "slept", + "social", + "sport", + "tuck", + "utter", + "valley", + "wolf", + "aim", + "alas", + "alter", + "arrow", + "awaken", + "beaten", + "belief", + "brand", + "ceiling", + "cheese", + "clue", + "confidence", + "connection", + "daily", + "disguise", + "eager", + "erase", + "essence", + "everytime", + "expression", + "fan", + "flag", + "flirt", + "foul", + "fur", + "giggle", + "glorious", + "ignorance", + "law", + "lifeless", + "measure", + "mighty", + "muse", + "north", + "opposite", + "paradise", + "patience", + "patient", + "pencil", + "petal", + "plate", + "ponder", + "possibly", + "practice", + "slice", + "spell", + "stock", + "strife", + "strip", + "suffocate", + "suit", + "tender", + "tool", + "trade", + "velvet", + "verse", + "waist", + "witch", + "aunt", + "bench", + "bold", + "cap", + "certainly", + "click", + "companion", + "creator", + "dart", + "delicate", + "determine", + "dish", + "dragon", + "drama", + "drum", + "dude", + "everybody", + "feast", + "forehead", + "former", + "fright", + "fully", + "gas", + "hook", + "hurl", + "invite", + "juice", + "manage", + "moral", + "possess", + "raw", + "rebel", + "royal", + "scale", + "scary", + "several", + "slight", + "stubborn", + "swell", + "talent", + "tea", + "terrible", + "thread", + "torment", + "trickle", + "usually", + "vast", + "violence", + "weave", + "acid", + "agony", + "ashamed", + "awe", + "belly", + "blend", + "blush", + "character", + "cheat", + "common", + "company", + "coward", + "creak", + "danger", + "deadly", + "defense", + "define", + "depend", + "desperate", + "destination", + "dew", + "duck", + "dusty", + "embarrass", + "engine", + "example", + "explore", + "foe", + "freely", + "frustrate", + "generation", + "glove", + "guilty", + "health", + "hurry", + "idiot", + "impossible", + "inhale", + "jaw", + "kingdom", + "mention", + "mist", + "moan", + "mumble", + "mutter", + "observe", + "ode", + "pathetic", + "pattern", + "pie", + "prefer", + "puff", + "rape", + "rare", + "revenge", + "rude", + "scrape", + "spiral", + "squeeze", + "strain", + "sunset", + "suspend", + "sympathy", + "thigh", + "throne", + "total", + "unseen", + "weapon", + "weary", +} diff --git a/ethereum.go b/ethereum.go index 18c1f8a23..1e1891589 100644 --- a/ethereum.go +++ b/ethereum.go @@ -3,24 +3,27 @@ package eth import ( "container/list" "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" "net" - "net/http" "strconv" "strings" "sync" "sync/atomic" "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") @@ -41,8 +44,8 @@ type Ethereum struct { // Channel for shutting down the ethereum shutdownChan chan bool quit chan bool + // DB interface - //db *ethdb.LDBDatabase db ethutil.Database // State manager for processing new blocks and managing the over all states stateManager *ethchain.StateManager @@ -51,6 +54,8 @@ type Ethereum struct { txPool *ethchain.TxPool // The canonical chain blockChain *ethchain.BlockChain + // The block pool + blockPool *BlockPool // Peers (NYI) peers *list.List // Nonce @@ -73,7 +78,7 @@ type Ethereum struct { listening bool - reactor *ethutil.ReactorEngine + reactor *ethreact.ReactorEngine RpcServer *ethrpc.JsonRpcServer @@ -111,8 +116,9 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager clientIdentity: clientIdentity, isUpToDate: true, } - ethereum.reactor = ethutil.NewReactorEngine() + ethereum.reactor = ethreact.New() + ethereum.blockPool = NewBlockPool(ethereum) ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.blockChain = ethchain.NewBlockChain(ethereum) ethereum.stateManager = ethchain.NewStateManager(ethereum) @@ -123,7 +129,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager return ethereum, nil } -func (s *Ethereum) Reactor() *ethutil.ReactorEngine { +func (s *Ethereum) Reactor() *ethreact.ReactorEngine { return s.reactor } @@ -146,6 +152,9 @@ func (s *Ethereum) StateManager() *ethchain.StateManager { func (s *Ethereum) TxPool() *ethchain.TxPool { return s.txPool } +func (self *Ethereum) Db() ethutil.Database { + return self.db +} func (s *Ethereum) ServerCaps() Caps { return s.serverCaps @@ -355,6 +364,7 @@ func (s *Ethereum) ReapDeadPeerHandler() { // Start the ethereum func (s *Ethereum) Start(seed bool) { + s.reactor.Start() // Bind to addr and port ln, err := net.Listen("tcp", ":"+s.Port) if err != nil { @@ -420,22 +430,10 @@ func (s *Ethereum) Seed() { } // Connect to Peer list 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) { @@ -466,6 +464,8 @@ func (s *Ethereum) Stop() { } s.txPool.Stop() s.stateManager.Stop() + s.reactor.Flush() + s.reactor.Stop() ethlogger.Infoln("Server stopped") close(s.shutdownChan) diff --git a/ethlog/loggers.go b/ethlog/loggers.go index 50de213b3..b2760534b 100644 --- a/ethlog/loggers.go +++ b/ethlog/loggers.go @@ -39,7 +39,9 @@ func (msg *logMessage) send(logger LogSystem) { var logMessages chan (*logMessage) var logSystems []LogSystem -var quit chan bool +var quit chan chan error +var drained chan bool +var mutex = sync.Mutex{} type LogLevel uint8 @@ -52,34 +54,55 @@ const ( DebugDetailLevel ) +func dispatch(msg *logMessage) { + for _, logSystem := range logSystems { + if logSystem.GetLogLevel() >= msg.LogLevel { + msg.send(logSystem) + } + } +} + // log messages are dispatched to log writers func start() { -out: for { select { + case status := <-quit: + status <- nil + return case msg := <-logMessages: - for _, logSystem := range logSystems { - if logSystem.GetLogLevel() >= msg.LogLevel { - msg.send(logSystem) - } - } - case <-quit: - break out + dispatch(msg) + default: + drained <- true // this blocks until a message is sent to the queue } } } +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) func Flush() { - quit <- true - -done: - for { - select { - case <-logMessages: - default: - break done - } + if logSystems != nil { + <-drained } } @@ -97,7 +120,8 @@ func AddLogSystem(logSystem LogSystem) { defer mutex.Unlock() if logSystems == nil { logMessages = make(chan *logMessage, 10) - quit = make(chan bool, 1) + quit = make(chan chan error, 1) + drained = make(chan bool, 1) go start() } logSystems = append(logSystems, logSystem) @@ -106,14 +130,14 @@ func AddLogSystem(logSystem LogSystem) { func (logger *Logger) sendln(level LogLevel, v ...interface{}) { if logMessages != nil { msg := newPrintlnLogMessage(level, logger.tag, v...) - logMessages <- msg + send(msg) } } func (logger *Logger) sendf(level LogLevel, format string, v ...interface{}) { if logMessages != nil { msg := newPrintfLogMessage(level, logger.tag, format, v...) - logMessages <- msg + send(msg) } } diff --git a/ethlog/loggers_test.go b/ethlog/loggers_test.go index 89f416681..a9b1463e7 100644 --- a/ethlog/loggers_test.go +++ b/ethlog/loggers_test.go @@ -28,8 +28,19 @@ func (t *TestLogSystem) GetLogLevel() LogLevel { return t.level } -func quote(s string) string { - return fmt.Sprintf("'%s'", s) +func TestLoggerFlush(t *testing.T) { + 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) { @@ -41,10 +52,11 @@ func TestLoggerPrintln(t *testing.T) { logger.Infoln("info") logger.Debugln("debug") Flush() + Reset() output := testLogSystem.Output fmt.Println(quote(output)) 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.Debugf("debug") Flush() + Reset() output := testLogSystem.Output - fmt.Println(quote(output)) 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.Warnln("warn") Flush() + Reset() output0 := testLogSystem0.Output output1 := testLogSystem1.Output 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" { - 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() contents, _ := ioutil.ReadFile(filename) output := string(contents) - fmt.Println(quote(output)) 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 { os.Remove(filename) } diff --git a/ethminer/miner.go b/ethminer/miner.go index bfea8e580..e51b37e05 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -4,7 +4,7 @@ import ( "bytes" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethwire" "sort" ) @@ -15,13 +15,13 @@ type Miner struct { pow ethchain.PoW ethereum ethchain.EthManager coinbase []byte - reactChan chan ethutil.React + reactChan chan ethreact.Event txs ethchain.Transactions uncles []*ethchain.Block block *ethchain.Block powChan chan []byte - powQuitChan chan ethutil.React - quitChan chan bool + powQuitChan chan ethreact.Event + quitChan chan chan error } func (self *Miner) GetPow() ethchain.PoW { @@ -29,55 +29,53 @@ func (self *Miner) GetPow() ethchain.PoW { } 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 - powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block - powQuitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread - quitChan := make(chan bool, 1) + miner := Miner{ + pow: ðchain.EasyPow{}, + ethereum: ethereum, + coinbase: coinbase, + } - ethereum.Reactor().Subscribe("newBlock", reactChan) - ethereum.Reactor().Subscribe("newTx:pre", reactChan) + return &miner +} + +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. // The POW search method is actually blocking and if we don't // listen to the reactor events inside of the pow itself // The miner overseer will never get the reactor events themselves // Only after the miner will find the sha - ethereum.Reactor().Subscribe("newBlock", powQuitChan) - ethereum.Reactor().Subscribe("newTx:pre", powQuitChan) + reactor.Subscribe("newBlock", miner.powQuitChan) + reactor.Subscribe("newTx:pre", miner.powQuitChan) - miner := Miner{ - pow: ðchain.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") - miner.ethereum.Reactor().Post("miner:start", miner) + reactor.Post("miner:start", miner) } func (miner *Miner) listener() { -out: for { select { - case <-miner.quitChan: + case status := <-miner.quitChan: logger.Infoln("Stopped") - break out + status <- nil + return case chanMessage := <-miner.reactChan: if block, ok := chanMessage.Resource.(*ethchain.Block); ok { @@ -133,13 +131,22 @@ out: } } -func (self *Miner) Stop() { +func (miner *Miner) Stop() { logger.Infoln("Stopping...") - self.quitChan <- true - self.powQuitChan <- ethutil.React{} + miner.powQuitChan <- ethreact.Event{} - 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() { diff --git a/ethpipe/config.go b/ethpipe/config.go new file mode 100644 index 000000000..6c24df640 --- /dev/null +++ b/ethpipe/config.go @@ -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 +} diff --git a/ethpipe/js_pipe.go b/ethpipe/js_pipe.go new file mode 100644 index 000000000..b32e94a10 --- /dev/null +++ b/ethpipe/js_pipe.go @@ -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 +} diff --git a/ethpipe/js_types.go b/ethpipe/js_types.go new file mode 100644 index 000000000..0fb3a3876 --- /dev/null +++ b/ethpipe/js_types.go @@ -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(), + } +} diff --git a/ethpipe/object.go b/ethpipe/object.go new file mode 100644 index 000000000..f0032992c --- /dev/null +++ b/ethpipe/object.go @@ -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)) +} diff --git a/ethpipe/pipe.go b/ethpipe/pipe.go new file mode 100644 index 000000000..800a71139 --- /dev/null +++ b/ethpipe/pipe.go @@ -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(ðstate.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 +} diff --git a/ethpipe/pipe_test.go b/ethpipe/pipe_test.go new file mode 100644 index 000000000..071213050 --- /dev/null +++ b/ethpipe/pipe_test.go @@ -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) + } +} diff --git a/ethpipe/vm_env.go b/ethpipe/vm_env.go new file mode 100644 index 000000000..822a9e5c7 --- /dev/null +++ b/ethpipe/vm_env.go @@ -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 } diff --git a/ethpipe/world.go b/ethpipe/world.go new file mode 100644 index 000000000..4666362f9 --- /dev/null +++ b/ethpipe/world.go @@ -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 +} diff --git a/ethpub/pub.go b/ethpub/pub.go deleted file mode 100644 index 7cc7cf6ce..000000000 --- a/ethpub/pub.go +++ /dev/null @@ -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 -} diff --git a/ethpub/types.go b/ethpub/types.go deleted file mode 100644 index 5cfa2705e..000000000 --- a/ethpub/types.go +++ /dev/null @@ -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 "" -} - -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()} -} diff --git a/ethreact/README.md b/ethreact/README.md new file mode 100644 index 000000000..61af8a572 --- /dev/null +++ b/ethreact/README.md @@ -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) + + + \ No newline at end of file diff --git a/ethreact/reactor.go b/ethreact/reactor.go new file mode 100644 index 000000000..7fe2356db --- /dev/null +++ b/ethreact/reactor.go @@ -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) + } +} diff --git a/ethreact/reactor_test.go b/ethreact/reactor_test.go new file mode 100644 index 000000000..801a8abd0 --- /dev/null +++ b/ethreact/reactor_test.go @@ -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() +} diff --git a/ethrpc/packages.go b/ethrpc/packages.go index 0662f0edd..f2e57fa49 100644 --- a/ethrpc/packages.go +++ b/ethrpc/packages.go @@ -3,14 +3,15 @@ package ethrpc import ( "encoding/json" "errors" - "github.com/ethereum/eth-go/ethpub" - "github.com/ethereum/eth-go/ethutil" "math/big" "strings" + + "github.com/ethereum/eth-go/ethpipe" + "github.com/ethereum/eth-go/ethutil" ) type EthereumApi struct { - ethp *ethpub.PEthereum + pipe *ethpipe.JSPipe } type JsonArgs interface { @@ -72,8 +73,8 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error { if err != nil { return err } - // Do something - block := p.ethp.GetBlock(args.Hash) + + block := p.pipe.BlockByHash(args.Hash) *reply = NewSuccessRes(block) return nil } @@ -128,7 +129,7 @@ func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error { if err != nil { 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) return nil } @@ -138,13 +139,14 @@ func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error { if err != nil { 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) return nil } func (p *EthereumApi) GetKey(args interface{}, reply *string) error { - *reply = NewSuccessRes(p.ethp.GetKey()) + *reply = NewSuccessRes(p.pipe.Key()) return nil } @@ -174,7 +176,8 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error { if err != nil { return err } - state := p.ethp.GetStateObject(args.Address) + + state := p.pipe.World().SafeGet(ethutil.Hex2Bytes(args.Address)) var hx string 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()) } logger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx) - value := state.GetStorage(hx) - *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) + value := state.Storage(ethutil.Hex2Bytes(hx)) + *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value.Str()}) return nil } @@ -209,7 +212,7 @@ type GetPeerCountRes struct { } 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 } @@ -218,7 +221,7 @@ type GetListeningRes struct { } 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 } @@ -227,7 +230,7 @@ type GetCoinbaseRes struct { } 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 } @@ -236,7 +239,7 @@ type GetMiningRes struct { } 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 } @@ -245,7 +248,7 @@ func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *string) error { if err != nil { return err } - state := p.ethp.GetTxCountAt(args.Address) + state := p.pipe.TxCountAt(args.Address) *reply = NewSuccessRes(GetTxCountRes{Nonce: state}) return nil } @@ -271,8 +274,8 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { if err != nil { return err } - state := p.ethp.GetStateObject(args.Address) - *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) + state := p.pipe.World().SafeGet(ethutil.Hex2Bytes(args.Address)) + *reply = NewSuccessRes(BalanceRes{Balance: state.Balance.String(), Address: args.Address}) return nil } diff --git a/ethrpc/server.go b/ethrpc/server.go index d9d6f695b..4abe29aa9 100644 --- a/ethrpc/server.go +++ b/ethrpc/server.go @@ -2,11 +2,12 @@ package ethrpc import ( "fmt" - "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethpub" "net" "net/rpc" "net/rpc/jsonrpc" + + "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethpipe" ) var logger = ethlog.NewLogger("JSON") @@ -14,7 +15,7 @@ var logger = ethlog.NewLogger("JSON") type JsonRpcServer struct { quit chan bool listener net.Listener - ethp *ethpub.PEthereum + pipe *ethpipe.JSPipe } func (s *JsonRpcServer) exitHandler() { @@ -37,7 +38,7 @@ func (s *JsonRpcServer) Stop() { func (s *JsonRpcServer) Start() { logger.Infoln("Starting JSON-RPC server") go s.exitHandler() - rpc.Register(&EthereumApi{ethp: s.ethp}) + rpc.Register(&EthereumApi{pipe: s.pipe}) rpc.HandleHTTP() 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) l, err := net.Listen("tcp", sport) if err != nil { @@ -61,6 +62,6 @@ func NewJsonRpcServer(ethp *ethpub.PEthereum, port int) (*JsonRpcServer, error) return &JsonRpcServer{ listener: l, quit: make(chan bool), - ethp: ethp, + pipe: pipe, }, nil } diff --git a/ethstate/dump.go b/ethstate/dump.go new file mode 100644 index 000000000..be60a05fc --- /dev/null +++ b/ethstate/dump.go @@ -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 +} diff --git a/ethstate/manifest.go b/ethstate/manifest.go new file mode 100644 index 000000000..945de22ab --- /dev/null +++ b/ethstate/manifest.go @@ -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) +} diff --git a/ethstate/state.go b/ethstate/state.go index 51b585d4d..cf060e795 100644 --- a/ethstate/state.go +++ b/ethstate/state.go @@ -1,11 +1,12 @@ package ethstate import ( + "math/big" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethutil" - "math/big" ) var statelogger = ethlog.NewLogger("STATE") @@ -25,7 +26,7 @@ type State struct { } // 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()} } @@ -33,7 +34,7 @@ func NewState(trie *ethtrie.Trie) *State { func (self *State) GetBalance(addr []byte) *big.Int { stateObject := self.GetStateObject(addr) if stateObject != nil { - return stateObject.Amount + return stateObject.Balance } return ethutil.Big0 @@ -59,8 +60,6 @@ func (self *State) UpdateStateObject(stateObject *StateObject) { ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code) self.Trie.Update(string(addr), string(stateObject.RlpEncode())) - - self.manifest.AddObjectChange(stateObject) } // 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 { if self.Trie != nil { - state := NewState(self.Trie.Copy()) + state := New(self.Trie.Copy()) for k, stateObject := range self.stateObjects { state.stateObjects[k] = stateObject.Copy() } @@ -210,50 +209,13 @@ func (self *State) Update() { } } +func (self *State) Manifest() *Manifest { + return self.manifest +} + // Debug stuff func (self *State) CreateOutputForDiff() { for _, stateObject := range self.stateObjects { 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 -} diff --git a/ethstate/state_object.go b/ethstate/state_object.go index ab14b8604..67d09edd8 100644 --- a/ethstate/state_object.go +++ b/ethstate/state_object.go @@ -2,10 +2,11 @@ package ethstate import ( "fmt" + "math/big" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethutil" - "math/big" ) type Code []byte @@ -30,7 +31,7 @@ type StateObject struct { // Address of the object address []byte // Shared attributes - Amount *big.Int + Balance *big.Int CodeHash []byte Nonce uint64 // Contract related attributes @@ -56,40 +57,22 @@ func (self *StateObject) 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 { // This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter. address := ethutil.Address(addr) - object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)} - object.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) + object := &StateObject{address: address, Balance: new(big.Int), gasPool: new(big.Int)} + object.State = New(ethtrie.New(ethutil.Config.Db, "")) object.storage = make(Storage) object.gasPool = new(big.Int) 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.Amount = Amount - contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) + contract.Balance = balance + contract.State = New(ethtrie.New(ethutil.Config.Db, string(root))) return contract } @@ -103,7 +86,7 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject { func (self *StateObject) MarkForDeletion() { 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 { @@ -190,19 +173,19 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { } 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) { - 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) { - c.Amount = amount +func (c *StateObject) SetBalance(amount *big.Int) { + 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) ConvertGas(gas, price *big.Int) error { total := new(big.Int).Mul(gas, price) - if total.Cmp(c.Amount) > 0 { - return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total) + if total.Cmp(c.Balance) > 0 { + return fmt.Errorf("insufficient amount: %v, %v", c.Balance, total) } c.SubAmount(total) @@ -247,12 +230,12 @@ func (self *StateObject) RefundGas(gas, price *big.Int) { rGas := new(big.Int).Set(gas) rGas.Mul(rGas, price) - self.Amount.Sub(self.Amount, rGas) + self.Balance.Sub(self.Balance, rGas) } func (self *StateObject) Copy() *StateObject { stateObject := NewStateObject(self.Address()) - stateObject.Amount.Set(self.Amount) + stateObject.Balance.Set(self.Balance) stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash) stateObject.Nonce = self.Nonce if self.State != nil { @@ -290,7 +273,7 @@ func (c *StateObject) Init() Code { // Debug stuff 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) { fmt.Printf("%x %x\n", addr, value.Bytes()) }) @@ -309,15 +292,15 @@ func (c *StateObject) RlpEncode() []byte { 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) { decoder := ethutil.NewValueFromBytes(data) c.Nonce = decoder.Get(0).Uint() - c.Amount = decoder.Get(1).BigInt() - c.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) + c.Balance = decoder.Get(1).BigInt() + c.State = New(ethtrie.New(ethutil.Config.Db, decoder.Get(2).Interface())) c.storage = make(map[string]*ethutil.Value) c.gasPool = new(big.Int) diff --git a/ethstate/state_test.go b/ethstate/state_test.go index cd13e80bc..00c9de9d6 100644 --- a/ethstate/state_test.go +++ b/ethstate/state_test.go @@ -1,10 +1,11 @@ package ethstate import ( + "testing" + "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethutil" - "testing" ) var ZeroHash256 = make([]byte, 32) @@ -14,7 +15,7 @@ func TestSnapshot(t *testing.T) { ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") ethutil.Config.Db = db - state := NewState(ethtrie.NewTrie(db, "")) + state := New(ethtrie.New(db, "")) stateObject := state.GetOrNewStateObject([]byte("aa")) diff --git a/ethtrie/slice.go b/ethtrie/slice.go index b9d5d1285..cf4e8df7a 100644 --- a/ethtrie/slice.go +++ b/ethtrie/slice.go @@ -1,6 +1,6 @@ package ethtrie -import () +import "math" // Helper function for comparing slices 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 ... func MatchingNibbleLength(a, b []int) int { - i := 0 - for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) { - i += 1 + var i, length = 0, int(math.Min(float64(len(a)), float64(len(b)))) + + for i < length { + if a[i] != b[i] { + break + } + i++ } return i diff --git a/ethtrie/trie.go b/ethtrie/trie.go index f0f3fe5a8..38ae0754d 100644 --- a/ethtrie/trie.go +++ b/ethtrie/trie.go @@ -3,16 +3,17 @@ package ethtrie import ( "bytes" "fmt" - "github.com/ethereum/eth-go/ethcrypto" - "github.com/ethereum/eth-go/ethutil" _ "reflect" "sync" + + "github.com/ethereum/eth-go/ethcrypto" + "github.com/ethereum/eth-go/ethutil" ) func __ignore() { fmt.Println("") } 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) { t2.Update(key, v.Str()) @@ -158,7 +159,7 @@ func copyRoot(root interface{}) interface{} { 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 r := copyRoot(Root) p := copyRoot(Root) @@ -221,7 +222,7 @@ func (t *Trie) Cmp(trie *Trie) bool { // Returns a copy of this 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 { trie.cache.nodes[key] = node.Copy() } diff --git a/ethtrie/trie_test.go b/ethtrie/trie_test.go index 3989a8f45..2661f8f25 100644 --- a/ethtrie/trie_test.go +++ b/ethtrie/trie_test.go @@ -5,13 +5,14 @@ import ( _ "encoding/hex" _ "encoding/json" "fmt" - "github.com/ethereum/eth-go/ethutil" _ "io/ioutil" _ "math/rand" _ "net/http" _ "reflect" "testing" _ "time" + + "github.com/ethereum/eth-go/ethutil" ) const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ" @@ -38,14 +39,14 @@ func (db *MemDatabase) Print() {} func (db *MemDatabase) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil } -func New() (*MemDatabase, *Trie) { +func NewTrie() (*MemDatabase, *Trie) { db, _ := NewMemDatabase() - return db, NewTrie(db, "") + return db, New(db, "") } /* func TestTrieSync(t *testing.T) { - db, trie := New() + db, trie := NewTrie() trie.Update("dog", LONG_WORD) if len(db.db) != 0 { @@ -59,7 +60,7 @@ func TestTrieSync(t *testing.T) { } func TestTrieDirtyTracking(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("dog", LONG_WORD) if !trie.cache.IsDirty { t.Error("Expected trie to be dirty") @@ -79,7 +80,7 @@ func TestTrieDirtyTracking(t *testing.T) { } func TestTrieReset(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("cat", LONG_WORD) if len(trie.cache.nodes) == 0 { @@ -94,7 +95,7 @@ func TestTrieReset(t *testing.T) { } func TestTrieGet(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("cat", LONG_WORD) x := trie.Get("cat") @@ -104,7 +105,7 @@ func TestTrieGet(t *testing.T) { } func TestTrieUpdating(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("cat", LONG_WORD) trie.Update("cat", LONG_WORD+"1") x := trie.Get("cat") @@ -114,8 +115,8 @@ func TestTrieUpdating(t *testing.T) { } func TestTrieCmp(t *testing.T) { - _, trie1 := New() - _, trie2 := New() + _, trie1 := NewTrie() + _, trie2 := NewTrie() trie1.Update("doge", LONG_WORD) trie2.Update("doge", LONG_WORD) @@ -131,7 +132,7 @@ func TestTrieCmp(t *testing.T) { } func TestTrieDelete(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("cat", LONG_WORD) exp := trie.Root trie.Update("dog", LONG_WORD) @@ -150,7 +151,7 @@ func TestTrieDelete(t *testing.T) { } func TestTrieDeleteWithValue(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("c", LONG_WORD) exp := trie.Root trie.Update("ca", LONG_WORD) @@ -164,7 +165,7 @@ func TestTrieDeleteWithValue(t *testing.T) { } func TestTriePurge(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("c", LONG_WORD) trie.Update("ca", 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) { CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) { - _, trie := New() + _, trie := NewTrie() for key, value := range test.In { trie.Update(get(key), get(value)) } @@ -263,12 +264,12 @@ func TestRemote(t *testing.T) { func TestTrieReplay(t *testing.T) { CreateTests("https://raw.githubusercontent.com/ethereum/tests/develop/trietest.json", func(test Test) { - _, trie := New() + _, trie := NewTrie() for key, value := range test.In { trie.Update(get(key), get(value)) } - _, trie2 := New() + _, trie2 := NewTrie() trie.NewIterator().Each(func(key string, v *ethutil.Value) { trie2.Update(key, v.Str()) }) @@ -314,7 +315,7 @@ func TestRegression(t *testing.T) { roots := make(map[string]int) for i := 0; i < MaxTest; i++ { - _, trie := New() + _, trie := NewTrie() data := RandomData() for _, test := range data { @@ -333,7 +334,7 @@ func TestRegression(t *testing.T) { } func TestDelete(t *testing.T) { - _, trie := New() + _, trie := NewTrie() trie.Update("a", "jeffreytestlongstring") trie.Update("aa", "otherstring") @@ -352,7 +353,7 @@ func TestDelete(t *testing.T) { trie.Update("aaaa", "testmegood") fmt.Println("aa =>", trie.Get("aa")) - _, t2 := New() + _, t2 := NewTrie() trie.NewIterator().Each(func(key string, v *ethutil.Value) { if key == "aaaa" { t2.Update(key, v.Str()) @@ -369,7 +370,7 @@ func TestDelete(t *testing.T) { */ func TestRndCase(t *testing.T) { - _, trie := New() + _, trie := NewTrie() data := []struct{ k, v string }{ {"0000000000000000000000000000000000000000000000000000000000000001", "a07573657264617461000000000000000000000000000000000000000000000000"}, diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 34fff7d42..e38f89454 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -44,26 +44,28 @@ func BytesToNumber(b []byte) uint64 { // Read variable int // // Read a variable length number in big endian byte order -func ReadVarint(reader *bytes.Reader) (ret uint64) { - if reader.Len() == 8 { - var num uint64 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } else if reader.Len() == 4 { +func ReadVarInt(buff []byte) (ret uint64) { + switch l := len(buff); { + case l > 4: + d := LeftPadBytes(buff, 8) + binary.Read(bytes.NewReader(d), binary.BigEndian, &ret) + case l > 2: var num uint32 - binary.Read(reader, binary.BigEndian, &num) + d := LeftPadBytes(buff, 4) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) ret = uint64(num) - } else if reader.Len() == 2 { + case l > 1: var num uint16 - binary.Read(reader, binary.BigEndian, &num) + d := LeftPadBytes(buff, 2) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) ret = uint64(num) - } else { + default: var num uint8 - binary.Read(reader, binary.BigEndian, &num) + binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) ret = uint64(num) } - return ret + return } // Binary length @@ -98,6 +100,7 @@ func Bytes2Hex(d []byte) string { func Hex2Bytes(str string) []byte { h, _ := hex.DecodeString(str) + return h } @@ -128,6 +131,26 @@ func FormatData(data string) []byte { 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 { if l < len(slice) { return slice @@ -150,6 +173,28 @@ func LeftPadBytes(slice []byte, l int) []byte { 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) { if len(slice) < 20 { addr = LeftPadBytes(slice, 20) @@ -163,3 +208,11 @@ func Address(slice []byte) (addr []byte) { return } + +func ByteSliceToInterface(slice [][]byte) (ret []interface{}) { + for _, i := range slice { + ret = append(ret, i) + } + + return +} diff --git a/ethutil/bytes_test.go b/ethutil/bytes_test.go new file mode 100644 index 000000000..381efe7a2 --- /dev/null +++ b/ethutil/bytes_test.go @@ -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") + } +} diff --git a/ethutil/common.go b/ethutil/common.go index cbd94e7ad..8532d80f2 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -34,26 +34,43 @@ var ( // Currency to string // Returns a string representing a human readable format func CurrencyToString(num *big.Int) string { + var ( + fin *big.Int = num + denom string = "Wei" + ) + switch { 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: - 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: - 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: - 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: - 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: - 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: - 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: - 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 diff --git a/ethutil/config.go b/ethutil/config.go index 41bece21d..81052318e 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -3,8 +3,9 @@ package ethutil import ( "flag" "fmt" - "github.com/rakyll/globalconf" "os" + + "github.com/rakyll/globalconf" ) // Config struct @@ -28,8 +29,7 @@ var Config *ConfigManager func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager { if Config == nil { // create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags - _, err := os.Stat(ConfigFile) - if err != nil && os.IsNotExist(err) { + if !FileExist(ConfigFile) { fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile) os.Create(ConfigFile) } diff --git a/ethutil/path.go b/ethutil/path.go index 97f58ab7e..27022bcfa 100644 --- a/ethutil/path.go +++ b/ethutil/path.go @@ -1,6 +1,8 @@ package ethutil import ( + "io/ioutil" + "os" "os/user" "strings" ) @@ -18,3 +20,41 @@ func ExpandHomePath(p string) (path string) { 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 +} diff --git a/ethutil/reactor.go b/ethutil/reactor.go deleted file mode 100644 index 7cf145245..000000000 --- a/ethutil/reactor.go +++ /dev/null @@ -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}) - } -} diff --git a/ethutil/reactor_test.go b/ethutil/reactor_test.go deleted file mode 100644 index 48c2f0df3..000000000 --- a/ethutil/reactor_test.go +++ /dev/null @@ -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") - } -} diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 195ef0efb..17ff627eb 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -2,15 +2,16 @@ package ethutil import ( "bytes" - _ "encoding/binary" "fmt" - _ "log" - _ "math" "math/big" ) -type RlpEncodable interface { +type RlpEncode interface { RlpEncode() []byte +} + +type RlpEncodeDecode interface { + RlpEncode RlpValue() []interface{} } @@ -32,12 +33,14 @@ const ( RlpEmptyStr = 0x40 ) +const rlpEof = -1 + func Char(c []byte) int { if len(c) > 0 { return int(c[0]) } - return 0 + return rlpEof } func DecodeWithReader(reader *bytes.Buffer) interface{} { @@ -46,8 +49,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { // Read the next byte char := Char(reader.Next(1)) switch { - case char == 0: - return nil case char <= 0x7f: return char @@ -55,8 +56,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { return reader.Next(int(char - 0x80)) case char <= 0xbf: - buff := bytes.NewReader(reader.Next(int(char - 0xb8))) - length := ReadVarint(buff) + length := ReadVarInt(reader.Next(int(char - 0xb7))) return reader.Next(int(length)) @@ -64,82 +64,23 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { length := int(char - 0xc0) for i := 0; i < length; i++ { obj := DecodeWithReader(reader) - if obj != nil { - slice = append(slice, obj) - } else { - break - } + slice = append(slice, obj) } 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: - l := uint64(data[pos]) - 0xf7 - b := ReadVarint(bytes.NewReader(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) + length := ReadVarInt(reader.Next(int(char - 0xf7))) + for i := uint64(0); i < length; i++ { + obj := DecodeWithReader(reader) slice = append(slice, obj) - - i += (prevPos - pos) - pos = prevPos } - return slice, pos + return slice default: panic(fmt.Sprintf("byte not supported: %q", char)) } - return slice, 0 + return slice } var ( @@ -223,3 +164,67 @@ func Encode(object interface{}) []byte { 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 +} diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 095c01ecc..90057ab42 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -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) { value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01")) if value.Get(0).Str() != "dog" { diff --git a/ethutil/value.go b/ethutil/value.go index 735a71dbc..608d332ba 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -1,7 +1,6 @@ package ethutil import ( - "bytes" "fmt" "math/big" "reflect" @@ -67,7 +66,7 @@ func (val *Value) Uint() uint64 { } else if Val, ok := val.Val.(uint); ok { return uint64(Val) } 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 { return Val.Uint64() } @@ -75,6 +74,30 @@ func (val *Value) Uint() uint64 { 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 { if Val, ok := val.Val.(byte); ok { return Val @@ -123,6 +146,14 @@ func (val *Value) Bytes() []byte { return []byte{} } +func (val *Value) Err() error { + if err, ok := val.Val.(error); ok { + return err + } + + return nil +} + func (val *Value) Slice() []interface{} { if d, ok := val.Val.([]interface{}); ok { return d @@ -158,6 +189,11 @@ func (val *Value) IsStr() bool { return val.Type() == reflect.String } +func (self *Value) IsErr() bool { + _, ok := self.Val.(error) + return ok +} + // Special list checking function. Something is considered // a list if it's of type []interface{}. The list is usually // 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) } +func (self *Value) DeepCmp(o *Value) bool { + a := NewValue(self.BigInt()) + b := NewValue(o.BigInt()) + + return a.Cmp(b) +} + func (val *Value) Encode() []byte { return Encode(val.Val) } @@ -215,12 +258,15 @@ func (val *Value) Encode() []byte { func (self *Value) Decode() { v, _ := Decode(self.Bytes(), 0) self.Val = v + //self.Val = DecodeWithReader(bytes.NewBuffer(self.Bytes())) } func NewValueFromBytes(data []byte) *Value { if len(data) != 0 { - data, _ := Decode(data, 0) - return NewValue(data) + value := NewValue(data) + value.Decode() + + return value } return NewValue(nil) @@ -262,6 +308,55 @@ func (val *Value) Append(v interface{}) *Value { 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 { value *Value currentValue *Value diff --git a/ethutil/value_test.go b/ethutil/value_test.go index a100f44bc..710cbd887 100644 --- a/ethutil/value_test.go +++ b/ethutil/value_test.go @@ -63,3 +63,18 @@ func TestIterator(t *testing.T) { 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) + } +} diff --git a/ethvm/closure.go b/ethvm/closure.go index 505fd43fb..54bfd05f4 100644 --- a/ethvm/closure.go +++ b/ethvm/closure.go @@ -3,9 +3,10 @@ package ethvm // TODO Re write VM to use values instead of big integers? import ( + "math/big" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" - "math/big" ) type ClosureRef interface { @@ -17,9 +18,10 @@ type ClosureRef interface { // Basic inline closure object which implement the 'closure' interface type Closure struct { - caller ClosureRef - object *ethstate.StateObject - Code []byte + caller ClosureRef + object *ethstate.StateObject + Code []byte + message *ethstate.Message Gas, UsedGas, Price *big.Int @@ -27,8 +29,8 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure { - c := &Closure{caller: caller, object: object, Code: code, Args: nil} +func NewClosure(msg *ethstate.Message, caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure { + 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 // This pointer will be off the state transition diff --git a/ethvm/stack.go b/ethvm/stack.go index c06d63652..f4b0be393 100644 --- a/ethvm/stack.go +++ b/ethvm/stack.go @@ -59,7 +59,7 @@ func (st *Stack) Peek() *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] } diff --git a/ethvm/vm.go b/ethvm/vm.go index a93b56e60..ddad0d366 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -2,11 +2,12 @@ package ethvm import ( "fmt" + "math" + "math/big" + "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" - "math" - "math/big" ) type Debugger interface { @@ -51,6 +52,7 @@ type Environment interface { Time() int64 Difficulty() *big.Int Value() *big.Int + BlockHash() []byte } 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()) }) } - - 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) @@ -244,6 +246,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Add(y, x) + ensure256(base) + self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) @@ -254,6 +258,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Sub(y, x) + ensure256(base) + self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) @@ -264,6 +270,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mul(y, x) + ensure256(base) + self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) @@ -276,6 +284,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Div(y, x) } + ensure256(base) + self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) @@ -288,6 +298,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Div(y, x) } + ensure256(base) + self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) @@ -299,6 +311,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mod(y, x) + ensure256(base) + self.Printf(" = %v", base) stack.Push(base) case SMOD: @@ -309,6 +323,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mod(y, x) + ensure256(base) + self.Printf(" = %v", base) stack.Push(base) @@ -320,6 +336,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Exp(y, x, Pow256) + ensure256(base) + self.Printf(" = %v", base) stack.Push(base) @@ -627,8 +645,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { val, loc := stack.Popn() closure.SetStorage(loc, ethutil.NewValue(val)) - // Add the change to manifest - self.env.State().Manifest().AddStorageChange(closure.Object(), loc.Bytes(), val) + closure.message.AddStorageChange(loc.Bytes()) self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case JUMP: @@ -679,27 +696,35 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf(" (*) %x", addr).Endl() + msg := self.env.State().Manifest().AddMessage(ðstate.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 contract := self.env.State().NewStateObject(addr) - if contract.Amount.Cmp(value) >= 0 { + if contract.Balance.Cmp(value) >= 0 { closure.object.SubAmount(value) contract.AddAmount(value) // Set the init script initCode := mem.Get(offset.Int64(), size.Int64()) - //fmt.Printf("%x\n", initCode) + msg.Input = initCode + // Transfer all remaining gas to the new // contract so it may run the init script gas := new(big.Int).Set(closure.Gas) closure.UseGas(closure.Gas) // 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 // main script. contract.Code, _, err = c.Call(self, nil) } 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 { @@ -711,7 +736,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf("CREATE err %v", err) } else { stack.Push(ethutil.BigD(addr)) - self.Printf("CREATE success") + + msg.Output = contract.Code } self.Endl() @@ -735,8 +761,16 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) - if closure.object.Amount.Cmp(value) < 0 { - vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) + msg := self.env.State().Manifest().AddMessage(ðstate.Message{ + 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) @@ -750,7 +784,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { stateObject.AddAmount(value) // 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) ret, _, err := c.Call(self, args) if err != nil { @@ -765,6 +799,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { mem.Set(retOffset.Int64(), retSize.Int64(), ret) } + msg.Output = ret + // Debug hook if self.Dbg != nil { 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.AddAmount(closure.object.Amount) + receiver.AddAmount(closure.object.Balance) closure.object.MarkForDeletion() @@ -837,3 +873,18 @@ func (self *Vm) Endl() *Vm { 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) + } +} diff --git a/ethvm/vm_test.go b/ethvm/vm_test.go index 501d0c284..5b3a9aef1 100644 --- a/ethvm/vm_test.go +++ b/ethvm/vm_test.go @@ -2,14 +2,14 @@ package ethvm import ( "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" "math/big" "os" "testing" + + "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethstate" + "github.com/ethereum/eth-go/ethutil" ) type TestEnv struct { @@ -27,9 +27,7 @@ func (self TestEnv) State() *ethstate.State { return nil } func TestVm(t *testing.T) { ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4))) - db, _ := ethdb.NewMemDatabase() ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") - ethutil.Config.Db = db stateObject := ethstate.NewStateObject([]byte{'j', 'e', 'f', 'f'}) callerClosure := NewClosure(stateObject, stateObject, []byte{0x60, 0x01}, big.NewInt(1000000), big.NewInt(0)) diff --git a/ethwire/messaging.go b/ethwire/messaging.go index f13b72353..7ac0188a1 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -6,9 +6,10 @@ import ( "bytes" "errors" "fmt" - "github.com/ethereum/eth-go/ethutil" "net" "time" + + "github.com/ethereum/eth-go/ethutil" ) // 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 // defined by the wire protocol spec; it is easier for humans to ensure // correctness when values are explicit. - MsgHandshakeTy = 0x00 - MsgDiscTy = 0x01 - MsgPingTy = 0x02 - MsgPongTy = 0x03 - MsgGetPeersTy = 0x10 - MsgPeersTy = 0x11 - MsgTxTy = 0x12 - MsgBlockTy = 0x13 - MsgGetChainTy = 0x14 - MsgNotInChainTy = 0x15 - MsgGetTxsTy = 0x16 + MsgHandshakeTy = 0x00 + MsgDiscTy = 0x01 + MsgPingTy = 0x02 + MsgPongTy = 0x03 + MsgGetPeersTy = 0x10 + MsgPeersTy = 0x11 + MsgTxTy = 0x12 + MsgGetChainTy = 0x14 + MsgNotInChainTy = 0x15 + MsgGetTxsTy = 0x16 + MsgGetBlockHashesTy = 0x17 + MsgBlockHashesTy = 0x18 + MsgGetBlocksTy = 0x19 + MsgBlockTy = 0x13 + + MsgOldBlockTy = 0xbb MsgTalkTy = 0xff ) var msgTypeToString = map[MsgType]string{ - MsgHandshakeTy: "Handshake", - MsgDiscTy: "Disconnect", - MsgPingTy: "Ping", - MsgPongTy: "Pong", - MsgGetPeersTy: "Get peers", - MsgPeersTy: "Peers", - MsgTxTy: "Transactions", - MsgBlockTy: "Blocks", - MsgGetChainTy: "Get chain", - MsgGetTxsTy: "Get Txs", - MsgNotInChainTy: "Not in chain", + MsgHandshakeTy: "Handshake", + MsgDiscTy: "Disconnect", + MsgPingTy: "Ping", + MsgPongTy: "Pong", + MsgGetPeersTy: "Get peers", + MsgPeersTy: "Peers", + MsgTxTy: "Transactions", + MsgBlockTy: "Blocks", + MsgGetChainTy: "Get chain", + MsgGetTxsTy: "Get Txs", + MsgNotInChainTy: "Not in chain", + MsgGetBlockHashesTy: "Get block hashes", + MsgBlockHashesTy: "Block hashes", + MsgGetBlocksTy: "Get blocks", } func (mt MsgType) String() string { @@ -109,7 +118,7 @@ func (self *Connection) Write(typ MsgType, v ...interface{}) error { slice := [][]interface{}{[]interface{}{byte(typ)}} for _, value := range v { - if encodable, ok := value.(ethutil.RlpEncodable); ok { + if encodable, ok := value.(ethutil.RlpEncodeDecode); ok { slice = append(slice, encodable.RlpValue()) } else if raw, ok := value.([]interface{}); ok { slice = append(slice, raw) diff --git a/peer.go b/peer.go index ffba695ca..d841b2ad3 100644 --- a/peer.go +++ b/peer.go @@ -4,15 +4,18 @@ import ( "bytes" "container/list" "fmt" - "github.com/ethereum/eth-go/ethchain" - "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethutil" - "github.com/ethereum/eth-go/ethwire" + "math" + "math/big" "net" "strconv" "strings" "sync/atomic" "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") @@ -21,7 +24,7 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 23 + ProtocolVersion = 27 // Interval for ping/pong message pingPongTimer = 2 * time.Second ) @@ -124,9 +127,13 @@ type Peer struct { lastPong int64 lastBlockReceived time.Time - host []byte - port uint16 - caps Caps + host []byte + port uint16 + caps Caps + td *big.Int + bestHash []byte + lastReceivedHash []byte + requestedHashes [][]byte // This peer's public key 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) { - for attempts := 0; attempts < 5; attempts++ { + const maxTries = 3 + for attempts := 0; attempts < maxTries; attempts++ { conn, err = net.DialTimeout("tcp", addr, 10*time.Second) 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 } @@ -291,12 +300,14 @@ out: // Ping timer case <-pingTimer.C: - timeSince := time.Since(time.Unix(p.lastPong, 0)) - if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) { - peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince) - p.Stop() - return - } + /* + timeSince := time.Since(time.Unix(p.lastPong, 0)) + if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) { + peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince) + p.Stop() + return + } + */ p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, "")) p.pingStartTime = time.Now() @@ -340,7 +351,6 @@ func (p *Peer) HandleInbound() { for _, msg := range msgs { peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data) - nextMsg: switch msg.Type { case ethwire.MsgHandshakeTy: // Version message @@ -349,9 +359,10 @@ func (p *Peer) HandleInbound() { if p.caps.IsCap(CapPeerDiscTy) { p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, "")) } + case ethwire.MsgDiscTy: 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: // Respond back with pong 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 // active. p.lastPong = time.Now().Unix() - p.pingTime = time.Now().Sub(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) - } - } - + p.pingTime = time.Since(p.pingStartTime) case ethwire.MsgTxTy: // If the message was a transaction queue the transaction // in the TxPool where it will undergo validation and @@ -489,58 +396,6 @@ func (p *Peer) HandleInbound() { // Connect to the list of 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: // Get the current transactions of the pool txs := p.ethereum.TxPool().CurrentTransactions() @@ -552,15 +407,113 @@ func (p *Peer) HandleInbound() { // Broadcast it back to the peer p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface)) - // Unofficial but fun nonetheless - case ethwire.MsgTalkTy: - peerlogger.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str()) + case ethwire.MsgGetBlockHashesTy: + if msg.Data.Len() < 2 { + peerlogger.Debugln("err: argument length invalid ", msg.Data.Len()) + } + + hash := msg.Data.Get(0).Bytes() + amount := msg.Data.Get(1).Uint() + + hashes := p.ethereum.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() } +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 func (self *Peer) update() { serviceTimer := time.NewTicker(5 * time.Second) @@ -631,6 +584,7 @@ func (p *Peer) pushHandshake() error { pubkey := p.ethereum.KeyManager().PublicKey() msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ 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) @@ -716,10 +670,15 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { 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.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 @@ -728,7 +687,12 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { 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) } @@ -782,7 +746,7 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) { if !p.catchingUp { // Make sure nobody else is catching up when you want to do this p.catchingUp = true - msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(30)}) + msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)}) p.QueueMessage(msg) peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())