From a760ce05b948e89bc564af20599dcf95698ac0eb Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 Aug 2014 16:23:38 +0200 Subject: [PATCH] Updated chain for filtering --- ethchain/block.go | 13 ------- ethchain/block_chain.go | 2 ++ ethchain/block_chain_test.go | 23 ++++++++---- ethchain/state_manager.go | 70 ++++++++++++++++++++++++++++++++++-- ethchain/state_transition.go | 12 ++++++- ethchain/vm_env.go | 4 ++- ethereum.go | 5 ++- ethpipe/vm_env.go | 1 + ethstate/state.go | 45 +++-------------------- ethutil/rlp.go | 9 ++--- ethutil/value.go | 24 +++++++++++++ ethvm/closure.go | 3 +- ethvm/vm.go | 22 ++++++++++-- ethwire/messaging.go | 5 +-- 14 files changed, 164 insertions(+), 74 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 5b06fd58d..5765abd51 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -142,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 { diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 3eba90fca..611735707 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -300,6 +300,8 @@ func (bc *BlockChain) setLastBlock() { bc.genesisBlock.state.Trie.Sync() // Prepare the genesis block bc.Add(bc.genesisBlock) + fk := append([]byte("bloom"), bc.genesisBlock.Hash()...) + bc.Ethereum.Db().Put(fk, make([]byte, 255)) } // Set the last know difficulty (might be 0x0 as initial value, Genesis) 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/state_manager.go b/ethchain/state_manager.go index f06622fcb..a60b28b3f 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -45,6 +45,7 @@ type EthManager interface { Peers() *list.List KeyManager() *ethcrypto.KeyManager ClientIdentity() ethwire.ClientIdentity + Db() ethutil.Database } type StateManager struct { @@ -235,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 { @@ -363,14 +369,74 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) notifyChanges(state *ethstate.State) { +// 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 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() + + if ethutil.Config.Diff && ethutil.Config.DiffType == "all" { + fmt.Printf("## %x %x ##\n", block.Hash(), block.Number) + } + + receipts, err := sm.ApplyDiff(state, parent, block) + if err != nil { + return nil, err + } + + txSha := CreateTxSha(receipts) + if bytes.Compare(txSha, block.TxSha) != 0 { + return nil, fmt.Errorf("Error validating tx sha. Received %x, got %x", block.TxSha, txSha) + } + + // Block validation + if err = sm.ValidateBlock(block); err != nil { + statelogger.Errorln("Error validating block:", err) + return nil, err + } + + // I'm not sure, but I don't know if there should be thrown + // any errors at this time. + if err = sm.AccumelateRewards(state, block); err != nil { + statelogger.Errorln("Error accumulating reward", err) + return nil, err + } + + if !block.State().Cmp(state) { + err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root) + return nil, err + } + + return state.Manifest().Messages, nil } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index dfcbfcc04..489ff2b6a 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -211,6 +211,13 @@ 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, + }) + // Process the init code and create 'valid' contract if IsContractAddr(self.receiver) { // Evaluate the initialization script @@ -226,14 +233,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(receiver.Code, receiver, "code") if err != nil { self.state.Set(snapshot) return fmt.Errorf("Error during code execution %v", err) } + + msg.Output = ret } } 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/ethereum.go b/ethereum.go index 69bb93cff..c1c4c2f2f 100644 --- a/ethereum.go +++ b/ethereum.go @@ -44,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 @@ -149,6 +149,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 diff --git a/ethpipe/vm_env.go b/ethpipe/vm_env.go index c06a2a763..822a9e5c7 100644 --- a/ethpipe/vm_env.go +++ b/ethpipe/vm_env.go @@ -29,5 +29,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.value } func (self *VMEnv) State() *ethstate.State { return self.state } diff --git a/ethstate/state.go b/ethstate/state.go index 10bcf6335..19210916e 100644 --- a/ethstate/state.go +++ b/ethstate/state.go @@ -211,50 +211,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/ethutil/rlp.go b/ethutil/rlp.go index cf7f97ffd..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{} } diff --git a/ethutil/value.go b/ethutil/value.go index 2233b978c..608d332ba 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -74,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 diff --git a/ethvm/closure.go b/ethvm/closure.go index 505fd43fb..f9be952d4 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 { diff --git a/ethvm/vm.go b/ethvm/vm.go index a3c273f62..a0d4db591 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -52,6 +52,7 @@ type Environment interface { Time() int64 Difficulty() *big.Int Value() *big.Int + BlockHash() []byte } type Object interface { @@ -696,6 +697,12 @@ 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(), + }) + // Create a new contract contract := self.env.State().NewStateObject(addr) if contract.Balance.Cmp(value) >= 0 { @@ -704,7 +711,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // 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) @@ -728,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() @@ -752,6 +761,13 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) + 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(), + }) + if closure.object.Balance.Cmp(value) < 0 { vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance) @@ -782,6 +798,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) diff --git a/ethwire/messaging.go b/ethwire/messaging.go index f13b72353..d114a1c9d 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. @@ -109,7 +110,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)