From 6774ddaba29ae9e9db5065ce74055297b948adf9 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 21 Jul 2014 14:35:37 +0200 Subject: [PATCH 01/26] Fix regression on 32bit (windows) systems --- ethchain/vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 4fdf8b31ad..528088a493 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -506,7 +506,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { offset := stack.Pop().Int64() data := make([]byte, 32) - if len(closure.Args) >= int(offset) { + if big.NewInt(int64(len(closure.Args))).Cmp(big.NewInt(offset)) >= 0 { l := int64(math.Min(float64(offset+32), float64(len(closure.Args)))) copy(data, closure.Args[offset:l]) From 20ee1ae65e57ef7d60404277162c84c572c6bb10 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 21 Jul 2014 20:38:43 +0200 Subject: [PATCH 02/26] Refactored CALLDATALOAD to use big int * Added BigMin --- ethchain/vm.go | 14 +++++++++----- ethutil/big.go | 19 +++++++++++-------- ethutil/common.go | 12 +++++++----- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 34ecd95b71..a9bed1eca9 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -503,13 +503,17 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { vm.Printf(" => %v", vm.vars.Value) case CALLDATALOAD: require(1) - offset := stack.Pop().Int64() + var ( + offset = stack.Pop() + data = make([]byte, 32) + lenData = big.NewInt(int64(len(closure.Args))) + ) - data := make([]byte, 32) - if big.NewInt(int64(len(closure.Args))).Cmp(big.NewInt(offset)) >= 0 { - l := int64(math.Min(float64(offset+32), float64(len(closure.Args)))) + if lenData.Cmp(offset) >= 0 { + length := new(big.Int).Add(offset, ethutil.Big32) + length = ethutil.BigMin(length, lenData) - copy(data, closure.Args[offset:l]) + copy(data, closure.Args[offset.Int64():length.Int64()]) } vm.Printf(" => 0x%x", data) diff --git a/ethutil/big.go b/ethutil/big.go index 7af6f74145..ec263b8188 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -4,14 +4,6 @@ import ( "math/big" ) -var BigInt0 *big.Int = big.NewInt(0) - -// True -var BigTrue *big.Int = big.NewInt(1) - -// False -var BigFalse *big.Int = big.NewInt(0) - // Big pow // // Returns the power of two big integers @@ -73,3 +65,14 @@ func BigMax(x, y *big.Int) *big.Int { return x } + +// Big min +// +// Returns the minimum size big integer +func BigMin(x, y *big.Int) *big.Int { + if x.Cmp(y) >= 0 { + return y + } + + return x +} diff --git a/ethutil/common.go b/ethutil/common.go index 2fd031a20a..cbd94e7add 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -58,9 +58,11 @@ func CurrencyToString(num *big.Int) string { // Common big integers often used var ( - Big1 = big.NewInt(1) - Big2 = big.NewInt(2) - Big0 = big.NewInt(0) - Big32 = big.NewInt(32) - Big256 = big.NewInt(0xff) + Big1 = big.NewInt(1) + Big2 = big.NewInt(2) + Big0 = big.NewInt(0) + BigTrue = Big1 + BigFalse = Big0 + Big32 = big.NewInt(32) + Big256 = big.NewInt(0xff) ) From 1e8b54abfb7129fcdf4812ad01b6a7cd61e4f65d Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 22 Jul 2014 11:54:48 +0200 Subject: [PATCH 03/26] Refactored state, state object and vm * The State and StateObject have been moved to their own package * The VM is moved to it's own package --- ethchain/state_manager.go | 2 +- ethpub/pub.go | 6 + ethstate/.ethtest | 0 ethstate/errors.go | 23 ++ ethstate/state.go | 259 ++++++++++++ ethstate/state_object.go | 339 +++++++++++++++ ethstate/state_test.go | 35 ++ ethvm/.ethtest | 0 ethvm/asm.go | 44 ++ ethvm/closure.go | 116 ++++++ ethvm/common.go | 27 ++ ethvm/stack.go | 150 +++++++ ethvm/types.go | 346 ++++++++++++++++ ethvm/vm.go | 848 ++++++++++++++++++++++++++++++++++++++ ethvm/vm_test.go | 48 +++ 15 files changed, 2242 insertions(+), 1 deletion(-) create mode 100644 ethstate/.ethtest create mode 100644 ethstate/errors.go create mode 100644 ethstate/state.go create mode 100644 ethstate/state_object.go create mode 100644 ethstate/state_test.go create mode 100644 ethvm/.ethtest create mode 100644 ethvm/asm.go create mode 100644 ethvm/closure.go create mode 100644 ethvm/common.go create mode 100644 ethvm/stack.go create mode 100644 ethvm/types.go create mode 100644 ethvm/vm.go create mode 100644 ethvm/vm_test.go diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 6a6c67492a..8dc88d7c3a 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -123,9 +123,9 @@ done: cb := state.GetStateObject(coinbase.Address()) st := NewStateTransition(cb, tx, state, block) - //fmt.Printf("#%d\n", i+1) err = st.TransitionState() if err != nil { + statelogger.Infoln(err) switch { case IsNonceErr(err): err = nil // ignore error diff --git a/ethpub/pub.go b/ethpub/pub.go index 5d01a7a44a..2650edf021 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -48,6 +48,12 @@ type PEthereum struct { } 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(), diff --git a/ethstate/.ethtest b/ethstate/.ethtest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ethstate/errors.go b/ethstate/errors.go new file mode 100644 index 0000000000..d5d7db86b4 --- /dev/null +++ b/ethstate/errors.go @@ -0,0 +1,23 @@ +package ethstate + +import ( + "fmt" + "math/big" +) + +type GasLimitErr struct { + Message string + Is, Max *big.Int +} + +func IsGasLimitErr(err error) bool { + _, ok := err.(*GasLimitErr) + + return ok +} +func (err *GasLimitErr) Error() string { + return err.Message +} +func GasLimitError(is, max *big.Int) *GasLimitErr { + return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max} +} diff --git a/ethstate/state.go b/ethstate/state.go new file mode 100644 index 0000000000..a4b9b1e9c6 --- /dev/null +++ b/ethstate/state.go @@ -0,0 +1,259 @@ +package ethstate + +import ( + "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") + +// States within the ethereum protocol are used to store anything +// within the merkle trie. States take care of caching and storing +// nested states. It's the general query interface to retrieve: +// * Contracts +// * Accounts +type State struct { + // The trie for this structure + trie *ethtrie.Trie + + stateObjects map[string]*StateObject + + manifest *Manifest +} + +// Create a new state from a given trie +func NewState(trie *ethtrie.Trie) *State { + return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} +} + +// Retrieve the balance from the given address or 0 if object not found +func (self *State) GetBalance(addr []byte) *big.Int { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + return stateObject.Amount + } + + return ethutil.Big0 +} + +func (self *State) GetNonce(addr []byte) uint64 { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + return stateObject.Nonce + } + + return 0 +} + +// +// Setting, updating & deleting state object methods +// + +// Update the given state object and apply it to state trie +func (self *State) UpdateStateObject(stateObject *StateObject) { + addr := stateObject.Address() + + 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 +func (self *State) DeleteStateObject(stateObject *StateObject) { + self.trie.Delete(string(stateObject.Address())) + + delete(self.stateObjects, string(stateObject.Address())) +} + +// Retrieve a state object given my the address. Nil if not found +func (self *State) GetStateObject(addr []byte) *StateObject { + addr = ethutil.Address(addr) + + stateObject := self.stateObjects[string(addr)] + if stateObject != nil { + return stateObject + } + + data := self.trie.Get(string(addr)) + if len(data) == 0 { + return nil + } + + stateObject = NewStateObjectFromBytes(addr, []byte(data)) + self.stateObjects[string(addr)] = stateObject + + return stateObject +} + +// Retrieve a state object or create a new state object if nil +func (self *State) GetOrNewStateObject(addr []byte) *StateObject { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + stateObject = self.NewStateObject(addr) + } + + return stateObject +} + +// Create a state object whether it exist in the trie or not +func (self *State) NewStateObject(addr []byte) *StateObject { + addr = ethutil.Address(addr) + + statelogger.Infof("(+) %x\n", addr) + + stateObject := NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject + + return stateObject +} + +// Deprecated +func (self *State) GetAccount(addr []byte) *StateObject { + return self.GetOrNewStateObject(addr) +} + +// +// Setting, copying of the state methods +// + +func (s *State) Cmp(other *State) bool { + return s.trie.Cmp(other.trie) +} + +func (self *State) Copy() *State { + if self.trie != nil { + state := NewState(self.trie.Copy()) + for k, stateObject := range self.stateObjects { + state.stateObjects[k] = stateObject.Copy() + } + + return state + } + + return nil +} + +func (self *State) Set(state *State) { + if state == nil { + panic("Tried setting 'state' to nil through 'Set'") + } + + self.trie = state.trie + self.stateObjects = state.stateObjects +} + +func (s *State) Root() interface{} { + return s.trie.Root +} + +// Resets the trie and all siblings +func (s *State) Reset() { + s.trie.Undo() + + // Reset all nested states + for _, stateObject := range s.stateObjects { + if stateObject.state == nil { + continue + } + + //stateObject.state.Reset() + stateObject.Reset() + } + + s.Empty() +} + +// Syncs the trie and all siblings +func (s *State) Sync() { + // Sync all nested states + for _, stateObject := range s.stateObjects { + //s.UpdateStateObject(stateObject) + + if stateObject.state == nil { + continue + } + + stateObject.state.Sync() + } + + s.trie.Sync() + + s.Empty() +} + +func (self *State) Empty() { + self.stateObjects = make(map[string]*StateObject) +} + +func (self *State) Update() { + for _, stateObject := range self.stateObjects { + if stateObject.remove { + self.DeleteStateObject(stateObject) + } else { + stateObject.Sync() + + self.UpdateStateObject(stateObject) + } + } + + // FIXME trie delete is broken + valid, t2 := ethtrie.ParanoiaCheck(self.trie) + if !valid { + statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root) + + self.trie = t2 + } +} + +// 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 new file mode 100644 index 0000000000..d8513f37d4 --- /dev/null +++ b/ethstate/state_object.go @@ -0,0 +1,339 @@ +package ethstate + +import ( + "fmt" + "github.com/ethereum/eth-go/ethcrypto" + "github.com/ethereum/eth-go/ethtrie" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type Code []byte + +func (self Code) String() string { + return "" //strings.Join(Disassemble(self), " ") +} + +type Storage map[string]*ethutil.Value + +func (self Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range self { + // XXX Do we need a 'value' copy or is this sufficient? + cpy[key] = value + } + + return cpy +} + +type StateObject struct { + // Address of the object + address []byte + // Shared attributes + Amount *big.Int + CodeHash []byte + Nonce uint64 + // Contract related attributes + state *State + Code Code + initCode Code + + storage Storage + + // Total gas pool is the total amount of gas currently + // left if this object is the coinbase. Gas is directly + // purchased of the coinbase. + gasPool *big.Int + + // Mark for deletion + // When an object is marked for deletion it will be delete from the trie + // during the "update" phase of the state transition + remove bool +} + +func (self *StateObject) Reset() { + self.storage = make(Storage) + 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.storage = make(Storage) + object.gasPool = new(big.Int) + + return object +} + +func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { + contract := NewStateObject(address) + contract.Amount = Amount + contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) + + return contract +} + +func NewStateObjectFromBytes(address, data []byte) *StateObject { + object := &StateObject{address: address} + object.RlpDecode(data) + + return object +} + +func (self *StateObject) MarkForDeletion() { + self.remove = true + statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount) +} + +func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { + return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) +} + +func (c *StateObject) SetAddr(addr []byte, value interface{}) { + c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) +} + +func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { + return self.getStorage(key.Bytes()) +} +func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) { + self.setStorage(key.Bytes(), value) +} + +func (self *StateObject) getStorage(k []byte) *ethutil.Value { + key := ethutil.LeftPadBytes(k, 32) + + value := self.storage[string(key)] + if value == nil { + value = self.GetAddr(key) + + if !value.IsNil() { + self.storage[string(key)] = value + } + } + + return value + + //return self.GetAddr(key) +} + +func (self *StateObject) setStorage(k []byte, value *ethutil.Value) { + key := ethutil.LeftPadBytes(k, 32) + self.storage[string(key)] = value.Copy() +} + +// Iterate over each storage address and yield callback +func (self *StateObject) EachStorage(cb ethtrie.EachCallback) { + // First loop over the uncommit/cached values in storage + for key, value := range self.storage { + // XXX Most iterators Fns as it stands require encoded values + encoded := ethutil.NewValue(value.Encode()) + cb(key, encoded) + } + + it := self.state.trie.NewIterator() + it.Each(func(key string, value *ethutil.Value) { + // If it's cached don't call the callback. + if self.storage[key] == nil { + cb(key, value) + } + }) +} + +func (self *StateObject) Sync() { + for key, value := range self.storage { + if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 { + //data := self.getStorage([]byte(key)) + //fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data) + self.state.trie.Delete(string(key)) + continue + } + + self.SetAddr([]byte(key), value) + } + + valid, t2 := ethtrie.ParanoiaCheck(self.state.trie) + if !valid { + statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root) + + self.state.trie = t2 + } +} + +func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { + if int64(len(c.Code)-1) < pc.Int64() { + return ethutil.NewValue(0) + } + + return ethutil.NewValueFromBytes([]byte{c.Code[pc.Int64()]}) +} + +func (c *StateObject) AddAmount(amount *big.Int) { + c.SetAmount(new(big.Int).Add(c.Amount, amount)) + + statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) +} + +func (c *StateObject) SubAmount(amount *big.Int) { + c.SetAmount(new(big.Int).Sub(c.Amount, amount)) + + statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) +} + +func (c *StateObject) SetAmount(amount *big.Int) { + c.Amount = amount +} + +// +// Gas setters and getters +// + +// Return the gas back to the origin. Used by the Virtual machine or Closures +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) + } + + c.SubAmount(total) + + return nil +} + +func (self *StateObject) SetGasPool(gasLimit *big.Int) { + self.gasPool = new(big.Int).Set(gasLimit) + + statelogger.DebugDetailf("%x: fuel (+ %v)", self.Address(), self.gasPool) +} + +func (self *StateObject) BuyGas(gas, price *big.Int) error { + if self.gasPool.Cmp(gas) < 0 { + return GasLimitError(self.gasPool, gas) + } + + rGas := new(big.Int).Set(gas) + rGas.Mul(rGas, price) + + self.AddAmount(rGas) + + return nil +} + +func (self *StateObject) RefundGas(gas, price *big.Int) { + self.gasPool.Add(self.gasPool, gas) + + rGas := new(big.Int).Set(gas) + rGas.Mul(rGas, price) + + self.Amount.Sub(self.Amount, rGas) +} + +func (self *StateObject) Copy() *StateObject { + stateObject := NewStateObject(self.Address()) + stateObject.Amount.Set(self.Amount) + stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash) + stateObject.Nonce = self.Nonce + if self.state != nil { + stateObject.state = self.state.Copy() + } + stateObject.Code = ethutil.CopyBytes(self.Code) + stateObject.initCode = ethutil.CopyBytes(self.initCode) + stateObject.storage = self.storage.Copy() + stateObject.gasPool.Set(self.gasPool) + + return stateObject +} + +func (self *StateObject) Set(stateObject *StateObject) { + *self = *stateObject +} + +// +// Attribute accessors +// + +func (c *StateObject) State() *State { + return c.state +} + +func (c *StateObject) N() *big.Int { + return big.NewInt(int64(c.Nonce)) +} + +// Returns the address of the contract/account +func (c *StateObject) Address() []byte { + return c.address +} + +// Returns the initialization Code +func (c *StateObject) Init() Code { + return c.initCode +} + +// Debug stuff +func (self *StateObject) CreateOutputForDiff() { + fmt.Printf("%x %x %x %x\n", self.Address(), self.state.Root(), self.Amount.Bytes(), self.Nonce) + self.EachStorage(func(addr string, value *ethutil.Value) { + fmt.Printf("%x %x\n", addr, value.Bytes()) + }) +} + +// +// Encoding +// + +// State object encoding methods +func (c *StateObject) RlpEncode() []byte { + var root interface{} + if c.state != nil { + root = c.state.trie.Root + } else { + root = "" + } + + return ethutil.Encode([]interface{}{c.Nonce, c.Amount, 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.storage = make(map[string]*ethutil.Value) + c.gasPool = new(big.Int) + + c.CodeHash = decoder.Get(3).Bytes() + + c.Code, _ = ethutil.Config.Db.Get(c.CodeHash) +} + +// Storage change object. Used by the manifest for notifying changes to +// the sub channels. +type StorageState struct { + StateAddress []byte + Address []byte + Value *big.Int +} diff --git a/ethstate/state_test.go b/ethstate/state_test.go new file mode 100644 index 0000000000..cd13e80bce --- /dev/null +++ b/ethstate/state_test.go @@ -0,0 +1,35 @@ +package ethstate + +import ( + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethtrie" + "github.com/ethereum/eth-go/ethutil" + "testing" +) + +var ZeroHash256 = make([]byte, 32) + +func TestSnapshot(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") + ethutil.Config.Db = db + + state := NewState(ethtrie.NewTrie(db, "")) + + stateObject := state.GetOrNewStateObject([]byte("aa")) + + stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42)) + + snapshot := state.Copy() + + stateObject = state.GetStateObject([]byte("aa")) + stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(43)) + + state.Set(snapshot) + + stateObject = state.GetStateObject([]byte("aa")) + res := stateObject.GetStorage(ethutil.Big("0")) + if !res.Cmp(ethutil.NewValue(42)) { + t.Error("Expected storage 0 to be 42", res) + } +} diff --git a/ethvm/.ethtest b/ethvm/.ethtest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ethvm/asm.go b/ethvm/asm.go new file mode 100644 index 0000000000..fd559d1dc8 --- /dev/null +++ b/ethvm/asm.go @@ -0,0 +1,44 @@ +package ethvm + +import ( + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := OpCode(val) + + asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) + + switch op { + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + pc.Add(pc, ethutil.Big1) + a := int64(op) - int64(PUSH1) + 1 + if int(pc.Int64()+a) > len(script) { + return nil + } + + data := script[pc.Int64() : pc.Int64()+a] + if len(data) == 0 { + data = []byte{0} + } + asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) + + pc.Add(pc, big.NewInt(a-1)) + } + + pc.Add(pc, ethutil.Big1) + } + + return +} diff --git a/ethvm/closure.go b/ethvm/closure.go new file mode 100644 index 0000000000..505fd43fb9 --- /dev/null +++ b/ethvm/closure.go @@ -0,0 +1,116 @@ +package ethvm + +// TODO Re write VM to use values instead of big integers? + +import ( + "github.com/ethereum/eth-go/ethstate" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type ClosureRef interface { + ReturnGas(*big.Int, *big.Int) + Address() []byte + GetStorage(*big.Int) *ethutil.Value + SetStorage(*big.Int, *ethutil.Value) +} + +// Basic inline closure object which implement the 'closure' interface +type Closure struct { + caller ClosureRef + object *ethstate.StateObject + Code []byte + + Gas, UsedGas, Price *big.Int + + Args []byte +} + +// 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} + + // Gas should be a pointer so it can safely be reduced through the run + // This pointer will be off the state transition + c.Gas = gas //new(big.Int).Set(gas) + // In most cases price and value are pointers to transaction objects + // and we don't want the transaction's values to change. + c.Price = new(big.Int).Set(price) + c.UsedGas = new(big.Int) + + return c +} + +// Retuns the x element in data slice +func (c *Closure) GetStorage(x *big.Int) *ethutil.Value { + m := c.object.GetStorage(x) + if m == nil { + return ethutil.EmptyValue() + } + + return m +} + +func (c *Closure) Get(x *big.Int) *ethutil.Value { + return c.Gets(x, big.NewInt(1)) +} + +func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { + if x.Int64() >= int64(len(c.Code)) || y.Int64() >= int64(len(c.Code)) { + return ethutil.NewValue(0) + } + + partial := c.Code[x.Int64() : x.Int64()+y.Int64()] + + return ethutil.NewValue(partial) +} + +func (c *Closure) SetStorage(x *big.Int, val *ethutil.Value) { + c.object.SetStorage(x, val) +} + +func (c *Closure) Address() []byte { + return c.object.Address() +} + +func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) { + c.Args = args + + ret, err := vm.RunClosure(c) + + return ret, c.UsedGas, err +} + +func (c *Closure) Return(ret []byte) []byte { + // Return the remaining gas to the caller + c.caller.ReturnGas(c.Gas, c.Price) + + return ret +} + +func (c *Closure) UseGas(gas *big.Int) bool { + if c.Gas.Cmp(gas) < 0 { + return false + } + + // Sub the amount of gas from the remaining + c.Gas.Sub(c.Gas, gas) + c.UsedGas.Add(c.UsedGas, gas) + + return true +} + +// Implement the caller interface +func (c *Closure) ReturnGas(gas, price *big.Int) { + // Return the gas to the closure + c.Gas.Add(c.Gas, gas) + c.UsedGas.Sub(c.UsedGas, gas) +} + +func (c *Closure) Object() *ethstate.StateObject { + return c.object +} + +func (c *Closure) Caller() ClosureRef { + return c.caller +} diff --git a/ethvm/common.go b/ethvm/common.go new file mode 100644 index 0000000000..5a3aec648c --- /dev/null +++ b/ethvm/common.go @@ -0,0 +1,27 @@ +package ethvm + +import ( + "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +var vmlogger = ethlog.NewLogger("VM") + +var ( + GasStep = big.NewInt(1) + GasSha = big.NewInt(20) + GasSLoad = big.NewInt(20) + GasSStore = big.NewInt(100) + GasBalance = big.NewInt(20) + GasCreate = big.NewInt(100) + GasCall = big.NewInt(20) + GasMemory = big.NewInt(1) + GasData = big.NewInt(5) + GasTx = big.NewInt(500) + + Pow256 = ethutil.BigPow(2, 256) + + LogTyPretty byte = 0x1 + LogTyDiff byte = 0x2 +) diff --git a/ethvm/stack.go b/ethvm/stack.go new file mode 100644 index 0000000000..c06d636529 --- /dev/null +++ b/ethvm/stack.go @@ -0,0 +1,150 @@ +package ethvm + +import ( + "fmt" + "math" + "math/big" +) + +type OpType int + +const ( + tNorm = iota + tData + tExtro + tCrypto +) + +type TxCallback func(opType OpType) bool + +// Simple push/pop stack mechanism +type Stack struct { + data []*big.Int +} + +func NewStack() *Stack { + return &Stack{} +} + +func (st *Stack) Data() []*big.Int { + return st.data +} + +func (st *Stack) Len() int { + return len(st.data) +} + +func (st *Stack) Pop() *big.Int { + str := st.data[len(st.data)-1] + + copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1]) + st.data = st.data[:len(st.data)-1] + + return str +} + +func (st *Stack) Popn() (*big.Int, *big.Int) { + ints := st.data[len(st.data)-2:] + + copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2]) + st.data = st.data[:len(st.data)-2] + + return ints[0], ints[1] +} + +func (st *Stack) Peek() *big.Int { + str := st.data[len(st.data)-1] + + return str +} + +func (st *Stack) Peekn() (*big.Int, *big.Int) { + ints := st.data[:2] + + return ints[0], ints[1] +} + +func (st *Stack) Push(d *big.Int) { + st.data = append(st.data, new(big.Int).Set(d)) +} + +func (st *Stack) Get(amount *big.Int) []*big.Int { + // offset + size <= len(data) + length := big.NewInt(int64(len(st.data))) + if amount.Cmp(length) <= 0 { + start := new(big.Int).Sub(length, amount) + return st.data[start.Int64():length.Int64()] + } + + return nil +} + +func (st *Stack) Print() { + fmt.Println("### stack ###") + if len(st.data) > 0 { + for i, val := range st.data { + fmt.Printf("%-3d %v\n", i, val) + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("#############") +} + +type Memory struct { + store []byte +} + +func (m *Memory) Set(offset, size int64, value []byte) { + totSize := offset + size + lenSize := int64(len(m.store) - 1) + if totSize > lenSize { + // Calculate the diff between the sizes + diff := totSize - lenSize + if diff > 0 { + // Create a new empty slice and append it + newSlice := make([]byte, diff-1) + // Resize slice + m.store = append(m.store, newSlice...) + } + } + copy(m.store[offset:offset+size], value) +} + +func (m *Memory) Resize(size uint64) { + if uint64(m.Len()) < size { + m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) + } +} + +func (m *Memory) Get(offset, size int64) []byte { + if len(m.store) > int(offset) { + end := int(math.Min(float64(len(m.store)), float64(offset+size))) + + return m.store[offset:end] + } + + return nil +} + +func (m *Memory) Len() int { + return len(m.store) +} + +func (m *Memory) Data() []byte { + return m.store +} + +func (m *Memory) Print() { + fmt.Printf("### mem %d bytes ###\n", len(m.store)) + if len(m.store) > 0 { + addr := 0 + for i := 0; i+32 <= len(m.store); i += 32 { + fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) + addr++ + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("####################") +} diff --git a/ethvm/types.go b/ethvm/types.go new file mode 100644 index 0000000000..1c3f66139c --- /dev/null +++ b/ethvm/types.go @@ -0,0 +1,346 @@ +package ethvm + +import ( + "fmt" +) + +type OpCode int + +// Op codes +const ( + // 0x0 range - arithmetic ops + STOP = 0x00 + ADD = 0x01 + MUL = 0x02 + SUB = 0x03 + DIV = 0x04 + SDIV = 0x05 + MOD = 0x06 + SMOD = 0x07 + EXP = 0x08 + NEG = 0x09 + LT = 0x0a + GT = 0x0b + SLT = 0x0c + SGT = 0x0d + EQ = 0x0e + NOT = 0x0f + + // 0x10 range - bit ops + AND = 0x10 + OR = 0x11 + XOR = 0x12 + BYTE = 0x13 + + // 0x20 range - crypto + SHA3 = 0x20 + + // 0x30 range - closure state + ADDRESS = 0x30 + BALANCE = 0x31 + ORIGIN = 0x32 + CALLER = 0x33 + CALLVALUE = 0x34 + CALLDATALOAD = 0x35 + CALLDATASIZE = 0x36 + CALLDATACOPY = 0x37 + CODESIZE = 0x38 + CODECOPY = 0x39 + GASPRICE = 0x3a + + // 0x40 range - block operations + PREVHASH = 0x40 + COINBASE = 0x41 + TIMESTAMP = 0x42 + NUMBER = 0x43 + DIFFICULTY = 0x44 + GASLIMIT = 0x45 + + // 0x50 range - 'storage' and execution + POP = 0x50 + DUP = 0x51 + SWAP = 0x52 + MLOAD = 0x53 + MSTORE = 0x54 + MSTORE8 = 0x55 + SLOAD = 0x56 + SSTORE = 0x57 + JUMP = 0x58 + JUMPI = 0x59 + PC = 0x5a + MSIZE = 0x5b + GAS = 0x5c + + // 0x60 range + PUSH1 = 0x60 + PUSH2 = 0x61 + PUSH3 = 0x62 + PUSH4 = 0x63 + PUSH5 = 0x64 + PUSH6 = 0x65 + PUSH7 = 0x66 + PUSH8 = 0x67 + PUSH9 = 0x68 + PUSH10 = 0x69 + PUSH11 = 0x6a + PUSH12 = 0x6b + PUSH13 = 0x6c + PUSH14 = 0x6d + PUSH15 = 0x6e + PUSH16 = 0x6f + PUSH17 = 0x70 + PUSH18 = 0x71 + PUSH19 = 0x72 + PUSH20 = 0x73 + PUSH21 = 0x74 + PUSH22 = 0x75 + PUSH23 = 0x76 + PUSH24 = 0x77 + PUSH25 = 0x78 + PUSH26 = 0x79 + PUSH27 = 0x7a + PUSH28 = 0x7b + PUSH29 = 0x7c + PUSH30 = 0x7d + PUSH31 = 0x7e + PUSH32 = 0x7f + + // 0xf0 range - closures + CREATE = 0xf0 + CALL = 0xf1 + RETURN = 0xf2 + + // 0x70 range - other + LOG = 0xfe // XXX Unofficial + SUICIDE = 0xff +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + // 0x0 range - arithmetic ops + STOP: "STOP", + ADD: "ADD", + MUL: "MUL", + SUB: "SUB", + DIV: "DIV", + SDIV: "SDIV", + MOD: "MOD", + SMOD: "SMOD", + EXP: "EXP", + NEG: "NEG", + LT: "LT", + GT: "GT", + SLT: "SLT", + SGT: "SGT", + EQ: "EQ", + NOT: "NOT", + + // 0x10 range - bit ops + AND: "AND", + OR: "OR", + XOR: "XOR", + BYTE: "BYTE", + + // 0x20 range - crypto + SHA3: "SHA3", + + // 0x30 range - closure state + ADDRESS: "ADDRESS", + BALANCE: "BALANCE", + ORIGIN: "ORIGIN", + CALLER: "CALLER", + CALLVALUE: "CALLVALUE", + CALLDATALOAD: "CALLDATALOAD", + CALLDATASIZE: "CALLDATASIZE", + CALLDATACOPY: "CALLDATACOPY", + CODESIZE: "CODESIZE", + CODECOPY: "CODECOPY", + GASPRICE: "TXGASPRICE", + + // 0x40 range - block operations + PREVHASH: "PREVHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + + // 0x50 range - 'storage' and execution + POP: "POP", + DUP: "DUP", + SWAP: "SWAP", + MLOAD: "MLOAD", + MSTORE: "MSTORE", + MSTORE8: "MSTORE8", + SLOAD: "SLOAD", + SSTORE: "SSTORE", + JUMP: "JUMP", + JUMPI: "JUMPI", + PC: "PC", + MSIZE: "MSIZE", + GAS: "GAS", + + // 0x60 range - push + PUSH1: "PUSH1", + PUSH2: "PUSH2", + PUSH3: "PUSH3", + PUSH4: "PUSH4", + PUSH5: "PUSH5", + PUSH6: "PUSH6", + PUSH7: "PUSH7", + PUSH8: "PUSH8", + PUSH9: "PUSH9", + PUSH10: "PUSH10", + PUSH11: "PUSH11", + PUSH12: "PUSH12", + PUSH13: "PUSH13", + PUSH14: "PUSH14", + PUSH15: "PUSH15", + PUSH16: "PUSH16", + PUSH17: "PUSH17", + PUSH18: "PUSH18", + PUSH19: "PUSH19", + PUSH20: "PUSH20", + PUSH21: "PUSH21", + PUSH22: "PUSH22", + PUSH23: "PUSH23", + PUSH24: "PUSH24", + PUSH25: "PUSH25", + PUSH26: "PUSH26", + PUSH27: "PUSH27", + PUSH28: "PUSH28", + PUSH29: "PUSH29", + PUSH30: "PUSH30", + PUSH31: "PUSH31", + PUSH32: "PUSH32", + + // 0xf0 range + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + + // 0x70 range - other + LOG: "LOG", + SUICIDE: "SUICIDE", +} + +func (o OpCode) String() string { + str := opCodeToString[o] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode 0x%x", int(o)) + } + + return str +} + +// Op codes for assembling +var OpCodes = map[string]byte{ + // 0x0 range - arithmetic ops + "STOP": 0x00, + "ADD": 0x01, + "MUL": 0x02, + "SUB": 0x03, + "DIV": 0x04, + "SDIV": 0x05, + "MOD": 0x06, + "SMOD": 0x07, + "EXP": 0x08, + "NEG": 0x09, + "LT": 0x0a, + "GT": 0x0b, + "EQ": 0x0c, + "NOT": 0x0d, + + // 0x10 range - bit ops + "AND": 0x10, + "OR": 0x11, + "XOR": 0x12, + "BYTE": 0x13, + + // 0x20 range - crypto + "SHA3": 0x20, + + // 0x30 range - closure state + "ADDRESS": 0x30, + "BALANCE": 0x31, + "ORIGIN": 0x32, + "CALLER": 0x33, + "CALLVALUE": 0x34, + "CALLDATALOAD": 0x35, + "CALLDATASIZE": 0x36, + "GASPRICE": 0x38, + + // 0x40 range - block operations + "PREVHASH": 0x40, + "COINBASE": 0x41, + "TIMESTAMP": 0x42, + "NUMBER": 0x43, + "DIFFICULTY": 0x44, + "GASLIMIT": 0x45, + + // 0x50 range - 'storage' and execution + "POP": 0x51, + "DUP": 0x52, + "SWAP": 0x53, + "MLOAD": 0x54, + "MSTORE": 0x55, + "MSTORE8": 0x56, + "SLOAD": 0x57, + "SSTORE": 0x58, + "JUMP": 0x59, + "JUMPI": 0x5a, + "PC": 0x5b, + "MSIZE": 0x5c, + + // 0x70 range - 'push' + "PUSH1": 0x60, + "PUSH2": 0x61, + "PUSH3": 0x62, + "PUSH4": 0x63, + "PUSH5": 0x64, + "PUSH6": 0x65, + "PUSH7": 0x66, + "PUSH8": 0x67, + "PUSH9": 0x68, + "PUSH10": 0x69, + "PUSH11": 0x6a, + "PUSH12": 0x6b, + "PUSH13": 0x6c, + "PUSH14": 0x6d, + "PUSH15": 0x6e, + "PUSH16": 0x6f, + "PUSH17": 0x70, + "PUSH18": 0x71, + "PUSH19": 0x72, + "PUSH20": 0x73, + "PUSH21": 0x74, + "PUSH22": 0x75, + "PUSH23": 0x76, + "PUSH24": 0x77, + "PUSH25": 0x78, + "PUSH26": 0x70, + "PUSH27": 0x7a, + "PUSH28": 0x7b, + "PUSH29": 0x7c, + "PUSH30": 0x7d, + "PUSH31": 0x7e, + "PUSH32": 0x7f, + + // 0xf0 range - closures + "CREATE": 0xf0, + "CALL": 0xf1, + "RETURN": 0xf2, + + // 0x70 range - other + "LOG": 0xfe, + "SUICIDE": 0x7f, +} + +func IsOpCode(s string) bool { + for key, _ := range OpCodes { + if key == s { + return true + } + } + return false +} diff --git a/ethvm/vm.go b/ethvm/vm.go new file mode 100644 index 0000000000..2a83bae3d4 --- /dev/null +++ b/ethvm/vm.go @@ -0,0 +1,848 @@ +package ethvm + +import ( + "fmt" + "github.com/ethereum/eth-go/ethcrypto" + "github.com/ethereum/eth-go/ethstate" + "github.com/ethereum/eth-go/ethutil" + "math" + "math/big" +) + +type Debugger interface { + BreakHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool + StepHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool + BreakPoints() []int64 + SetCode(byteCode []byte) +} + +type Vm struct { + // Stack for processing contracts + stack *Stack + // non-persistent key/value memory storage + mem map[string]*big.Int + + env Environment + + Verbose bool + + logTy byte + logStr string + + err error + + // Debugging + Dbg Debugger + + BreakPoints []int64 + Stepping bool + Fn string +} + +type Environment interface { + State() *ethstate.State + + Origin() []byte + BlockNumber() *big.Int + PrevHash() []byte + Coinbase() []byte + Time() int64 + Difficulty() *big.Int + Data() []string + Value() *big.Int +} + +type Object interface { + GetStorage(key *big.Int) *ethutil.Value + SetStorage(key *big.Int, value *ethutil.Value) +} + +func New(env Environment) *Vm { + lt := LogTyPretty + if ethutil.Config.Diff { + lt = LogTyDiff + } + + return &Vm{env: env, logTy: lt} +} + +func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { + // Recover from any require exception + defer func() { + if r := recover(); r != nil { + ret = closure.Return(nil) + err = fmt.Errorf("%v", r) + vmlogger.Errorln("vm err", err) + } + }() + + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(closure.Code) + } + + // Don't bother with the execution if there's no code. + if len(closure.Code) == 0 { + return closure.Return(nil), nil + } + + vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", self.Fn, closure.Address(), closure.Gas, closure.Args) + + var ( + op OpCode + + mem = &Memory{} + stack = NewStack() + pc = big.NewInt(0) + step = 0 + prevStep = 0 + require = func(m int) { + if stack.Len() < m { + panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m)) + } + } + ) + + for { + prevStep = step + // The base for all big integer arithmetic + base := new(big.Int) + + step++ + // Get the memory location of pc + val := closure.Get(pc) + // Get the opcode (it must be an opcode!) + op = OpCode(val.Uint()) + + // XXX Leave this Println intact. Don't change this to the log system. + // Used for creating diffs between implementations + if self.logTy == LogTyDiff { + /* + switch op { + case STOP, RETURN, SUICIDE: + closure.object.EachStorage(func(key string, value *ethutil.Value) { + value.Decode() + 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()) + */ + } + + gas := new(big.Int) + addStepGasUsage := func(amount *big.Int) { + if amount.Cmp(ethutil.Big0) >= 0 { + gas.Add(gas, amount) + } + } + + addStepGasUsage(GasStep) + + var newMemSize uint64 = 0 + switch op { + case STOP: + gas.Set(ethutil.Big0) + case SUICIDE: + gas.Set(ethutil.Big0) + case SLOAD: + gas.Set(GasSLoad) + case SSTORE: + var mult *big.Int + y, x := stack.Peekn() + val := closure.GetStorage(x) + if val.BigInt().Cmp(ethutil.Big0) == 0 && len(y.Bytes()) > 0 { + mult = ethutil.Big2 + } else if val.BigInt().Cmp(ethutil.Big0) != 0 && len(y.Bytes()) == 0 { + mult = ethutil.Big0 + } else { + mult = ethutil.Big1 + } + gas = new(big.Int).Mul(mult, GasSStore) + case BALANCE: + gas.Set(GasBalance) + case MSTORE: + require(2) + newMemSize = stack.Peek().Uint64() + 32 + case MLOAD: + require(1) + + newMemSize = stack.Peek().Uint64() + 32 + case MSTORE8: + require(2) + newMemSize = stack.Peek().Uint64() + 1 + case RETURN: + require(2) + + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() + case SHA3: + require(2) + + gas.Set(GasSha) + + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() + case CALLDATACOPY: + require(3) + + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() + case CODECOPY: + require(3) + + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() + case CALL: + require(7) + gas.Set(GasCall) + addStepGasUsage(stack.data[stack.Len()-1]) + + x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64() + y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64() + + newMemSize = uint64(math.Max(float64(x), float64(y))) + case CREATE: + require(3) + gas.Set(GasCreate) + + newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() + } + + newMemSize = (newMemSize + 31) / 32 * 32 + if newMemSize > uint64(mem.Len()) { + m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 + addStepGasUsage(big.NewInt(int64(m))) + } + + if !closure.UseGas(gas) { + err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas) + + closure.UseGas(closure.Gas) + + return closure.Return(nil), err + } + + self.Printf("(pc) %-3d -o- %-14s", pc, op.String()) + self.Printf(" (g) %-3v (%v)", gas, closure.Gas) + + mem.Resize(newMemSize) + + switch op { + case LOG: + stack.Print() + mem.Print() + // 0x20 range + case ADD: + require(2) + x, y := stack.Popn() + self.Printf(" %v + %v", y, x) + + base.Add(y, x) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case SUB: + require(2) + x, y := stack.Popn() + self.Printf(" %v - %v", y, x) + + base.Sub(y, x) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case MUL: + require(2) + x, y := stack.Popn() + self.Printf(" %v * %v", y, x) + + base.Mul(y, x) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case DIV: + require(2) + x, y := stack.Popn() + self.Printf(" %v / %v", y, x) + + base.Div(y, x) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) + case SDIV: + require(2) + x, y := stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Div(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + stack.Push(z) + case MOD: + require(2) + x, y := stack.Popn() + + self.Printf(" %v %% %v", y, x) + + base.Mod(y, x) + + self.Printf(" = %v", base) + stack.Push(base) + case SMOD: + require(2) + x, y := stack.Popn() + // n > 2**255 + if x.Cmp(Pow256) > 0 { + x.Sub(Pow256, x) + } + if y.Cmp(Pow256) > 0 { + y.Sub(Pow256, y) + } + z := new(big.Int) + z.Mod(x, y) + if z.Cmp(Pow256) > 0 { + z.Sub(Pow256, z) + } + // Push result on to the stack + stack.Push(z) + case EXP: + require(2) + x, y := stack.Popn() + + self.Printf(" %v ** %v", y, x) + + base.Exp(y, x, Pow256) + + self.Printf(" = %v", base) + + stack.Push(base) + case NEG: + require(1) + base.Sub(Pow256, stack.Pop()) + stack.Push(base) + case LT: + require(2) + x, y := stack.Popn() + self.Printf(" %v < %v", y, x) + // x < y + if y.Cmp(x) < 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case GT: + require(2) + x, y := stack.Popn() + self.Printf(" %v > %v", y, x) + + // x > y + if y.Cmp(x) > 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + + case SLT: + require(2) + x, y := stack.Popn() + self.Printf(" %v < %v", y, x) + // x < y + if y.Cmp(x) < 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case SGT: + require(2) + x, y := stack.Popn() + self.Printf(" %v > %v", y, x) + + // x > y + if y.Cmp(x) > 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + + case EQ: + require(2) + x, y := stack.Popn() + self.Printf(" %v == %v", y, x) + + // x == y + if x.Cmp(y) == 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case NOT: + require(1) + x := stack.Pop() + if x.Cmp(ethutil.BigFalse) > 0 { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) + } + + // 0x10 range + case AND: + require(2) + x, y := stack.Popn() + self.Printf(" %v & %v", y, x) + + stack.Push(base.And(y, x)) + case OR: + require(2) + x, y := stack.Popn() + self.Printf(" %v | %v", y, x) + + stack.Push(base.Or(y, x)) + case XOR: + require(2) + x, y := stack.Popn() + self.Printf(" %v ^ %v", y, x) + + stack.Push(base.Xor(y, x)) + case BYTE: + require(2) + val, th := stack.Popn() + if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 { + byt := big.NewInt(int64(val.Bytes()[th.Int64()])) + stack.Push(byt) + + self.Printf(" => 0x%x", byt.Bytes()) + } else { + stack.Push(ethutil.BigFalse) + } + + // 0x20 range + case SHA3: + require(2) + size, offset := stack.Popn() + data := ethcrypto.Sha3Bin(mem.Get(offset.Int64(), size.Int64())) + + stack.Push(ethutil.BigD(data)) + + self.Printf(" => %x", data) + // 0x30 range + case ADDRESS: + stack.Push(ethutil.BigD(closure.Address())) + + self.Printf(" => %x", closure.Address()) + case BALANCE: + require(1) + + addr := stack.Pop().Bytes() + balance := self.env.State().GetBalance(addr) + + stack.Push(balance) + + self.Printf(" => %v (%x)", balance, addr) + case ORIGIN: + origin := self.env.Origin() + + stack.Push(ethutil.BigD(origin)) + + self.Printf(" => %x", origin) + case CALLER: + caller := closure.caller.Address() + stack.Push(ethutil.BigD(caller)) + + self.Printf(" => %x", caller) + case CALLVALUE: + value := self.env.Value() + + stack.Push(value) + + self.Printf(" => %v", value) + case CALLDATALOAD: + require(1) + var ( + offset = stack.Pop() + data = make([]byte, 32) + lenData = big.NewInt(int64(len(closure.Args))) + ) + + if lenData.Cmp(offset) >= 0 { + length := new(big.Int).Add(offset, ethutil.Big32) + length = ethutil.BigMin(length, lenData) + + copy(data, closure.Args[offset.Int64():length.Int64()]) + } + + self.Printf(" => 0x%x", data) + + stack.Push(ethutil.BigD(data)) + case CALLDATASIZE: + l := int64(len(closure.Args)) + stack.Push(big.NewInt(l)) + + self.Printf(" => %d", l) + case CALLDATACOPY: + var ( + size = int64(len(closure.Args)) + mOff = stack.Pop().Int64() + cOff = stack.Pop().Int64() + l = stack.Pop().Int64() + ) + + if cOff > size { + cOff = 0 + l = 0 + } else if cOff+l > size { + l = 0 + } + + code := closure.Args[cOff : cOff+l] + + mem.Set(mOff, l, code) + case CODESIZE: + l := big.NewInt(int64(len(closure.Code))) + stack.Push(l) + + self.Printf(" => %d", l) + case CODECOPY: + var ( + size = int64(len(closure.Code)) + mOff = stack.Pop().Int64() + cOff = stack.Pop().Int64() + l = stack.Pop().Int64() + ) + + if cOff > size { + cOff = 0 + l = 0 + } else if cOff+l > size { + l = 0 + } + + code := closure.Code[cOff : cOff+l] + //fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff) + + mem.Set(mOff, l, code) + //fmt.Println(Code(mem.Get(mOff, l))) + case GASPRICE: + stack.Push(closure.Price) + + self.Printf(" => %v", closure.Price) + + // 0x40 range + case PREVHASH: + prevHash := self.env.PrevHash() + + stack.Push(ethutil.BigD(prevHash)) + + self.Printf(" => 0x%x", prevHash) + case COINBASE: + coinbase := self.env.Coinbase() + + stack.Push(ethutil.BigD(coinbase)) + + self.Printf(" => 0x%x", coinbase) + case TIMESTAMP: + time := self.env.Time() + + stack.Push(big.NewInt(time)) + + self.Printf(" => 0x%x", time) + case NUMBER: + number := self.env.BlockNumber() + + stack.Push(number) + + self.Printf(" => 0x%x", number.Bytes()) + case DIFFICULTY: + difficulty := self.env.Difficulty() + + stack.Push(difficulty) + + self.Printf(" => 0x%x", difficulty.Bytes()) + case GASLIMIT: + // TODO + stack.Push(big.NewInt(0)) + + // 0x50 range + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + a := big.NewInt(int64(op) - int64(PUSH1) + 1) + pc.Add(pc, ethutil.Big1) + data := closure.Gets(pc, a) + val := ethutil.BigD(data.Bytes()) + // Push value to stack + stack.Push(val) + pc.Add(pc, a.Sub(a, big.NewInt(1))) + + step += int(op) - int(PUSH1) + 1 + + self.Printf(" => 0x%x", data.Bytes()) + case POP: + require(1) + stack.Pop() + case DUP: + require(1) + stack.Push(stack.Peek()) + + self.Printf(" => 0x%x", stack.Peek().Bytes()) + case SWAP: + require(2) + x, y := stack.Popn() + stack.Push(y) + stack.Push(x) + case MLOAD: + require(1) + offset := stack.Pop() + val := ethutil.BigD(mem.Get(offset.Int64(), 32)) + stack.Push(val) + + self.Printf(" => 0x%x", val.Bytes()) + case MSTORE: // Store the value at stack top-1 in to memory at location stack top + require(2) + // Pop value of the stack + val, mStart := stack.Popn() + mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) + + self.Printf(" => 0x%x", val) + case MSTORE8: + require(2) + val, mStart := stack.Popn() + //base.And(val, new(big.Int).SetInt64(0xff)) + //mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) + mem.store[mStart.Int64()] = byte(val.Int64() & 0xff) + + self.Printf(" => 0x%x", val) + case SLOAD: + require(1) + loc := stack.Pop() + val := closure.GetStorage(loc) + + stack.Push(val.BigInt()) + + self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) + case SSTORE: + require(2) + 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) + + self.Printf(" {0x%x : 0x%x}", loc, val) + case JUMP: + require(1) + pc = stack.Pop() + // Reduce pc by one because of the increment that's at the end of this for loop + self.Printf(" ~> %v", pc).Endl() + + continue + case JUMPI: + require(2) + cond, pos := stack.Popn() + if cond.Cmp(ethutil.BigTrue) >= 0 { + pc = pos + + self.Printf(" ~> %v (t)", pc).Endl() + + continue + } else { + self.Printf(" (f)") + } + case PC: + stack.Push(pc) + case MSIZE: + stack.Push(big.NewInt(int64(mem.Len()))) + case GAS: + stack.Push(closure.Gas) + // 0x60 range + case CREATE: + require(3) + + var ( + err error + value = stack.Pop() + size, offset = stack.Popn() + + // Snapshot the current stack so we are able to + // revert back to it later. + snapshot = self.env.State().Copy() + ) + + // Generate a new address + addr := ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce) + for i := uint64(0); self.env.State().GetStateObject(addr) != nil; i++ { + ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce+i) + } + closure.object.Nonce++ + + self.Printf(" (*) %x", addr).Endl() + + // Create a new contract + contract := self.env.State().NewStateObject(addr) + if contract.Amount.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) + // 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) + // 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) + } + + if err != nil { + stack.Push(ethutil.BigFalse) + + // Revert the state as it was before. + self.env.State().Set(snapshot) + + self.Printf("CREATE err %v", err) + } else { + stack.Push(ethutil.BigD(addr)) + self.Printf("CREATE success") + } + self.Endl() + + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(closure.Code) + } + case CALL: + require(7) + + self.Endl() + + gas := stack.Pop() + // Pop gas and value of the stack. + value, addr := stack.Popn() + // Pop input size and offset + inSize, inOffset := stack.Popn() + // Pop return size and offset + retSize, retOffset := stack.Popn() + + // 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) + + closure.ReturnGas(gas, nil) + + stack.Push(ethutil.BigFalse) + } else { + snapshot := self.env.State().Copy() + + stateObject := self.env.State().GetOrNewStateObject(addr.Bytes()) + + closure.object.SubAmount(value) + stateObject.AddAmount(value) + + // Create a new callable closure + c := NewClosure(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 { + stack.Push(ethutil.BigFalse) + + vmlogger.Debugf("Closure execution failed. %v\n", err) + + self.env.State().Set(snapshot) + } else { + stack.Push(ethutil.BigTrue) + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) + } + + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(closure.Code) + } + } + case RETURN: + require(2) + size, offset := stack.Popn() + ret := mem.Get(offset.Int64(), size.Int64()) + + self.Printf(" => (%d) 0x%x", len(ret), ret).Endl() + + return closure.Return(ret), nil + case SUICIDE: + require(1) + + receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes()) + + receiver.AddAmount(closure.object.Amount) + + closure.object.MarkForDeletion() + + fallthrough + case STOP: // Stop the closure + self.Endl() + + return closure.Return(nil), nil + default: + vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) + fmt.Println(ethstate.Code(closure.Code)) + + return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) + } + + pc.Add(pc, ethutil.Big1) + + self.Endl() + + if self.Dbg != nil { + for _, instrNo := range self.Dbg.BreakPoints() { + if pc.Cmp(big.NewInt(instrNo)) == 0 { + self.Stepping = true + + if !self.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) { + return nil, nil + } + } else if self.Stepping { + if !self.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) { + return nil, nil + } + } + } + } + + } +} + +func (self *Vm) Printf(format string, v ...interface{}) *Vm { + if self.Verbose && self.logTy == LogTyPretty { + self.logStr += fmt.Sprintf(format, v...) + } + + return self +} + +func (self *Vm) Endl() *Vm { + if self.Verbose && self.logTy == LogTyPretty { + vmlogger.Debugln(self.logStr) + self.logStr = "" + } + + return self +} diff --git a/ethvm/vm_test.go b/ethvm/vm_test.go new file mode 100644 index 0000000000..f0bdc8e2cf --- /dev/null +++ b/ethvm/vm_test.go @@ -0,0 +1,48 @@ +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" +) + +type TestEnv struct { +} + +func (self TestEnv) GetObject() Object { return nil } +func (self TestEnv) Origin() []byte { return nil } +func (self TestEnv) BlockNumber() *big.Int { return nil } +func (self TestEnv) PrevHash() []byte { return nil } +func (self TestEnv) Coinbase() []byte { return nil } +func (self TestEnv) Time() int64 { return 0 } +func (self TestEnv) Difficulty() *big.Int { return nil } +func (self TestEnv) Data() []string { return nil } +func (self TestEnv) Value() *big.Int { return nil } +func (self TestEnv) GetBalance(addr []byte) *big.Int { return nil } +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)) + + vm := New(TestEnv{}) + vm.Verbose = true + + ret, _, e := callerClosure.Call(vm, nil) + if e != nil { + fmt.Println("error", e) + } + fmt.Println(ret) +} From 490ca410c01a1b8076214d00c21d2edf09c24f86 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 22 Jul 2014 15:57:54 +0200 Subject: [PATCH 04/26] Minor improvements and fixes to the new vm structure --- ethchain/asm.go | 4 ++-- ethchain/vm.go | 5 +++-- ethstate/state_object.go | 2 +- ethvm/asm.go | 4 ++-- ethvm/vm.go | 8 ++------ ethvm/vm_test.go | 19 ++++++++----------- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/ethchain/asm.go b/ethchain/asm.go index 2697953fd8..9f99b0c48d 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -25,7 +25,7 @@ func Disassemble(script []byte) (asm []string) { pc.Add(pc, ethutil.Big1) a := int64(op) - int64(PUSH1) + 1 if int(pc.Int64()+a) > len(script) { - return nil + return } data := script[pc.Int64() : pc.Int64()+a] @@ -40,5 +40,5 @@ func Disassemble(script []byte) (asm []string) { pc.Add(pc, ethutil.Big1) } - return + return asm } diff --git a/ethchain/vm.go b/ethchain/vm.go index a9bed1eca9..4cbb4e1c4b 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -563,10 +563,9 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { } code := closure.Script[cOff : cOff+l] - //fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff) + fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff) mem.Set(mOff, l, code) - //fmt.Println(Code(mem.Get(mOff, l))) case GASPRICE: stack.Push(closure.Price) @@ -673,6 +672,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { stack.Push(pc) case MSIZE: stack.Push(big.NewInt(int64(mem.Len()))) + + vm.Printf(" => %v", mem.Len()).Endl() case GAS: stack.Push(closure.Gas) // 0x60 range diff --git a/ethstate/state_object.go b/ethstate/state_object.go index d8513f37d4..6b00c53698 100644 --- a/ethstate/state_object.go +++ b/ethstate/state_object.go @@ -11,7 +11,7 @@ import ( type Code []byte func (self Code) String() string { - return "" //strings.Join(Disassemble(self), " ") + return string(self) //strings.Join(Disassemble(self), " ") } type Storage map[string]*ethutil.Value diff --git a/ethvm/asm.go b/ethvm/asm.go index fd559d1dc8..7ff15a240d 100644 --- a/ethvm/asm.go +++ b/ethvm/asm.go @@ -18,7 +18,7 @@ func Disassemble(script []byte) (asm []string) { // Get the opcode (it must be an opcode!) op := OpCode(val) - asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) + asm = append(asm, fmt.Sprintf("%v", op)) switch op { case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: @@ -32,7 +32,7 @@ func Disassemble(script []byte) (asm []string) { if len(data) == 0 { data = []byte{0} } - asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) + asm = append(asm, fmt.Sprintf("0x%x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethvm/vm.go b/ethvm/vm.go index 2a83bae3d4..1f0ae89916 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -48,7 +48,6 @@ type Environment interface { Coinbase() []byte Time() int64 Difficulty() *big.Int - Data() []string Value() *big.Int } @@ -420,7 +419,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { require(2) val, th := stack.Popn() if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 { - byt := big.NewInt(int64(val.Bytes()[th.Int64()])) + byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) stack.Push(byt) self.Printf(" => 0x%x", byt.Bytes()) @@ -530,10 +529,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { } code := closure.Code[cOff : cOff+l] - //fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff) mem.Set(mOff, l, code) - //fmt.Println(Code(mem.Get(mOff, l))) case GASPRICE: stack.Push(closure.Price) @@ -638,7 +635,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // Add the change to manifest self.env.State().Manifest().AddStorageChange(closure.Object(), loc.Bytes(), val) - self.Printf(" {0x%x : 0x%x}", loc, val) + self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case JUMP: require(1) pc = stack.Pop() @@ -802,7 +799,6 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { return closure.Return(nil), nil default: vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) - fmt.Println(ethstate.Code(closure.Code)) return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) } diff --git a/ethvm/vm_test.go b/ethvm/vm_test.go index f0bdc8e2cf..501d0c2841 100644 --- a/ethvm/vm_test.go +++ b/ethvm/vm_test.go @@ -15,17 +15,14 @@ import ( type TestEnv struct { } -func (self TestEnv) GetObject() Object { return nil } -func (self TestEnv) Origin() []byte { return nil } -func (self TestEnv) BlockNumber() *big.Int { return nil } -func (self TestEnv) PrevHash() []byte { return nil } -func (self TestEnv) Coinbase() []byte { return nil } -func (self TestEnv) Time() int64 { return 0 } -func (self TestEnv) Difficulty() *big.Int { return nil } -func (self TestEnv) Data() []string { return nil } -func (self TestEnv) Value() *big.Int { return nil } -func (self TestEnv) GetBalance(addr []byte) *big.Int { return nil } -func (self TestEnv) State() *ethstate.State { return nil } +func (self TestEnv) Origin() []byte { return nil } +func (self TestEnv) BlockNumber() *big.Int { return nil } +func (self TestEnv) PrevHash() []byte { return nil } +func (self TestEnv) Coinbase() []byte { return nil } +func (self TestEnv) Time() int64 { return 0 } +func (self TestEnv) Difficulty() *big.Int { return nil } +func (self TestEnv) Value() *big.Int { return nil } +func (self TestEnv) State() *ethstate.State { return nil } func TestVm(t *testing.T) { ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4))) From 5d57b7847105639efe1945e1ff588b1116a11a54 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 22 Jul 2014 16:16:00 +0200 Subject: [PATCH 05/26] Added big int to uint switch --- ethutil/value.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ethutil/value.go b/ethutil/value.go index fba7426d13..735a71dbc9 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -68,6 +68,8 @@ func (val *Value) Uint() uint64 { return uint64(Val) } else if Val, ok := val.Val.([]byte); ok { return ReadVarint(bytes.NewReader(Val)) + } else if Val, ok := val.Val.(*big.Int); ok { + return Val.Uint64() } return 0 From 06ec80f39495bdd92878468cf862f52e9748f1ca Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Jul 2014 12:04:27 +0200 Subject: [PATCH 06/26] zero devision check --- ethchain/vm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 528088a493..07e9a0f2c7 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -306,7 +306,9 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { x, y := stack.Popn() vm.Printf(" %v / %v", y, x) - base.Div(y, x) + if x.Cmp(ethutil.Big0) != 0 { + base.Div(y, x) + } vm.Printf(" = %v", base) // Pop result back on the stack From 958b482ada677028e11698c219ed5b1e70b224e6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Jul 2014 12:36:47 +0200 Subject: [PATCH 07/26] div 0 err --- ethvm/vm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ethvm/vm.go b/ethvm/vm.go index 1f0ae89916..e159d8ff1b 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -268,7 +268,9 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { x, y := stack.Popn() self.Printf(" %v / %v", y, x) - base.Div(y, x) + if x.Cmp(ethutil.Big0) != 0 { + base.Div(y, x) + } self.Printf(" = %v", base) // Pop result back on the stack From 32d125131f602d63f66ee7eb09439074f0b94a91 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:04:15 +0200 Subject: [PATCH 08/26] Refactored to new state and vm --- README.md | 2 +- ethchain/block.go | 27 +- ethchain/block_chain.go | 4 +- ethchain/closure.go | 121 ----- ethchain/stack.go | 150 ------ ethchain/state.go | 250 ---------- ethchain/state_manager.go | 35 +- ethchain/state_object.go | 368 --------------- ethchain/state_object_test.go | 52 --- ethchain/state_test.go | 30 -- ethchain/state_transition.go | 95 ++-- ethchain/transaction_pool.go | 3 +- ethchain/vm.go | 843 ---------------------------------- ethchain/vm_env.go | 28 ++ ethchain/vm_test.go | 68 --- ethpub/pub.go | 5 +- ethpub/types.go | 15 +- ethstate/state.go | 52 +-- ethstate/state_object.go | 44 +- peer.go | 27 +- 20 files changed, 195 insertions(+), 2024 deletions(-) delete mode 100644 ethchain/closure.go delete mode 100644 ethchain/stack.go delete mode 100644 ethchain/state.go delete mode 100644 ethchain/state_object.go delete mode 100644 ethchain/state_object_test.go delete mode 100644 ethchain/state_test.go delete mode 100644 ethchain/vm.go create mode 100644 ethchain/vm_env.go delete mode 100644 ethchain/vm_test.go diff --git a/README.md b/README.md index 69c6eb0ad1..5b0f6e07fd 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.5.20". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 0.6.0". 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. diff --git a/ethchain/block.go b/ethchain/block.go index 447d55f994..437525e355 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "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" @@ -39,7 +40,7 @@ type Block struct { Coinbase []byte // Block Trie state //state *ethutil.Trie - state *State + state *ethstate.State // Difficulty for the current block Difficulty *big.Int // Creation time @@ -104,7 +105,7 @@ func CreateBlock(root interface{}, } block.SetUncles([]*Block{}) - block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, root)) + block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, root)) return block } @@ -116,12 +117,12 @@ func (block *Block) Hash() []byte { func (block *Block) HashNoNonce() []byte { return ethcrypto.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, - block.UncleSha, block.Coinbase, block.state.trie.Root, + block.UncleSha, block.Coinbase, block.state.Trie.Root, block.TxSha, block.Difficulty, block.Number, block.MinGasPrice, block.GasLimit, block.GasUsed, block.Time, block.Extra})) } -func (block *Block) State() *State { +func (block *Block) State() *ethstate.State { return block.state } @@ -140,17 +141,17 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { base := new(big.Int) contract.Amount = base.Sub(contract.Amount, fee) - block.state.trie.Update(string(addr), string(contract.RlpEncode())) + block.state.Trie.Update(string(addr), string(contract.RlpEncode())) - data := block.state.trie.Get(string(block.Coinbase)) + data := block.state.Trie.Get(string(block.Coinbase)) // Get the ether (Coinbase) and add the fee (gief fee to miner) - account := NewStateObjectFromBytes(block.Coinbase, []byte(data)) + 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.Trie.Update(string(block.Coinbase), string(ether.RlpEncode())) block.state.UpdateStateObject(account) return true @@ -312,7 +313,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 = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val)) + block.state = ethstate.NewState(ethtrie.NewTrie(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() @@ -354,7 +355,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 = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val)) + block.state = ethstate.NewState(ethtrie.NewTrie(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() @@ -369,7 +370,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block { } func (block *Block) GetRoot() interface{} { - return block.state.trie.Root + return block.state.Trie.Root } func (self *Block) Receipts() []*Receipt { @@ -385,7 +386,7 @@ func (block *Block) header() []interface{} { // Coinbase address block.Coinbase, // root state - block.state.trie.Root, + block.state.Trie.Root, // Sha of tx block.TxSha, // Current block Difficulty @@ -429,7 +430,7 @@ func (block *Block) String() string { block.PrevHash, block.UncleSha, block.Coinbase, - block.state.trie.Root, + block.state.Trie.Root, block.TxSha, block.Difficulty, block.Number, diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index f72a777066..1a26627871 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -44,7 +44,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block { hash := ZeroHash256 if bc.CurrentBlock != nil { - root = bc.CurrentBlock.state.trie.Root + root = bc.CurrentBlock.state.Trie.Root hash = bc.LastBlockHash lastBlockTime = bc.CurrentBlock.Time } @@ -297,7 +297,7 @@ func (bc *BlockChain) setLastBlock() { } else { AddTestNetFunds(bc.genesisBlock) - bc.genesisBlock.state.trie.Sync() + bc.genesisBlock.state.Trie.Sync() // Prepare the genesis block bc.Add(bc.genesisBlock) diff --git a/ethchain/closure.go b/ethchain/closure.go deleted file mode 100644 index 1f7f8d703c..0000000000 --- a/ethchain/closure.go +++ /dev/null @@ -1,121 +0,0 @@ -package ethchain - -// TODO Re write VM to use values instead of big integers? - -import ( - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -type ClosureRef interface { - ReturnGas(*big.Int, *big.Int, *State) - Address() []byte - GetStorage(*big.Int) *ethutil.Value - SetStorage(*big.Int, *ethutil.Value) - N() *big.Int -} - -// Basic inline closure object which implement the 'closure' interface -type Closure struct { - caller ClosureRef - object *StateObject - Script []byte - State *State - - Gas, UsedGas, Price *big.Int - - Args []byte -} - -// Create a new closure for the given data items -func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure { - c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil} - - // Gas should be a pointer so it can safely be reduced through the run - // This pointer will be off the state transition - c.Gas = gas //new(big.Int).Set(gas) - // In most cases price and value are pointers to transaction objects - // and we don't want the transaction's values to change. - c.Price = new(big.Int).Set(price) - c.UsedGas = new(big.Int) - - return c -} - -// Retuns the x element in data slice -func (c *Closure) GetStorage(x *big.Int) *ethutil.Value { - m := c.object.GetStorage(x) - if m == nil { - return ethutil.EmptyValue() - } - - return m -} - -func (c *Closure) Get(x *big.Int) *ethutil.Value { - return c.Gets(x, big.NewInt(1)) -} - -func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { - if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) { - return ethutil.NewValue(0) - } - - partial := c.Script[x.Int64() : x.Int64()+y.Int64()] - - return ethutil.NewValue(partial) -} - -func (c *Closure) SetStorage(x *big.Int, val *ethutil.Value) { - c.object.SetStorage(x, val) -} - -func (c *Closure) Address() []byte { - return c.object.Address() -} - -func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) { - c.Args = args - - ret, err := vm.RunClosure(c) - - return ret, c.UsedGas, err -} - -func (c *Closure) Return(ret []byte) []byte { - // Return the remaining gas to the caller - c.caller.ReturnGas(c.Gas, c.Price, c.State) - - return ret -} - -func (c *Closure) UseGas(gas *big.Int) bool { - if c.Gas.Cmp(gas) < 0 { - return false - } - - // Sub the amount of gas from the remaining - c.Gas.Sub(c.Gas, gas) - c.UsedGas.Add(c.UsedGas, gas) - - return true -} - -// Implement the caller interface -func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { - // Return the gas to the closure - c.Gas.Add(c.Gas, gas) - c.UsedGas.Sub(c.UsedGas, gas) -} - -func (c *Closure) Object() *StateObject { - return c.object -} - -func (c *Closure) Caller() ClosureRef { - return c.caller -} - -func (c *Closure) N() *big.Int { - return c.object.N() -} diff --git a/ethchain/stack.go b/ethchain/stack.go deleted file mode 100644 index a9fa2e5224..0000000000 --- a/ethchain/stack.go +++ /dev/null @@ -1,150 +0,0 @@ -package ethchain - -import ( - "fmt" - "math" - "math/big" -) - -type OpType int - -const ( - tNorm = iota - tData - tExtro - tCrypto -) - -type TxCallback func(opType OpType) bool - -// Simple push/pop stack mechanism -type Stack struct { - data []*big.Int -} - -func NewStack() *Stack { - return &Stack{} -} - -func (st *Stack) Data() []*big.Int { - return st.data -} - -func (st *Stack) Len() int { - return len(st.data) -} - -func (st *Stack) Pop() *big.Int { - str := st.data[len(st.data)-1] - - copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1]) - st.data = st.data[:len(st.data)-1] - - return str -} - -func (st *Stack) Popn() (*big.Int, *big.Int) { - ints := st.data[len(st.data)-2:] - - copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2]) - st.data = st.data[:len(st.data)-2] - - return ints[0], ints[1] -} - -func (st *Stack) Peek() *big.Int { - str := st.data[len(st.data)-1] - - return str -} - -func (st *Stack) Peekn() (*big.Int, *big.Int) { - ints := st.data[:2] - - return ints[0], ints[1] -} - -func (st *Stack) Push(d *big.Int) { - st.data = append(st.data, new(big.Int).Set(d)) -} - -func (st *Stack) Get(amount *big.Int) []*big.Int { - // offset + size <= len(data) - length := big.NewInt(int64(len(st.data))) - if amount.Cmp(length) <= 0 { - start := new(big.Int).Sub(length, amount) - return st.data[start.Int64():length.Int64()] - } - - return nil -} - -func (st *Stack) Print() { - fmt.Println("### stack ###") - if len(st.data) > 0 { - for i, val := range st.data { - fmt.Printf("%-3d %v\n", i, val) - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("#############") -} - -type Memory struct { - store []byte -} - -func (m *Memory) Set(offset, size int64, value []byte) { - totSize := offset + size - lenSize := int64(len(m.store) - 1) - if totSize > lenSize { - // Calculate the diff between the sizes - diff := totSize - lenSize - if diff > 0 { - // Create a new empty slice and append it - newSlice := make([]byte, diff-1) - // Resize slice - m.store = append(m.store, newSlice...) - } - } - copy(m.store[offset:offset+size], value) -} - -func (m *Memory) Resize(size uint64) { - if uint64(m.Len()) < size { - m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) - } -} - -func (m *Memory) Get(offset, size int64) []byte { - if len(m.store) > int(offset) { - end := int(math.Min(float64(len(m.store)), float64(offset+size))) - - return m.store[offset:end] - } - - return nil -} - -func (m *Memory) Len() int { - return len(m.store) -} - -func (m *Memory) Data() []byte { - return m.store -} - -func (m *Memory) Print() { - fmt.Printf("### mem %d bytes ###\n", len(m.store)) - if len(m.store) > 0 { - addr := 0 - for i := 0; i+32 <= len(m.store); i += 32 { - fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) - addr++ - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("####################") -} diff --git a/ethchain/state.go b/ethchain/state.go deleted file mode 100644 index 3a9929ecc5..0000000000 --- a/ethchain/state.go +++ /dev/null @@ -1,250 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethcrypto" - "github.com/ethereum/eth-go/ethtrie" - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -// States within the ethereum protocol are used to store anything -// within the merkle trie. States take care of caching and storing -// nested states. It's the general query interface to retrieve: -// * Contracts -// * Accounts -type State struct { - // The trie for this structure - trie *ethtrie.Trie - - stateObjects map[string]*StateObject - - manifest *Manifest -} - -// Create a new state from a given trie -func NewState(trie *ethtrie.Trie) *State { - return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} -} - -// Retrieve the balance from the given address or 0 if object not found -func (self *State) GetBalance(addr []byte) *big.Int { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - return stateObject.Amount - } - - return ethutil.Big0 -} - -func (self *State) GetNonce(addr []byte) uint64 { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - return stateObject.Nonce - } - - return 0 -} - -// -// Setting, updating & deleting state object methods -// - -// Update the given state object and apply it to state trie -func (self *State) UpdateStateObject(stateObject *StateObject) { - addr := stateObject.Address() - - ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script()) - - self.trie.Update(string(addr), string(stateObject.RlpEncode())) - - self.manifest.AddObjectChange(stateObject) -} - -// Delete the given state object and delete it from the state trie -func (self *State) DeleteStateObject(stateObject *StateObject) { - self.trie.Delete(string(stateObject.Address())) - - delete(self.stateObjects, string(stateObject.Address())) -} - -// Retrieve a state object given my the address. Nil if not found -func (self *State) GetStateObject(addr []byte) *StateObject { - addr = ethutil.Address(addr) - - stateObject := self.stateObjects[string(addr)] - if stateObject != nil { - return stateObject - } - - data := self.trie.Get(string(addr)) - if len(data) == 0 { - return nil - } - - stateObject = NewStateObjectFromBytes(addr, []byte(data)) - self.stateObjects[string(addr)] = stateObject - - return stateObject -} - -// Retrieve a state object or create a new state object if nil -func (self *State) GetOrNewStateObject(addr []byte) *StateObject { - stateObject := self.GetStateObject(addr) - if stateObject == nil { - stateObject = self.NewStateObject(addr) - } - - return stateObject -} - -// Create a state object whether it exist in the trie or not -func (self *State) NewStateObject(addr []byte) *StateObject { - statelogger.Infof("(+) %x\n", addr) - - stateObject := NewStateObject(addr) - self.stateObjects[string(addr)] = stateObject - - return stateObject -} - -// Deprecated -func (self *State) GetAccount(addr []byte) *StateObject { - return self.GetOrNewStateObject(addr) -} - -// -// Setting, copying of the state methods -// - -func (s *State) Cmp(other *State) bool { - return s.trie.Cmp(other.trie) -} - -func (self *State) Copy() *State { - if self.trie != nil { - state := NewState(self.trie.Copy()) - for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() - } - - return state - } - - return nil -} - -func (self *State) Set(state *State) { - if state == nil { - panic("Tried setting 'state' to nil through 'Set'") - } - - self.trie = state.trie - self.stateObjects = state.stateObjects -} - -func (s *State) Root() interface{} { - return s.trie.Root -} - -// Resets the trie and all siblings -func (s *State) Reset() { - s.trie.Undo() - - // Reset all nested states - for _, stateObject := range s.stateObjects { - if stateObject.state == nil { - continue - } - - //stateObject.state.Reset() - stateObject.Reset() - } - - s.Empty() -} - -// Syncs the trie and all siblings -func (s *State) Sync() { - // Sync all nested states - for _, stateObject := range s.stateObjects { - //s.UpdateStateObject(stateObject) - - if stateObject.state == nil { - continue - } - - stateObject.state.Sync() - } - - s.trie.Sync() - - s.Empty() -} - -func (self *State) Empty() { - self.stateObjects = make(map[string]*StateObject) -} - -func (self *State) Update() { - for _, stateObject := range self.stateObjects { - if stateObject.remove { - self.DeleteStateObject(stateObject) - } else { - stateObject.Sync() - - self.UpdateStateObject(stateObject) - } - } - - // FIXME trie delete is broken - valid, t2 := ethtrie.ParanoiaCheck(self.trie) - if !valid { - statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root) - - self.trie = t2 - } -} - -// Debug stuff -func (self *State) CreateOutputForDiff() { - for _, stateObject := range self.stateObjects { - stateObject.CreateOutputForDiff() - } -} - -// 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/ethchain/state_manager.go b/ethchain/state_manager.go index 8dc88d7c3a..9408cf3313 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethlog" - _ "github.com/ethereum/eth-go/ethtrie" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "math/big" @@ -50,8 +50,6 @@ type StateManager struct { mutex sync.Mutex // Canonical block chain bc *BlockChain - // Stack for processing contracts - stack *Stack // non-persistent key/value memory storage mem map[string]*big.Int // Proof of work used for validating @@ -62,10 +60,10 @@ type StateManager struct { // Transiently state. The trans state isn't ever saved, validated and // it could be used for setting account nonces without effecting // the main states. - transState *State + transState *ethstate.State // Mining state. The mining state is used purely and solely by the mining // operation. - miningState *State + miningState *ethstate.State // The last attempted block is mainly used for debugging purposes // This does not have to be a valid block and will be set during @@ -75,7 +73,6 @@ type StateManager struct { func NewStateManager(ethereum EthManager) *StateManager { sm := &StateManager{ - stack: NewStack(), mem: make(map[string]*big.Int), Pow: &EasyPow{}, Ethereum: ethereum, @@ -87,19 +84,19 @@ func NewStateManager(ethereum EthManager) *StateManager { return sm } -func (sm *StateManager) CurrentState() *State { +func (sm *StateManager) CurrentState() *ethstate.State { return sm.Ethereum.BlockChain().CurrentBlock.State() } -func (sm *StateManager) TransState() *State { +func (sm *StateManager) TransState() *ethstate.State { return sm.transState } -func (sm *StateManager) MiningState() *State { +func (sm *StateManager) MiningState() *ethstate.State { return sm.miningState } -func (sm *StateManager) NewMiningState() *State { +func (sm *StateManager) NewMiningState() *ethstate.State { sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy() return sm.miningState @@ -109,7 +106,7 @@ func (sm *StateManager) BlockChain() *BlockChain { return sm.bc } -func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { +func (self *StateManager) ProcessTransactions(coinbase *ethstate.StateObject, state *ethstate.State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { var ( receipts Receipts handled, unhandled Transactions @@ -225,7 +222,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { } if !block.State().Cmp(state) { - err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) + err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root) return } @@ -242,7 +239,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { if dontReact == false { sm.Ethereum.Reactor().Post("newBlock", block) - state.manifest.Reset() + state.Manifest().Reset() } sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) @@ -255,7 +252,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { return nil } -func (sm *StateManager) ApplyDiff(state *State, parent, block *Block) (receipts Receipts, err error) { +func (sm *StateManager) ApplyDiff(state *ethstate.State, parent, block *Block) (receipts Receipts, err error) { coinbase := state.GetOrNewStateObject(block.Coinbase) coinbase.SetGasPool(block.CalcGasLimit(parent)) @@ -340,7 +337,7 @@ func CalculateUncleReward(block *Block) *big.Int { return UncleReward } -func (sm *StateManager) AccumelateRewards(state *State, block *Block) error { +func (sm *StateManager) AccumelateRewards(state *ethstate.State, block *Block) error { // Get the account associated with the coinbase account := state.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address @@ -364,14 +361,14 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) notifyChanges(state *State) { - for addr, stateObject := range state.manifest.objectChanges { +func (sm *StateManager) notifyChanges(state *ethstate.State) { + for addr, stateObject := range state.Manifest().ObjectChanges { sm.Ethereum.Reactor().Post("object:"+addr, stateObject) } - for stateObjectAddr, mappedObjects := range state.manifest.storageChanges { + for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges { for addr, value := range mappedObjects { - sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value}) + sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, ðstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value}) } } } diff --git a/ethchain/state_object.go b/ethchain/state_object.go deleted file mode 100644 index 1846090155..0000000000 --- a/ethchain/state_object.go +++ /dev/null @@ -1,368 +0,0 @@ -package ethchain - -import ( - "fmt" - "github.com/ethereum/eth-go/ethcrypto" - "github.com/ethereum/eth-go/ethtrie" - "github.com/ethereum/eth-go/ethutil" - "math/big" - "strings" -) - -type Code []byte - -func (self Code) String() string { - return strings.Join(Disassemble(self), " ") -} - -type Storage map[string]*ethutil.Value - -func (self Storage) Copy() Storage { - cpy := make(Storage) - for key, value := range self { - // XXX Do we need a 'value' copy or is this sufficient? - cpy[key] = value - } - - return cpy -} - -type StateObject struct { - // Address of the object - address []byte - // Shared attributes - Amount *big.Int - ScriptHash []byte - Nonce uint64 - // Contract related attributes - state *State - script Code - initScript Code - - storage Storage - - // Total gas pool is the total amount of gas currently - // left if this object is the coinbase. Gas is directly - // purchased of the coinbase. - gasPool *big.Int - - // Mark for deletion - // When an object is marked for deletion it will be delete from the trie - // during the "update" phase of the state transition - remove bool -} - -func (self *StateObject) Reset() { - self.storage = make(Storage) - 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.initScript = 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.storage = make(Storage) - object.gasPool = new(big.Int) - - return object -} - -func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { - contract := NewStateObject(address) - contract.Amount = Amount - contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) - - return contract -} - -func NewStateObjectFromBytes(address, data []byte) *StateObject { - object := &StateObject{address: address} - object.RlpDecode(data) - - return object -} - -func (self *StateObject) MarkForDeletion() { - self.remove = true - statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount) -} - -func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { - return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) -} - -func (c *StateObject) SetAddr(addr []byte, value interface{}) { - c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) -} - -func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { - return self.getStorage(key.Bytes()) -} -func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) { - self.setStorage(key.Bytes(), value) -} - -func (self *StateObject) getStorage(k []byte) *ethutil.Value { - key := ethutil.LeftPadBytes(k, 32) - - value := self.storage[string(key)] - if value == nil { - value = self.GetAddr(key) - - if !value.IsNil() { - self.storage[string(key)] = value - } - } - - return value - - //return self.GetAddr(key) -} - -func (self *StateObject) setStorage(k []byte, value *ethutil.Value) { - key := ethutil.LeftPadBytes(k, 32) - self.storage[string(key)] = value.Copy() - - /* - if value.BigInt().Cmp(ethutil.Big0) == 0 { - self.state.trie.Delete(string(key)) - return - } - - self.SetAddr(key, value) - */ -} - -// Iterate over each storage address and yield callback -func (self *StateObject) EachStorage(cb ethtrie.EachCallback) { - // First loop over the uncommit/cached values in storage - for key, value := range self.storage { - // XXX Most iterators Fns as it stands require encoded values - encoded := ethutil.NewValue(value.Encode()) - cb(key, encoded) - } - - it := self.state.trie.NewIterator() - it.Each(func(key string, value *ethutil.Value) { - // If it's cached don't call the callback. - if self.storage[key] == nil { - cb(key, value) - } - }) -} - -func (self *StateObject) Sync() { - /* - fmt.Println("############# BEFORE ################") - self.state.EachStorage(func(key string, value *ethutil.Value) { - fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes()) - }) - fmt.Printf("%x @:%x\n", self.Address(), self.state.Root()) - fmt.Println("#####################################") - */ - for key, value := range self.storage { - if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 { - //data := self.getStorage([]byte(key)) - //fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data) - self.state.trie.Delete(string(key)) - continue - } - - self.SetAddr([]byte(key), value) - } - - valid, t2 := ethtrie.ParanoiaCheck(self.state.trie) - if !valid { - statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root) - - self.state.trie = t2 - } - - /* - fmt.Println("############# AFTER ################") - self.state.EachStorage(func(key string, value *ethutil.Value) { - fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes()) - }) - */ - //fmt.Printf("%x @:%x\n", self.Address(), self.state.Root()) -} - -func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { - if int64(len(c.script)-1) < pc.Int64() { - return ethutil.NewValue(0) - } - - return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) -} - -func (c *StateObject) AddAmount(amount *big.Int) { - c.SetAmount(new(big.Int).Add(c.Amount, amount)) - - statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount) -} - -func (c *StateObject) SubAmount(amount *big.Int) { - c.SetAmount(new(big.Int).Sub(c.Amount, amount)) - - statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) -} - -func (c *StateObject) SetAmount(amount *big.Int) { - c.Amount = amount -} - -// -// Gas setters and getters -// - -// Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {} -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) - } - - c.SubAmount(total) - - return nil -} - -func (self *StateObject) SetGasPool(gasLimit *big.Int) { - self.gasPool = new(big.Int).Set(gasLimit) - - statelogger.DebugDetailf("%x: fuel (+ %v)", self.Address(), self.gasPool) -} - -func (self *StateObject) BuyGas(gas, price *big.Int) error { - if self.gasPool.Cmp(gas) < 0 { - return GasLimitError(self.gasPool, gas) - } - - rGas := new(big.Int).Set(gas) - rGas.Mul(rGas, price) - - self.AddAmount(rGas) - - return nil -} - -func (self *StateObject) RefundGas(gas, price *big.Int) { - self.gasPool.Add(self.gasPool, gas) - - rGas := new(big.Int).Set(gas) - rGas.Mul(rGas, price) - - self.Amount.Sub(self.Amount, rGas) -} - -func (self *StateObject) Copy() *StateObject { - stateObject := NewStateObject(self.Address()) - stateObject.Amount.Set(self.Amount) - stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash) - stateObject.Nonce = self.Nonce - if self.state != nil { - stateObject.state = self.state.Copy() - } - stateObject.script = ethutil.CopyBytes(self.script) - stateObject.initScript = ethutil.CopyBytes(self.initScript) - stateObject.storage = self.storage.Copy() - stateObject.gasPool.Set(self.gasPool) - - return stateObject -} - -func (self *StateObject) Set(stateObject *StateObject) { - *self = *stateObject -} - -// -// Attribute accessors -// - -func (c *StateObject) State() *State { - return c.state -} - -func (c *StateObject) N() *big.Int { - return big.NewInt(int64(c.Nonce)) -} - -// Returns the address of the contract/account -func (c *StateObject) Address() []byte { - return c.address -} - -// Returns the main script body -func (c *StateObject) Script() Code { - return c.script -} - -// Returns the initialization script -func (c *StateObject) Init() Code { - return c.initScript -} - -// Debug stuff -func (self *StateObject) CreateOutputForDiff() { - fmt.Printf("%x %x %x %x\n", self.Address(), self.state.Root(), self.Amount.Bytes(), self.Nonce) - self.EachStorage(func(addr string, value *ethutil.Value) { - fmt.Printf("%x %x\n", addr, value.Bytes()) - }) -} - -// -// Encoding -// - -// State object encoding methods -func (c *StateObject) RlpEncode() []byte { - var root interface{} - if c.state != nil { - root = c.state.trie.Root - } else { - root = "" - } - - return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethcrypto.Sha3Bin(c.script)}) -} - -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.storage = make(map[string]*ethutil.Value) - c.gasPool = new(big.Int) - - c.ScriptHash = decoder.Get(3).Bytes() - - c.script, _ = ethutil.Config.Db.Get(c.ScriptHash) -} - -// Storage change object. Used by the manifest for notifying changes to -// the sub channels. -type StorageState struct { - StateAddress []byte - Address []byte - Value *big.Int -} diff --git a/ethchain/state_object_test.go b/ethchain/state_object_test.go deleted file mode 100644 index 2588100d0b..0000000000 --- a/ethchain/state_object_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package ethchain - -import ( - "fmt" - "github.com/ethereum/eth-go/ethdb" - "github.com/ethereum/eth-go/ethutil" - "math/big" - "testing" -) - -func TestSync(t *testing.T) { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256) - - contract.script = []byte{42} - - state.UpdateStateObject(contract) - state.Sync() - - object := state.GetStateObject([]byte("aa")) - if len(object.Script()) == 0 { - t.Fail() - } -} - -func TestObjectGet(t *testing.T) { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH") - - db, _ := ethdb.NewMemDatabase() - ethutil.Config.Db = db - - state := NewState(ethutil.NewTrie(db, "")) - - contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256) - state.UpdateStateObject(contract) - - contract = state.GetStateObject([]byte("aa")) - contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello")) - o := contract.GetMem(big.NewInt(0)) - fmt.Println(o) - - state.UpdateStateObject(contract) - contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello00")) - - contract = state.GetStateObject([]byte("aa")) - o = contract.GetMem(big.NewInt(0)) - fmt.Println("after", o) -} diff --git a/ethchain/state_test.go b/ethchain/state_test.go deleted file mode 100644 index 95be0f373c..0000000000 --- a/ethchain/state_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethdb" - "github.com/ethereum/eth-go/ethutil" - "testing" -) - -func TestSnapshot(t *testing.T) { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256) - state.UpdateStateObject(stateObject) - stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42)) - - snapshot := state.Copy() - - stateObject = state.GetStateObject([]byte("aa")) - stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(43)) - - state.Set(snapshot) - - stateObject = state.GetStateObject([]byte("aa")) - if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) { - t.Error("Expected storage 0 to be 42") - } -} diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 8ed528c9f2..a03a3a94a9 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -2,6 +2,10 @@ package ethchain import ( "fmt" + "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" ) @@ -27,17 +31,17 @@ type StateTransition struct { gas, gasPrice *big.Int value *big.Int data []byte - state *State + state *ethstate.State block *Block - cb, rec, sen *StateObject + cb, rec, sen *ethstate.StateObject } -func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition { +func NewStateTransition(coinbase *ethstate.StateObject, tx *Transaction, state *ethstate.State, block *Block) *StateTransition { return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil} } -func (self *StateTransition) Coinbase() *StateObject { +func (self *StateTransition) Coinbase() *ethstate.StateObject { if self.cb != nil { return self.cb } @@ -45,7 +49,7 @@ func (self *StateTransition) Coinbase() *StateObject { self.cb = self.state.GetOrNewStateObject(self.coinbase) return self.cb } -func (self *StateTransition) Sender() *StateObject { +func (self *StateTransition) Sender() *ethstate.StateObject { if self.sen != nil { return self.sen } @@ -54,7 +58,7 @@ func (self *StateTransition) Sender() *StateObject { return self.sen } -func (self *StateTransition) Receiver() *StateObject { +func (self *StateTransition) Receiver() *ethstate.StateObject { if self.tx != nil && self.tx.CreatesContract() { return nil } @@ -67,7 +71,7 @@ func (self *StateTransition) Receiver() *StateObject { return self.rec } -func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject { +func (self *StateTransition) MakeStateObject(state *ethstate.State, tx *Transaction) *ethstate.StateObject { contract := MakeContract(tx, state) return contract @@ -154,7 +158,7 @@ func (self *StateTransition) TransitionState() (err error) { var ( tx = self.tx sender = self.Sender() - receiver *StateObject + receiver *ethstate.StateObject ) defer self.RefundGas() @@ -163,13 +167,13 @@ func (self *StateTransition) TransitionState() (err error) { sender.Nonce += 1 // Transaction gas - if err = self.UseGas(GasTx); err != nil { + if err = self.UseGas(ethvm.GasTx); err != nil { return } // Pay data gas dataPrice := big.NewInt(int64(len(self.data))) - dataPrice.Mul(dataPrice, GasData) + dataPrice.Mul(dataPrice, ethvm.GasData) if err = self.UseGas(dataPrice); err != nil { return } @@ -178,7 +182,7 @@ func (self *StateTransition) TransitionState() (err error) { return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount) } - var snapshot *State + var snapshot *ethstate.State // If the receiver is nil it's a contract (\0*32). if tx.CreatesContract() { // Subtract the (irreversible) amount from the senders account @@ -220,10 +224,10 @@ func (self *StateTransition) TransitionState() (err error) { return fmt.Errorf("Error during init execution %v", err) } - receiver.script = code + receiver.Code = code } else { - if len(receiver.Script()) > 0 { - _, err = self.Eval(receiver.Script(), receiver, "code") + if len(receiver.Code) > 0 { + _, err = self.Eval(receiver.Code, receiver, "code") if err != nil { self.state.Set(snapshot) @@ -235,7 +239,7 @@ func (self *StateTransition) TransitionState() (err error) { return } -func (self *StateTransition) transferValue(sender, receiver *StateObject) 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) } @@ -248,34 +252,61 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error return nil } -func (self *StateTransition) Eval(script []byte, context *StateObject, typ string) (ret []byte, err error) { +func (self *StateTransition) Eval(script []byte, context *ethstate.StateObject, typ string) (ret []byte, err error) { var ( - block = self.block - initiator = self.Sender() - state = self.state + transactor = self.Sender() + state = self.state + env = NewEnv(state, self.tx, self.block) + callerClosure = ethvm.NewClosure(transactor, context, script, self.gas, self.gasPrice) ) - closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice) - vm := NewVm(state, nil, RuntimeVars{ - Origin: initiator.Address(), - Block: block, - BlockNumber: block.Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: self.value, - }) + vm := ethvm.New(env) vm.Verbose = true vm.Fn = typ - ret, err = Call(vm, closure, self.data) + ret, _, err = callerClosure.Call(vm, self.tx.Data) + + /* + closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice) + vm := NewVm(state, nil, RuntimeVars{ + Origin: initiator.Address(), + Block: block, + BlockNumber: block.Number, + PrevHash: block.PrevHash, + Coinbase: block.Coinbase, + Time: block.Time, + Diff: block.Difficulty, + Value: self.value, + }) + vm.Verbose = true + vm.Fn = typ + + ret, err = Call(vm, closure, self.data) + */ return } -func Call(vm *Vm, closure *Closure, data []byte) (ret []byte, err error) { +/* +func Call(vm *eth.Vm, closure *Closure, data []byte) (ret []byte, err error) { ret, _, err = closure.Call(vm, data) return } +*/ + +// Converts an transaction in to a state object +func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject { + // Create contract if there's no recipient + if tx.IsContract() { + addr := tx.CreationAddress() + + contract := state.NewStateObject(addr) + contract.InitCode = tx.Data + contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) + + return contract + } + + return nil +} diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 6ab8d83d9d..21c6ea3de3 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -5,6 +5,7 @@ import ( "container/list" "fmt" "github.com/ethereum/eth-go/ethlog" + "github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethwire" "math/big" "sync" @@ -252,7 +253,7 @@ func (pool *TxPool) CurrentTransactions() []*Transaction { return txList } -func (pool *TxPool) RemoveInvalid(state *State) { +func (pool *TxPool) RemoveInvalid(state *ethstate.State) { for e := pool.pool.Front(); e != nil; e = e.Next() { tx := e.Value.(*Transaction) sender := state.GetAccount(tx.Sender()) diff --git a/ethchain/vm.go b/ethchain/vm.go deleted file mode 100644 index 4cbb4e1c4b..0000000000 --- a/ethchain/vm.go +++ /dev/null @@ -1,843 +0,0 @@ -package ethchain - -import ( - "fmt" - "github.com/ethereum/eth-go/ethcrypto" - "github.com/ethereum/eth-go/ethlog" - "github.com/ethereum/eth-go/ethutil" - "math" - "math/big" -) - -var vmlogger = ethlog.NewLogger("VM") - -var ( - GasStep = big.NewInt(1) - GasSha = big.NewInt(20) - GasSLoad = big.NewInt(20) - GasSStore = big.NewInt(100) - GasBalance = big.NewInt(20) - GasCreate = big.NewInt(100) - GasCall = big.NewInt(20) - GasMemory = big.NewInt(1) - GasData = big.NewInt(5) - GasTx = big.NewInt(500) - - LogTyPretty byte = 0x1 - LogTyDiff byte = 0x2 -) - -type Debugger interface { - BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool - StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool - BreakPoints() []int64 - SetCode(byteCode []byte) -} - -type Vm struct { - // Stack for processing contracts - stack *Stack - // non-persistent key/value memory storage - mem map[string]*big.Int - - vars RuntimeVars - - state *State - - stateManager *StateManager - - Verbose bool - - logTy byte - logStr string - - err error - - // Debugging - Dbg Debugger - - BreakPoints []int64 - Stepping bool - Fn string -} - -type RuntimeVars struct { - Origin []byte - Block *Block - BlockNumber *big.Int - PrevHash []byte - Coinbase []byte - Time int64 - Diff *big.Int - TxData []string - Value *big.Int -} - -func (self *Vm) Printf(format string, v ...interface{}) *Vm { - if self.Verbose && self.logTy == LogTyPretty { - self.logStr += fmt.Sprintf(format, v...) - } - - return self -} - -func (self *Vm) Endl() *Vm { - if self.Verbose && self.logTy == LogTyPretty { - vmlogger.Debugln(self.logStr) - self.logStr = "" - } - - return self -} - -func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { - lt := LogTyPretty - if ethutil.Config.Diff { - lt = LogTyDiff - } - - return &Vm{vars: vars, state: state, stateManager: stateManager, logTy: lt} -} - -var Pow256 = ethutil.BigPow(2, 256) - -var isRequireError = false - -func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) { - // Recover from any require exception - defer func() { - if r := recover(); r != nil { - ret = closure.Return(nil) - err = fmt.Errorf("%v", r) - vmlogger.Errorln("vm err", err) - } - }() - - // Debug hook - if vm.Dbg != nil { - vm.Dbg.SetCode(closure.Script) - } - - // Don't bother with the execution if there's no code. - if len(closure.Script) == 0 { - return closure.Return(nil), nil - } - - vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.Address(), closure.Gas, closure.Args) - - var ( - op OpCode - - mem = &Memory{} - stack = NewStack() - pc = big.NewInt(0) - step = 0 - prevStep = 0 - require = func(m int) { - if stack.Len() < m { - isRequireError = true - panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m)) - } - } - ) - - for { - prevStep = step - // The base for all big integer arithmetic - base := new(big.Int) - - step++ - // Get the memory location of pc - val := closure.Get(pc) - // Get the opcode (it must be an opcode!) - op = OpCode(val.Uint()) - - // XXX Leave this Println intact. Don't change this to the log system. - // Used for creating diffs between implementations - if vm.logTy == LogTyDiff { - switch op { - case STOP, RETURN, SUICIDE: - closure.object.Sync() - closure.object.EachStorage(func(key string, value *ethutil.Value) { - value.Decode() - 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()) - } - - gas := new(big.Int) - addStepGasUsage := func(amount *big.Int) { - if amount.Cmp(ethutil.Big0) >= 0 { - gas.Add(gas, amount) - } - } - - addStepGasUsage(GasStep) - - var newMemSize uint64 = 0 - switch op { - case STOP: - gas.Set(ethutil.Big0) - case SUICIDE: - gas.Set(ethutil.Big0) - case SLOAD: - gas.Set(GasSLoad) - case SSTORE: - var mult *big.Int - y, x := stack.Peekn() - val := closure.GetStorage(x) - if val.BigInt().Cmp(ethutil.Big0) == 0 && len(y.Bytes()) > 0 { - mult = ethutil.Big2 - } else if val.BigInt().Cmp(ethutil.Big0) != 0 && len(y.Bytes()) == 0 { - mult = ethutil.Big0 - } else { - mult = ethutil.Big1 - } - gas = new(big.Int).Mul(mult, GasSStore) - case BALANCE: - gas.Set(GasBalance) - case MSTORE: - require(2) - newMemSize = stack.Peek().Uint64() + 32 - case MLOAD: - require(1) - - newMemSize = stack.Peek().Uint64() + 32 - case MSTORE8: - require(2) - newMemSize = stack.Peek().Uint64() + 1 - case RETURN: - require(2) - - newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() - case SHA3: - require(2) - - gas.Set(GasSha) - - newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() - case CALLDATACOPY: - require(3) - - newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() - case CODECOPY: - require(3) - - newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() - case CALL: - require(7) - gas.Set(GasCall) - addStepGasUsage(stack.data[stack.Len()-1]) - - x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64() - y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64() - - newMemSize = uint64(math.Max(float64(x), float64(y))) - case CREATE: - require(3) - gas.Set(GasCreate) - - newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() - } - - newMemSize = (newMemSize + 31) / 32 * 32 - if newMemSize > uint64(mem.Len()) { - m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 - addStepGasUsage(big.NewInt(int64(m))) - } - - if !closure.UseGas(gas) { - err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas) - - closure.UseGas(closure.Gas) - - return closure.Return(nil), err - } - - vm.Printf("(pc) %-3d -o- %-14s", pc, op.String()) - vm.Printf(" (g) %-3v (%v)", gas, closure.Gas) - - mem.Resize(newMemSize) - - switch op { - case LOG: - stack.Print() - mem.Print() - // 0x20 range - case ADD: - require(2) - x, y := stack.Popn() - vm.Printf(" %v + %v", y, x) - - base.Add(y, x) - - vm.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case SUB: - require(2) - x, y := stack.Popn() - vm.Printf(" %v - %v", y, x) - - base.Sub(y, x) - - vm.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case MUL: - require(2) - x, y := stack.Popn() - vm.Printf(" %v * %v", y, x) - - base.Mul(y, x) - - vm.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case DIV: - require(2) - x, y := stack.Popn() - vm.Printf(" %v / %v", y, x) - - base.Div(y, x) - - vm.Printf(" = %v", base) - // Pop result back on the stack - stack.Push(base) - case SDIV: - require(2) - x, y := stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Div(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - stack.Push(z) - case MOD: - require(2) - x, y := stack.Popn() - - vm.Printf(" %v %% %v", y, x) - - base.Mod(y, x) - - vm.Printf(" = %v", base) - stack.Push(base) - case SMOD: - require(2) - x, y := stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Mod(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - stack.Push(z) - case EXP: - require(2) - x, y := stack.Popn() - - vm.Printf(" %v ** %v", y, x) - - base.Exp(y, x, Pow256) - - vm.Printf(" = %v", base) - - stack.Push(base) - case NEG: - require(1) - base.Sub(Pow256, stack.Pop()) - stack.Push(base) - case LT: - require(2) - x, y := stack.Popn() - vm.Printf(" %v < %v", y, x) - // x < y - if y.Cmp(x) < 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case GT: - require(2) - x, y := stack.Popn() - vm.Printf(" %v > %v", y, x) - - // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - - case SLT: - require(2) - x, y := stack.Popn() - vm.Printf(" %v < %v", y, x) - // x < y - if y.Cmp(x) < 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case SGT: - require(2) - x, y := stack.Popn() - vm.Printf(" %v > %v", y, x) - - // x > y - if y.Cmp(x) > 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - - case EQ: - require(2) - x, y := stack.Popn() - vm.Printf(" %v == %v", y, x) - - // x == y - if x.Cmp(y) == 0 { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - case NOT: - require(1) - x := stack.Pop() - if x.Cmp(ethutil.BigFalse) > 0 { - stack.Push(ethutil.BigFalse) - } else { - stack.Push(ethutil.BigTrue) - } - - // 0x10 range - case AND: - require(2) - x, y := stack.Popn() - vm.Printf(" %v & %v", y, x) - - stack.Push(base.And(y, x)) - case OR: - require(2) - x, y := stack.Popn() - vm.Printf(" %v | %v", y, x) - - stack.Push(base.Or(y, x)) - case XOR: - require(2) - x, y := stack.Popn() - vm.Printf(" %v ^ %v", y, x) - - stack.Push(base.Xor(y, x)) - case BYTE: - require(2) - val, th := stack.Popn() - if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 { - byt := big.NewInt(int64(val.Bytes()[th.Int64()])) - stack.Push(byt) - - vm.Printf(" => 0x%x", byt.Bytes()) - } else { - stack.Push(ethutil.BigFalse) - } - - // 0x20 range - case SHA3: - require(2) - size, offset := stack.Popn() - data := ethcrypto.Sha3Bin(mem.Get(offset.Int64(), size.Int64())) - - stack.Push(ethutil.BigD(data)) - - vm.Printf(" => %x", data) - // 0x30 range - case ADDRESS: - stack.Push(ethutil.BigD(closure.Address())) - - vm.Printf(" => %x", closure.Address()) - case BALANCE: - require(1) - - addr := stack.Pop().Bytes() - balance := vm.state.GetBalance(addr) - - stack.Push(balance) - - vm.Printf(" => %v (%x)", balance, addr) - case ORIGIN: - stack.Push(ethutil.BigD(vm.vars.Origin)) - - vm.Printf(" => %x", vm.vars.Origin) - case CALLER: - caller := closure.caller.Address() - stack.Push(ethutil.BigD(caller)) - - vm.Printf(" => %x", caller) - case CALLVALUE: - stack.Push(vm.vars.Value) - - vm.Printf(" => %v", vm.vars.Value) - case CALLDATALOAD: - require(1) - var ( - offset = stack.Pop() - data = make([]byte, 32) - lenData = big.NewInt(int64(len(closure.Args))) - ) - - if lenData.Cmp(offset) >= 0 { - length := new(big.Int).Add(offset, ethutil.Big32) - length = ethutil.BigMin(length, lenData) - - copy(data, closure.Args[offset.Int64():length.Int64()]) - } - - vm.Printf(" => 0x%x", data) - - stack.Push(ethutil.BigD(data)) - case CALLDATASIZE: - l := int64(len(closure.Args)) - stack.Push(big.NewInt(l)) - - vm.Printf(" => %d", l) - case CALLDATACOPY: - var ( - size = int64(len(closure.Args)) - mOff = stack.Pop().Int64() - cOff = stack.Pop().Int64() - l = stack.Pop().Int64() - ) - - if cOff > size { - cOff = 0 - l = 0 - } else if cOff+l > size { - l = 0 - } - - code := closure.Args[cOff : cOff+l] - - mem.Set(mOff, l, code) - case CODESIZE: - l := big.NewInt(int64(len(closure.Script))) - stack.Push(l) - - vm.Printf(" => %d", l) - case CODECOPY: - var ( - size = int64(len(closure.Script)) - mOff = stack.Pop().Int64() - cOff = stack.Pop().Int64() - l = stack.Pop().Int64() - ) - - if cOff > size { - cOff = 0 - l = 0 - } else if cOff+l > size { - l = 0 - } - - code := closure.Script[cOff : cOff+l] - fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff) - - mem.Set(mOff, l, code) - case GASPRICE: - stack.Push(closure.Price) - - // 0x40 range - case PREVHASH: - stack.Push(ethutil.BigD(vm.vars.PrevHash)) - case COINBASE: - stack.Push(ethutil.BigD(vm.vars.Coinbase)) - case TIMESTAMP: - stack.Push(big.NewInt(vm.vars.Time)) - case NUMBER: - stack.Push(vm.vars.BlockNumber) - case DIFFICULTY: - stack.Push(vm.vars.Diff) - case GASLIMIT: - // TODO - stack.Push(big.NewInt(0)) - - // 0x50 range - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - a := big.NewInt(int64(op) - int64(PUSH1) + 1) - pc.Add(pc, ethutil.Big1) - data := closure.Gets(pc, a) - val := ethutil.BigD(data.Bytes()) - // Push value to stack - stack.Push(val) - pc.Add(pc, a.Sub(a, big.NewInt(1))) - - step += int(op) - int(PUSH1) + 1 - - vm.Printf(" => 0x%x", data.Bytes()) - case POP: - require(1) - stack.Pop() - case DUP: - require(1) - stack.Push(stack.Peek()) - - vm.Printf(" => 0x%x", stack.Peek().Bytes()) - case SWAP: - require(2) - x, y := stack.Popn() - stack.Push(y) - stack.Push(x) - case MLOAD: - require(1) - offset := stack.Pop() - val := ethutil.BigD(mem.Get(offset.Int64(), 32)) - stack.Push(val) - - vm.Printf(" => 0x%x", val.Bytes()) - case MSTORE: // Store the value at stack top-1 in to memory at location stack top - require(2) - // Pop value of the stack - val, mStart := stack.Popn() - mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) - - vm.Printf(" => 0x%x", val) - case MSTORE8: - require(2) - val, mStart := stack.Popn() - //base.And(val, new(big.Int).SetInt64(0xff)) - //mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) - mem.store[mStart.Int64()] = byte(val.Int64() & 0xff) - - vm.Printf(" => 0x%x", val) - case SLOAD: - require(1) - loc := stack.Pop() - val := closure.GetStorage(loc) - - stack.Push(val.BigInt()) - - vm.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) - case SSTORE: - require(2) - val, loc := stack.Popn() - closure.SetStorage(loc, ethutil.NewValue(val)) - - // Add the change to manifest - vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) - - vm.Printf(" {0x%x : 0x%x}", loc, val) - case JUMP: - require(1) - pc = stack.Pop() - // Reduce pc by one because of the increment that's at the end of this for loop - vm.Printf(" ~> %v", pc).Endl() - - continue - case JUMPI: - require(2) - cond, pos := stack.Popn() - if cond.Cmp(ethutil.BigTrue) >= 0 { - pc = pos - - vm.Printf(" ~> %v (t)", pc).Endl() - - continue - } else { - vm.Printf(" (f)") - } - case PC: - stack.Push(pc) - case MSIZE: - stack.Push(big.NewInt(int64(mem.Len()))) - - vm.Printf(" => %v", mem.Len()).Endl() - case GAS: - stack.Push(closure.Gas) - // 0x60 range - case CREATE: - require(3) - - var ( - err error - value = stack.Pop() - size, offset = stack.Popn() - - // Snapshot the current stack so we are able to - // revert back to it later. - snapshot = vm.state.Copy() - ) - - // Generate a new address - addr := ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()) - for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ { - ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()+i) - } - closure.object.Nonce++ - - vm.Printf(" (*) %x", addr).Endl() - - // Create a new contract - contract := vm.state.NewStateObject(addr) - if contract.Amount.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) - // 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, vm.state, gas, closure.Price) - // Call the closure and set the return value as - // main script. - contract.script, err = Call(vm, c, nil) - } else { - err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) - } - - if err != nil { - stack.Push(ethutil.BigFalse) - - // Revert the state as it was before. - vm.state.Set(snapshot) - - vm.Printf("CREATE err %v", err) - } else { - stack.Push(ethutil.BigD(addr)) - vm.Printf("CREATE success") - } - vm.Endl() - - // Debug hook - if vm.Dbg != nil { - vm.Dbg.SetCode(closure.Script) - } - case CALL: - require(7) - - vm.Endl() - - gas := stack.Pop() - // Pop gas and value of the stack. - value, addr := stack.Popn() - // Pop input size and offset - inSize, inOffset := stack.Popn() - // Pop return size and offset - retSize, retOffset := stack.Popn() - - // 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) - - closure.ReturnGas(gas, nil, nil) - - stack.Push(ethutil.BigFalse) - } else { - snapshot := vm.state.Copy() - - stateObject := vm.state.GetOrNewStateObject(addr.Bytes()) - - closure.object.SubAmount(value) - stateObject.AddAmount(value) - - // Create a new callable closure - c := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price) - // Executer the closure and get the return value (if any) - ret, err := Call(vm, c, args) - if err != nil { - stack.Push(ethutil.BigFalse) - - vmlogger.Debugf("Closure execution failed. %v\n", err) - - vm.state.Set(snapshot) - } else { - stack.Push(ethutil.BigTrue) - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) - } - - // Debug hook - if vm.Dbg != nil { - vm.Dbg.SetCode(closure.Script) - } - } - case RETURN: - require(2) - size, offset := stack.Popn() - ret := mem.Get(offset.Int64(), size.Int64()) - - vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl() - - return closure.Return(ret), nil - case SUICIDE: - require(1) - - receiver := vm.state.GetOrNewStateObject(stack.Pop().Bytes()) - - receiver.AddAmount(closure.object.Amount) - - closure.object.MarkForDeletion() - - fallthrough - case STOP: // Stop the closure - vm.Endl() - - return closure.Return(nil), nil - default: - vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) - fmt.Println(Code(closure.Script)) - - return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) - } - - pc.Add(pc, ethutil.Big1) - - vm.Endl() - - if vm.Dbg != nil { - for _, instrNo := range vm.Dbg.BreakPoints() { - if pc.Cmp(big.NewInt(instrNo)) == 0 { - vm.Stepping = true - - if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) { - return nil, nil - } - } else if vm.Stepping { - if !vm.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) { - return nil, nil - } - } - } - } - - } -} diff --git a/ethchain/vm_env.go b/ethchain/vm_env.go new file mode 100644 index 0000000000..711cfbe9fe --- /dev/null +++ b/ethchain/vm_env.go @@ -0,0 +1,28 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethstate" + "math/big" +) + +type VMEnv struct { + state *ethstate.State + block *Block + tx *Transaction +} + +func NewEnv(state *ethstate.State, tx *Transaction, block *Block) *VMEnv { + return &VMEnv{ + state: state, + block: block, + } +} + +func (self *VMEnv) Origin() []byte { return self.tx.Sender() } +func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number } +func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash } +func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase } +func (self *VMEnv) Time() int64 { return self.block.Time } +func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } +func (self *VMEnv) Value() *big.Int { return self.tx.Value } +func (self *VMEnv) State() *ethstate.State { return self.state } diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go deleted file mode 100644 index c8023cd790..0000000000 --- a/ethchain/vm_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package ethchain - -import ( - _ "bytes" - "fmt" - "github.com/ethereum/eth-go/ethdb" - "github.com/ethereum/eth-go/ethutil" - "math/big" - "testing" -) - -func TestRun4(t *testing.T) { - ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - callerScript, err := ethutil.Compile(` - this.store[this.origin()] = 10**20 - hello := "world" - - return lambda { - big to = this.data[0] - big from = this.origin() - big value = this.data[1] - - if this.store[from] >= value { - this.store[from] = this.store[from] - value - this.store[to] = this.store[to] + value - } - } - `) - if err != nil { - fmt.Println(err) - } - fmt.Println(Disassemble(callerScript)) - - callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript) - callerTx.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) - - // Contract addr as test address - gas := big.NewInt(1000) - gasPrice := big.NewInt(10) - account := NewAccount(ContractAddr, big.NewInt(10000000)) - fmt.Println("account.Amount =", account.Amount) - c := MakeContract(callerTx, state) - e := account.ConvertGas(gas, gasPrice) - if e != nil { - fmt.Println(err) - } - fmt.Println("account.Amount =", account.Amount) - callerClosure := NewClosure(account, c, callerScript, state, gas, gasPrice) - - vm := NewVm(state, nil, RuntimeVars{ - Origin: account.Address(), - BlockNumber: big.NewInt(1), - PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - Time: 1, - Diff: big.NewInt(256), - }) - var ret []byte - ret, _, e = callerClosure.Call(vm, nil, nil) - if e != nil { - fmt.Println("error", e) - } - fmt.Println(ret) -} diff --git a/ethpub/pub.go b/ethpub/pub.go index 2650edf021..7cc7cf6ce3 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -6,6 +6,7 @@ import ( "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" @@ -24,11 +25,11 @@ type helper struct { func EthereumConfig(stateManager *ethchain.StateManager) helper { return helper{stateManager} } -func (self helper) obj() *ethchain.StateObject { +func (self helper) obj() *ethstate.StateObject { return self.sm.CurrentState().GetStateObject(cnfCtr) } -func (self helper) NameReg() *ethchain.StateObject { +func (self helper) NameReg() *ethstate.StateObject { if self.obj() != nil { addr := self.obj().GetStorage(big.NewInt(0)) if len(addr.Bytes()) > 0 { diff --git a/ethpub/types.go b/ethpub/types.go index 52ea5a6bb0..5cfa2705e1 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -5,6 +5,7 @@ import ( "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" @@ -154,10 +155,10 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) * } type PStateObject struct { - object *ethchain.StateObject + object *ethstate.StateObject } -func NewPStateObject(object *ethchain.StateObject) *PStateObject { +func NewPStateObject(object *ethstate.StateObject) *PStateObject { return &PStateObject{object: object} } @@ -200,7 +201,7 @@ func (c *PStateObject) Nonce() int { func (c *PStateObject) Root() string { if c.object != nil { - return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State().Root()).Bytes()) + return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State.Root()).Bytes()) } return "" @@ -208,7 +209,7 @@ func (c *PStateObject) Root() string { func (c *PStateObject) IsContract() bool { if c.object != nil { - return len(c.object.Script()) > 0 + return len(c.object.Code) > 0 } return false @@ -245,7 +246,7 @@ func (c *PStateObject) StateKeyVal(asJson bool) interface{} { func (c *PStateObject) Script() string { if c.object != nil { - return strings.Join(ethchain.Disassemble(c.object.Script()), " ") + return strings.Join(ethchain.Disassemble(c.object.Code), " ") } return "" @@ -253,7 +254,7 @@ func (c *PStateObject) Script() string { func (c *PStateObject) HexScript() string { if c.object != nil { - return ethutil.Bytes2Hex(c.object.Script()) + return ethutil.Bytes2Hex(c.object.Code) } return "" @@ -265,6 +266,6 @@ type PStorageState struct { Value string } -func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState { +func NewPStorageState(storageObject *ethstate.StorageState) *PStorageState { return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()} } diff --git a/ethstate/state.go b/ethstate/state.go index a4b9b1e9c6..51b585d4da 100644 --- a/ethstate/state.go +++ b/ethstate/state.go @@ -17,7 +17,7 @@ var statelogger = ethlog.NewLogger("STATE") // * Accounts type State struct { // The trie for this structure - trie *ethtrie.Trie + Trie *ethtrie.Trie stateObjects map[string]*StateObject @@ -26,7 +26,7 @@ type State struct { // Create a new state from a given trie func NewState(trie *ethtrie.Trie) *State { - return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} + return &State{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()} } // Retrieve the balance from the given address or 0 if object not found @@ -58,14 +58,14 @@ 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.Trie.Update(string(addr), string(stateObject.RlpEncode())) self.manifest.AddObjectChange(stateObject) } // Delete the given state object and delete it from the state trie func (self *State) DeleteStateObject(stateObject *StateObject) { - self.trie.Delete(string(stateObject.Address())) + self.Trie.Delete(string(stateObject.Address())) delete(self.stateObjects, string(stateObject.Address())) } @@ -79,7 +79,7 @@ func (self *State) GetStateObject(addr []byte) *StateObject { return stateObject } - data := self.trie.Get(string(addr)) + data := self.Trie.Get(string(addr)) if len(data) == 0 { return nil } @@ -122,12 +122,12 @@ func (self *State) GetAccount(addr []byte) *StateObject { // func (s *State) Cmp(other *State) bool { - return s.trie.Cmp(other.trie) + return s.Trie.Cmp(other.Trie) } func (self *State) Copy() *State { - if self.trie != nil { - state := NewState(self.trie.Copy()) + if self.Trie != nil { + state := NewState(self.Trie.Copy()) for k, stateObject := range self.stateObjects { state.stateObjects[k] = stateObject.Copy() } @@ -143,21 +143,21 @@ func (self *State) Set(state *State) { panic("Tried setting 'state' to nil through 'Set'") } - self.trie = state.trie + self.Trie = state.Trie self.stateObjects = state.stateObjects } func (s *State) Root() interface{} { - return s.trie.Root + return s.Trie.Root } // Resets the trie and all siblings func (s *State) Reset() { - s.trie.Undo() + s.Trie.Undo() // Reset all nested states for _, stateObject := range s.stateObjects { - if stateObject.state == nil { + if stateObject.State == nil { continue } @@ -174,14 +174,14 @@ func (s *State) Sync() { for _, stateObject := range s.stateObjects { //s.UpdateStateObject(stateObject) - if stateObject.state == nil { + if stateObject.State == nil { continue } - stateObject.state.Sync() + stateObject.State.Sync() } - s.trie.Sync() + s.Trie.Sync() s.Empty() } @@ -202,11 +202,11 @@ func (self *State) Update() { } // FIXME trie delete is broken - valid, t2 := ethtrie.ParanoiaCheck(self.trie) + valid, t2 := ethtrie.ParanoiaCheck(self.Trie) if !valid { - statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root) + statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.Trie.Root, t2.Root) - self.trie = t2 + self.Trie = t2 } } @@ -230,8 +230,8 @@ type Manifest struct { objectAddresses map[string]bool storageAddresses map[string]map[string]bool - objectChanges map[string]*StateObject - storageChanges map[string]map[string]*big.Int + ObjectChanges map[string]*StateObject + StorageChanges map[string]map[string]*big.Int } func NewManifest() *Manifest { @@ -242,18 +242,18 @@ func NewManifest() *Manifest { } func (m *Manifest) Reset() { - m.objectChanges = make(map[string]*StateObject) - m.storageChanges = make(map[string]map[string]*big.Int) + 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 + 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) + 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 + m.StorageChanges[string(stateObject.Address())][string(storageAddr)] = storage } diff --git a/ethstate/state_object.go b/ethstate/state_object.go index 6b00c53698..ab14b86049 100644 --- a/ethstate/state_object.go +++ b/ethstate/state_object.go @@ -34,9 +34,9 @@ type StateObject struct { CodeHash []byte Nonce uint64 // Contract related attributes - state *State + State *State Code Code - initCode Code + InitCode Code storage Storage @@ -53,7 +53,7 @@ type StateObject struct { func (self *StateObject) Reset() { self.storage = make(Storage) - self.state.Reset() + self.State.Reset() } /* @@ -79,7 +79,7 @@ func NewStateObject(addr []byte) *StateObject { 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.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) object.storage = make(Storage) object.gasPool = new(big.Int) @@ -89,7 +89,7 @@ func NewStateObject(addr []byte) *StateObject { func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { contract := NewStateObject(address) contract.Amount = Amount - contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) + contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) return contract } @@ -107,11 +107,11 @@ func (self *StateObject) MarkForDeletion() { } func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { - return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) + return ethutil.NewValueFromBytes([]byte(c.State.Trie.Get(string(addr)))) } func (c *StateObject) SetAddr(addr []byte, value interface{}) { - c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) + c.State.Trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) } func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { @@ -152,7 +152,7 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) { cb(key, encoded) } - it := self.state.trie.NewIterator() + it := self.State.Trie.NewIterator() it.Each(func(key string, value *ethutil.Value) { // If it's cached don't call the callback. if self.storage[key] == nil { @@ -166,18 +166,18 @@ func (self *StateObject) Sync() { if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 { //data := self.getStorage([]byte(key)) //fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data) - self.state.trie.Delete(string(key)) + self.State.Trie.Delete(string(key)) continue } self.SetAddr([]byte(key), value) } - valid, t2 := ethtrie.ParanoiaCheck(self.state.trie) + valid, t2 := ethtrie.ParanoiaCheck(self.State.Trie) if !valid { - statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root) + statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Trie.Root, t2.Root) - self.state.trie = t2 + self.State.Trie = t2 } } @@ -255,11 +255,11 @@ func (self *StateObject) Copy() *StateObject { stateObject.Amount.Set(self.Amount) stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash) stateObject.Nonce = self.Nonce - if self.state != nil { - stateObject.state = self.state.Copy() + if self.State != nil { + stateObject.State = self.State.Copy() } stateObject.Code = ethutil.CopyBytes(self.Code) - stateObject.initCode = ethutil.CopyBytes(self.initCode) + stateObject.InitCode = ethutil.CopyBytes(self.InitCode) stateObject.storage = self.storage.Copy() stateObject.gasPool.Set(self.gasPool) @@ -274,10 +274,6 @@ func (self *StateObject) Set(stateObject *StateObject) { // Attribute accessors // -func (c *StateObject) State() *State { - return c.state -} - func (c *StateObject) N() *big.Int { return big.NewInt(int64(c.Nonce)) } @@ -289,12 +285,12 @@ func (c *StateObject) Address() []byte { // Returns the initialization Code func (c *StateObject) Init() Code { - return c.initCode + return c.InitCode } // 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.Amount.Bytes(), self.Nonce) self.EachStorage(func(addr string, value *ethutil.Value) { fmt.Printf("%x %x\n", addr, value.Bytes()) }) @@ -307,8 +303,8 @@ func (self *StateObject) CreateOutputForDiff() { // State object encoding methods func (c *StateObject) RlpEncode() []byte { var root interface{} - if c.state != nil { - root = c.state.trie.Root + if c.State != nil { + root = c.State.Trie.Root } else { root = "" } @@ -321,7 +317,7 @@ func (c *StateObject) RlpDecode(data []byte) { 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.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.storage = make(map[string]*ethutil.Value) c.gasPool = new(big.Int) diff --git a/peer.go b/peer.go index 89032364e7..1b8ebb5a48 100644 --- a/peer.go +++ b/peer.go @@ -470,23 +470,20 @@ func (p *Peer) HandleInbound() { p.pushPeers() case ethwire.MsgPeersTy: // Received a list of peers (probably because MsgGetPeersTy was send) - // Only act on message if we actually requested for a peers list - if p.requestedPeerList { - data := msg.Data - // Create new list of possible peers for the ethereum to process - peers := make([]string, data.Len()) - // Parse each possible peer - for i := 0; i < data.Len(); i++ { - value := data.Get(i) - peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint()) - } + data := msg.Data + // Create new list of possible peers for the ethereum to process + peers := make([]string, data.Len()) + // Parse each possible peer + for i := 0; i < data.Len(); i++ { + value := data.Get(i) + peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint()) + } - // Connect to the list of peers - p.ethereum.ProcessPeerList(peers) - // Mark unrequested again - p.requestedPeerList = false + // Connect to the list of peers + p.ethereum.ProcessPeerList(peers) + // Mark unrequested again + p.requestedPeerList = false - } case ethwire.MsgGetChainTy: var parent *ethchain.Block // Length minus one since the very last element in the array is a count From cbd71ef8f590bf7e97beaa81256d188c21587ef9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:10:18 +0200 Subject: [PATCH 09/26] Add Tx --- ethchain/vm_env.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethchain/vm_env.go b/ethchain/vm_env.go index 711cfbe9fe..ddead77fd7 100644 --- a/ethchain/vm_env.go +++ b/ethchain/vm_env.go @@ -15,6 +15,7 @@ func NewEnv(state *ethstate.State, tx *Transaction, block *Block) *VMEnv { return &VMEnv{ state: state, block: block, + tx: tx, } } From 306b5bcff306bbdc5bc0b1590fca552f4fda41f6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:11:30 +0200 Subject: [PATCH 10/26] Changed catching up code & peer handler * Peers should be added no matter what * Catch up with _anyone_ --- peer.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/peer.go b/peer.go index 1b8ebb5a48..16340cda5d 100644 --- a/peer.go +++ b/peer.go @@ -122,9 +122,6 @@ type Peer struct { // Last received pong message lastPong int64 - // Indicates whether a MsgGetPeersTy was requested of the peer - // this to prevent receiving false peers. - requestedPeerList bool host []byte port uint16 @@ -463,9 +460,6 @@ func (p *Peer) HandleInbound() { p.ethereum.TxPool().QueueTransaction(tx) } case ethwire.MsgGetPeersTy: - // Flag this peer as a 'requested of new peers' this to - // prevent malicious peers being forced. - p.requestedPeerList = true // Peer asked for list of connected peers p.pushPeers() case ethwire.MsgPeersTy: @@ -481,9 +475,6 @@ func (p *Peer) HandleInbound() { // Connect to the list of peers p.ethereum.ProcessPeerList(peers) - // Mark unrequested again - p.requestedPeerList = false - case ethwire.MsgGetChainTy: var parent *ethchain.Block // Length minus one since the very last element in the array is a count @@ -695,11 +686,13 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers) - // Catch up with the connected peer - if !p.ethereum.IsUpToDate() { - peerlogger.Debugln("Already syncing up with a peer; sleeping") - time.Sleep(10 * time.Second) - } + /* + // Catch up with the connected peer + if !p.ethereum.IsUpToDate() { + peerlogger.Debugln("Already syncing up with a peer; sleeping") + time.Sleep(10 * time.Second) + } + */ p.SyncWithPeerToLastKnown() peerlogger.Debugln(p) From 3c3292d505b1c857f7fa28da0d37e3982ef5f8b5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:12:00 +0200 Subject: [PATCH 11/26] Added recoverable option --- ethvm/vm.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ethvm/vm.go b/ethvm/vm.go index e159d8ff1b..b81c8a189c 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -37,6 +37,8 @@ type Vm struct { BreakPoints []int64 Stepping bool Fn string + + Recoverable bool } type Environment interface { @@ -62,18 +64,20 @@ func New(env Environment) *Vm { lt = LogTyDiff } - return &Vm{env: env, logTy: lt} + return &Vm{env: env, logTy: lt, Recoverable: true} } func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { - // Recover from any require exception - defer func() { - if r := recover(); r != nil { - ret = closure.Return(nil) - err = fmt.Errorf("%v", r) - vmlogger.Errorln("vm err", err) - } - }() + if self.Recoverable { + // Recover from any require exception + defer func() { + if r := recover(); r != nil { + ret = closure.Return(nil) + err = fmt.Errorf("%v", r) + vmlogger.Errorln("vm err", err) + } + }() + } // Debug hook if self.Dbg != nil { From a45c08f9fe320d79d2abc7c29e5f3b986130c5bb Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:19:55 +0200 Subject: [PATCH 12/26] Removed old code --- ethchain/state_transition.go | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index a03a3a94a9..266328ce8e 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -266,34 +266,8 @@ func (self *StateTransition) Eval(script []byte, context *ethstate.StateObject, ret, _, err = callerClosure.Call(vm, self.tx.Data) - /* - closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice) - vm := NewVm(state, nil, RuntimeVars{ - Origin: initiator.Address(), - Block: block, - BlockNumber: block.Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: self.value, - }) - vm.Verbose = true - vm.Fn = typ - - ret, err = Call(vm, closure, self.data) - */ - - return -} - -/* -func Call(vm *eth.Vm, closure *Closure, data []byte) (ret []byte, err error) { - ret, _, err = closure.Call(vm, data) - return } -*/ // Converts an transaction in to a state object func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject { From 6d69ca36a755ea44e7ce4ba7b135afb0b9dbff05 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:25:41 +0200 Subject: [PATCH 13/26] Peer reconnect attempt --- peer.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/peer.go b/peer.go index 16340cda5d..ac4ba82abc 100644 --- a/peer.go +++ b/peer.go @@ -177,10 +177,20 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { // Set up the connection in another goroutine so we don't block the main thread go func() { - conn, err := net.DialTimeout("tcp", addr, 10*time.Second) + var ( + err error + conn net.Conn + ) + + for attempts := 0; attempts < 5; attempts++ { + conn, err = net.DialTimeout("tcp", addr, 10*time.Second) + if err != nil { + peerlogger.Debugf("Peer connection failed. Retrying (%d/5)\n", attempts+1) + } + } if err != nil { - peerlogger.Debugln("Connection to peer failed", err) + peerlogger.Debugln("Connection to peer failed. Giving up.", err) p.Stop() return } From dcf4fad97156f431612ed3915e167ce5a5314588 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 12:30:04 +0200 Subject: [PATCH 14/26] Networking code --- peer.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/peer.go b/peer.go index ac4ba82abc..c73617ed5b 100644 --- a/peer.go +++ b/peer.go @@ -177,18 +177,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { // Set up the connection in another goroutine so we don't block the main thread go func() { - var ( - err error - conn net.Conn - ) - - for attempts := 0; attempts < 5; attempts++ { - conn, err = net.DialTimeout("tcp", addr, 10*time.Second) - if err != nil { - peerlogger.Debugf("Peer connection failed. Retrying (%d/5)\n", attempts+1) - } - } - + conn, err := p.Connect(addr) if err != nil { peerlogger.Debugln("Connection to peer failed. Giving up.", err) p.Stop() @@ -206,6 +195,21 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { return p } +func (self *Peer) Connect(addr string) (conn net.Conn, err error) { + for attempts := 0; attempts < 5; attempts++ { + conn, err = net.DialTimeout("tcp", addr, 10*time.Second) + if err != nil { + peerlogger.Debugf("Peer connection failed. Retrying (%d/5)\n", attempts+1) + continue + } + + // Success + return + } + + return +} + // Getters func (p *Peer) PingTime() string { return p.pingTime.String() From 7ee49c32b70b0bd3e9709865c0b9c43d16c8f18c Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Jul 2014 17:10:54 +0200 Subject: [PATCH 15/26] Added update method and general service timer * disable catching up if no block received for longer than 10 seconds --- peer.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/peer.go b/peer.go index c73617ed5b..ffba695caa 100644 --- a/peer.go +++ b/peer.go @@ -121,7 +121,8 @@ type Peer struct { versionKnown bool // Last received pong message - lastPong int64 + lastPong int64 + lastBlockReceived time.Time host []byte port uint16 @@ -408,10 +409,7 @@ func (p *Peer) HandleInbound() { for i := msg.Data.Len() - 1; i >= 0; i-- { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) - //p.ethereum.StateManager().PrepareDefault(block) - //state := p.ethereum.StateManager().CurrentState() err = p.ethereum.StateManager().Process(block, false) - if err != nil { if ethutil.Config.Debug { peerlogger.Infof("Block %x failed\n", block.Hash()) @@ -422,6 +420,8 @@ func (p *Peer) HandleInbound() { } else { lastBlock = block } + + p.lastBlockReceived = time.Now() } if msg.Data.Len() <= 1 { @@ -561,6 +561,25 @@ func (p *Peer) HandleInbound() { p.Stop() } +// General update method +func (self *Peer) update() { + serviceTimer := time.NewTicker(5 * time.Second) + +out: + for { + select { + case <-serviceTimer.C: + if time.Since(self.lastBlockReceived) > 10*time.Second { + self.catchingUp = false + } + case <-self.quit: + break out + } + } + + serviceTimer.Stop() +} + func (p *Peer) Start() { peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String()) servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String()) @@ -583,6 +602,8 @@ func (p *Peer) Start() { go p.HandleOutbound() // Run the inbound handler in a new goroutine go p.HandleInbound() + // Run the general update handler + go p.update() // Wait a few seconds for startup and then ask for an initial ping time.Sleep(2 * time.Second) From 54f9ea14e197ad805f24592153f1b9e69f3bc5c3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 25 Jul 2014 09:57:47 +0200 Subject: [PATCH 16/26] Removed old S(DIV/MOD) --- ethvm/vm.go | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/ethvm/vm.go b/ethvm/vm.go index b81c8a189c..a93b56e605 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -282,20 +282,15 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { case SDIV: require(2) x, y := stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Div(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) + self.Printf(" %v / %v", y, x) + + if x.Cmp(ethutil.Big0) != 0 { + base.Div(y, x) } - // Push result on to the stack - stack.Push(z) + + self.Printf(" = %v", base) + // Pop result back on the stack + stack.Push(base) case MOD: require(2) x, y := stack.Popn() @@ -309,20 +304,14 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { case SMOD: require(2) x, y := stack.Popn() - // n > 2**255 - if x.Cmp(Pow256) > 0 { - x.Sub(Pow256, x) - } - if y.Cmp(Pow256) > 0 { - y.Sub(Pow256, y) - } - z := new(big.Int) - z.Mod(x, y) - if z.Cmp(Pow256) > 0 { - z.Sub(Pow256, z) - } - // Push result on to the stack - stack.Push(z) + + self.Printf(" %v %% %v", y, x) + + base.Mod(y, x) + + self.Printf(" = %v", base) + stack.Push(base) + case EXP: require(2) x, y := stack.Popn() From 41bd38147c2e5968283facf641b2444c09f53d14 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Jul 2014 11:24:44 +0200 Subject: [PATCH 17/26] Clean up and util methods --- ethchain/block.go | 13 ++++--------- ethereum.go | 13 +++++++------ ethutil/bytes.go | 1 + ethutil/config.go | 6 +++--- ethutil/path.go | 40 ++++++++++++++++++++++++++++++++++++++++ ethvm/vm.go | 5 +++-- peer.go | 15 +++++++++------ 7 files changed, 67 insertions(+), 26 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 437525e355..e00bcb24f8 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) diff --git a/ethereum.go b/ethereum.go index 18c1f8a232..799e9cb390 100644 --- a/ethereum.go +++ b/ethereum.go @@ -3,12 +3,6 @@ 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" @@ -18,6 +12,13 @@ import ( "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/ethrpc" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" ) const seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt" diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 34fff7d42c..53b8cf645d 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -98,6 +98,7 @@ func Bytes2Hex(d []byte) string { func Hex2Bytes(str string) []byte { h, _ := hex.DecodeString(str) + return h } diff --git a/ethutil/config.go b/ethutil/config.go index 41bece21d1..81052318e1 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 97f58ab7e5..27022bcfa4 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/ethvm/vm.go b/ethvm/vm.go index a93b56e605..e0a9d831ba 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 { diff --git a/peer.go b/peer.go index ffba695caa..691a2f5750 100644 --- a/peer.go +++ b/peer.go @@ -4,15 +4,16 @@ 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" "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") @@ -197,10 +198,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 } From 8e7c4f91e33bd99d3a4d320cdc59cf0bab3831b6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 10:33:30 +0200 Subject: [PATCH 18/26] Added ops --- ethutil/value.go | 60 +++++++++++++++++++++++++++++++++++++++++-- ethutil/value_test.go | 15 +++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/ethutil/value.go b/ethutil/value.go index 735a71dbc9..635683e666 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -1,7 +1,6 @@ package ethutil import ( - "bytes" "fmt" "math/big" "reflect" @@ -67,7 +66,8 @@ 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() + //return ReadVarint(bytes.NewReader(Val)) } else if Val, ok := val.Val.(*big.Int); ok { return Val.Uint64() } @@ -207,6 +207,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) } @@ -262,6 +269,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 a100f44bc8..710cbd8876 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) + } +} From d1d2b660dcc3c7539940a5e3d5a48ad10487bd8f Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 15:55:08 +0200 Subject: [PATCH 19/26] Prot --- peer.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/peer.go b/peer.go index 691a2f5750..f4314e35c7 100644 --- a/peer.go +++ b/peer.go @@ -22,7 +22,7 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 23 + ProtocolVersion = 25 // Interval for ping/pong message pingPongTimer = 2 * time.Second ) @@ -436,19 +436,20 @@ func (p *Peer) HandleInbound() { 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)) + b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0)) + + peerlogger.Infof("Attempting to catch (%x). Parent unknown\n", b.Hash()) + p.catchingUp = false - peerlogger.Infof("Attempting to catch (%x). Parent known\n", b.Hash()) - p.catchingUp = false + p.CatchupWithPeer(b.Hash()) - p.CatchupWithPeer(b.Hash()) + peerlogger.Infoln(b) - peerlogger.Infoln(b) + /* + peerlogger.Infoln("Attempting to catch. Parent known") + p.catchingUp = false + p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) */ - 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 From 6e94c024e476a0e96b81d1cdd60dbb88b723593a Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 23:31:33 +0200 Subject: [PATCH 20/26] Added big data test and updating to reader --- ethutil/rlp.go | 139 +++++++++++++++++++++++--------------------- ethutil/rlp_test.go | 11 ++++ 2 files changed, 85 insertions(+), 65 deletions(-) diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 195ef0efbb..333a849279 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -55,8 +55,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)) @@ -72,74 +71,20 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { } 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) - slice = append(slice, obj) - - i += (prevPos - pos) - pos = prevPos + length := ReadVarInt(reader.Next(int(char - 0xf7))) + for i := uint64(0); i < length; i++ { + obj := DecodeWithReader(reader) + if obj != nil { + slice = append(slice, obj) + } else { + break + } } - return slice, pos - default: - panic(fmt.Sprintf("byte not supported: %q", char)) } - return slice, 0 + return slice } var ( @@ -223,3 +168,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 095c01ecc3..90057ab42d 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" { From 6fd2401cdf792996c0183f896412831dd335377a Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 23:33:18 +0200 Subject: [PATCH 21/26] Fixed issue with var int reading. Reading uneven byte slices were broken. --- ethutil/bytes.go | 30 +++++++++++++++++++++++++++--- ethutil/value.go | 1 - 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 53b8cf645d..eca2cc3669 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -45,15 +45,15 @@ func BytesToNumber(b []byte) uint64 { // // Read a variable length number in big endian byte order func ReadVarint(reader *bytes.Reader) (ret uint64) { - if reader.Len() == 8 { + if reader.Len() > 4 { var num uint64 binary.Read(reader, binary.BigEndian, &num) ret = uint64(num) - } else if reader.Len() == 4 { + } else if reader.Len() > 2 { var num uint32 binary.Read(reader, binary.BigEndian, &num) ret = uint64(num) - } else if reader.Len() == 2 { + } else if reader.Len() > 0 { var num uint16 binary.Read(reader, binary.BigEndian, &num) ret = uint64(num) @@ -66,6 +66,30 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) { return ret } +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 + d := LeftPadBytes(buff, 4) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + case l > 1: + var num uint16 + d := LeftPadBytes(buff, 2) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + default: + var num uint8 + binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) + ret = uint64(num) + } + + return +} + // Binary length // // Returns the true binary length of the given number diff --git a/ethutil/value.go b/ethutil/value.go index 635683e666..85dc44ed67 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -67,7 +67,6 @@ func (val *Value) Uint() uint64 { return uint64(Val) } else if Val, ok := val.Val.([]byte); ok { return new(big.Int).SetBytes(Val).Uint64() - //return ReadVarint(bytes.NewReader(Val)) } else if Val, ok := val.Val.(*big.Int); ok { return Val.Uint64() } From 5ca29381175d553bb764040e8cd892a4ba370645 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 23:33:33 +0200 Subject: [PATCH 22/26] Fixed --- ethutil/bytes.go | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/ethutil/bytes.go b/ethutil/bytes.go index eca2cc3669..4027e39867 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -44,28 +44,6 @@ 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() > 4 { - var num uint64 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } else if reader.Len() > 2 { - var num uint32 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } else if reader.Len() > 0 { - var num uint16 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } else { - var num uint8 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } - - return ret -} - func ReadVarInt(buff []byte) (ret uint64) { switch l := len(buff); { case l > 4: From 74d701202583b49d29f5dbca00aec419bdee8e1d Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 23:33:59 +0200 Subject: [PATCH 23/26] Added temp seed --- ethereum.go | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/ethereum.go b/ethereum.go index 799e9cb390..395eba954c 100644 --- a/ethereum.go +++ b/ethereum.go @@ -3,10 +3,8 @@ package eth import ( "container/list" "fmt" - "io/ioutil" "math/rand" "net" - "net/http" "strconv" "strings" "sync" @@ -21,7 +19,10 @@ import ( "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") @@ -421,22 +422,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) { From 27f892265312255811867fab83acbeefa1626cec Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Jul 2014 23:34:21 +0200 Subject: [PATCH 24/26] Increased block request amount --- peer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peer.go b/peer.go index f4314e35c7..d977b20863 100644 --- a/peer.go +++ b/peer.go @@ -786,7 +786,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()) From 1f9894c0845a5259adbfd30fe3a86631e6403b8d Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 00:31:15 +0200 Subject: [PATCH 25/26] Old code removed and renamed amount to balance --- ethchain/block.go | 27 ------------ ethchain/block_chain.go | 7 ++-- ethchain/state_transition.go | 15 +++---- ethchain/transaction_pool.go | 79 ++---------------------------------- ethpub/types.go | 7 ++-- ethrpc/packages.go | 7 ++-- ethstate/state.go | 5 ++- ethstate/state_object.go | 39 +++++++++--------- ethvm/vm.go | 10 ++--- 9 files changed, 52 insertions(+), 144 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index e00bcb24f8..ac56f58c32 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -125,33 +125,6 @@ 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) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 1a26627871..2509037988 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") @@ -280,7 +281,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) } } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 266328ce8e..02a8e0e825 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 @@ -240,8 +241,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 diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 21c6ea3de3..b0d62fd91a 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/ethpub/types.go b/ethpub/types.go index 5cfa2705e1..faf75bbe14 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -3,12 +3,13 @@ package ethpub import ( "encoding/json" "fmt" + "strings" + "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 @@ -175,9 +176,9 @@ func (c *PStateObject) GetStorage(address string) string { return "" } -func (c *PStateObject) Value() string { +func (c *PStateObject) Balance() string { if c.object != nil { - return c.object.Amount.String() + return c.object.Balance.String() } return "" diff --git a/ethrpc/packages.go b/ethrpc/packages.go index 0662f0edd1..d307d03143 100644 --- a/ethrpc/packages.go +++ b/ethrpc/packages.go @@ -3,10 +3,11 @@ 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/ethpub" + "github.com/ethereum/eth-go/ethutil" ) type EthereumApi struct { @@ -272,7 +273,7 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { return err } state := p.ethp.GetStateObject(args.Address) - *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) + *reply = NewSuccessRes(BalanceRes{Balance: state.Balance(), Address: args.Address}) return nil } diff --git a/ethstate/state.go b/ethstate/state.go index 51b585d4da..693a591c35 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") @@ -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 diff --git a/ethstate/state_object.go b/ethstate/state_object.go index ab14b86049..5932fbee6f 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 @@ -78,7 +79,7 @@ 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 := &StateObject{address: address, Balance: new(big.Int), gasPool: new(big.Int)} object.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, "")) object.storage = make(Storage) object.gasPool = new(big.Int) @@ -86,9 +87,9 @@ func NewStateObject(addr []byte) *StateObject { 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.Balance = balance contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root))) return contract @@ -103,7 +104,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 +191,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 +214,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 +248,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 +291,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,14 +310,14 @@ 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.Balance = decoder.Get(1).BigInt() c.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.storage = make(map[string]*ethutil.Value) c.gasPool = new(big.Int) diff --git a/ethvm/vm.go b/ethvm/vm.go index e0a9d831ba..e469fa826b 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -682,7 +682,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // 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) @@ -700,7 +700,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // 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 { @@ -736,8 +736,8 @@ 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) + 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) @@ -784,7 +784,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() From 42d47ecfb09ac0b419db5722602d9b02e21f2457 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Jul 2014 11:26:38 +0200 Subject: [PATCH 26/26] Removed peer disconnect on pong timeout. Fixes #106 This mechanism wasn't very accurate so it has been removed. --- peer.go | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/peer.go b/peer.go index d977b20863..4b0523e705 100644 --- a/peer.go +++ b/peer.go @@ -294,12 +294,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() @@ -354,7 +356,7 @@ func (p *Peer) HandleInbound() { } 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, "")) @@ -363,11 +365,17 @@ 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) + p.pingTime = time.Since(p.pingStartTime) case ethwire.MsgBlockTy: // Get all blocks and process them - var block, lastBlock *ethchain.Block - var err error + //var block, lastBlock *ethchain.Block + //var err error + + var ( + block, lastBlock *ethchain.Block + blockChain = p.ethereum.BlockChain() + err error + ) // Make sure we are actually receiving anything if msg.Data.Len()-1 > 1 && p.diverted { @@ -383,11 +391,11 @@ func (p *Peer) HandleInbound() { 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()) { + if !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) { + if blockChain.HasBlockWithPrevHash(block.PrevHash) { p.diverted = false - if !p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) { + if !blockChain.FindCanonicalChainFromMsg(msg, block.PrevHash) { p.SyncWithPeerToLastKnown() break nextMsg }