diff options
author | obscuren <geffobscura@gmail.com> | 2014-06-17 00:25:24 +0800 |
---|---|---|
committer | obscuren <geffobscura@gmail.com> | 2014-06-17 00:25:24 +0800 |
commit | 5a0e75173626704c3d58be582dff318218569ef3 (patch) | |
tree | 801ec6d33fe21e46230a3082cd069534f64aa251 | |
parent | 006ac772e6c81271a84ff56e00527b2adbc0129c (diff) | |
parent | ff0f15f7634ca713b0ce8232a8fa63eec5c3fad7 (diff) | |
download | go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar.gz go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar.bz2 go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar.lz go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar.xz go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.tar.zst go-tangerine-5a0e75173626704c3d58be582dff318218569ef3.zip |
Merge branch 'release/0.5.13'
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | ethchain/asm.go | 12 | ||||
-rw-r--r-- | ethchain/block.go | 30 | ||||
-rw-r--r-- | ethchain/block_chain.go | 19 | ||||
-rw-r--r-- | ethchain/closure.go | 26 | ||||
-rw-r--r-- | ethchain/deprecated.go | 236 | ||||
-rw-r--r-- | ethchain/error.go | 35 | ||||
-rw-r--r-- | ethchain/fees.go | 24 | ||||
-rw-r--r-- | ethchain/stack.go | 6 | ||||
-rw-r--r-- | ethchain/state.go | 173 | ||||
-rw-r--r-- | ethchain/state_manager.go | 156 | ||||
-rw-r--r-- | ethchain/state_object.go | 74 | ||||
-rw-r--r-- | ethchain/state_transition.go | 260 | ||||
-rw-r--r-- | ethchain/transaction.go | 22 | ||||
-rw-r--r-- | ethchain/transaction_pool.go | 44 | ||||
-rw-r--r-- | ethchain/types.go | 53 | ||||
-rw-r--r-- | ethchain/vm.go | 166 | ||||
-rw-r--r-- | ethminer/miner.go | 15 | ||||
-rw-r--r-- | ethpub/pub.go | 10 | ||||
-rw-r--r-- | ethpub/types.go | 13 | ||||
-rw-r--r-- | ethutil/big.go | 4 | ||||
-rw-r--r-- | ethutil/common.go | 4 | ||||
-rw-r--r-- | ethutil/config.go | 39 | ||||
-rw-r--r-- | ethutil/keypair.go | 6 | ||||
-rw-r--r-- | ethutil/rlp.go | 1 | ||||
-rw-r--r-- | ethwire/messaging.go | 163 | ||||
-rw-r--r-- | peer.go | 30 |
27 files changed, 1271 insertions, 352 deletions
@@ -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 5.0 RC12". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0 RC13". 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/asm.go b/ethchain/asm.go index 430a89450..c267f9b55 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -25,16 +25,10 @@ func Disassemble(script []byte) (asm []string) { pc.Add(pc, ethutil.Big1) a := int64(op) - int64(PUSH1) + 1 data := script[pc.Int64() : pc.Int64()+a] - val := ethutil.BigD(data) - - var b []byte - if val.Int64() == 0 { - b = []byte{0} - } else { - b = val.Bytes() + if len(data) == 0 { + data = []byte{0} } - - asm = append(asm, fmt.Sprintf("0x%x", b)) + asm = append(asm, fmt.Sprintf("%#x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethchain/block.go b/ethchain/block.go index 73e29f878..fee4a2d59 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -154,6 +154,36 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { return true } +func (block *Block) CalcGasLimit(parent *Block) *big.Int { + if block.Number.Cmp(big.NewInt(0)) == 0 { + return ethutil.BigPow(10, 6) + } + + previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit) + current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5)) + curInt := new(big.Int).Div(current.Num(), current.Denom()) + + result := new(big.Int).Add(previous, curInt) + result.Div(result, big.NewInt(1024)) + + min := ethutil.BigPow(10, 4) + + return ethutil.BigMax(min, result) + /* + base := new(big.Int) + base2 := new(big.Int) + parentGL := bc.CurrentBlock.GasLimit + parentUsed := bc.CurrentBlock.GasUsed + + base.Mul(parentGL, big.NewInt(1024-1)) + base2.Mul(parentUsed, big.NewInt(6)) + base2.Div(base2, big.NewInt(5)) + base.Add(base, base2) + base.Div(base, big.NewInt(1024)) + */ + +} + func (block *Block) BlockInfo() BlockInfo { bi := BlockInfo{} data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index b45d254b5..19b5248d7 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -55,6 +55,8 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block { nil, "") + block.MinGasPrice = big.NewInt(10000000000000) + if bc.CurrentBlock != nil { var mul *big.Int if block.Time < lastBlockTime+42 { @@ -72,19 +74,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block { block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1) - // max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024) - base := new(big.Int) - base2 := new(big.Int) - parentGL := bc.CurrentBlock.GasLimit - parentUsed := bc.CurrentBlock.GasUsed - - base.Mul(parentGL, big.NewInt(1024-1)) - base2.Mul(parentUsed, big.NewInt(6)) - base2.Div(base2, big.NewInt(5)) - base.Add(base, base2) - base.Div(base, big.NewInt(1024)) - - block.GasLimit = ethutil.BigMax(big.NewInt(10000), base) + block.GasLimit = block.CalcGasLimit(bc.CurrentBlock) } return block @@ -271,7 +261,7 @@ func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block { func AddTestNetFunds(block *Block) { for _, addr := range []string{ - "8a40bfaa73256b60764c1bf40675a99083efb075", + "51ba59315b3a95761d0863b05ccc7a7f54703d99", "e4157b34ea9615cfbde6b4fda419828124b70c78", "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", "6c386a4b26f73c802f34673f7248bb118f97424a", @@ -285,7 +275,6 @@ func AddTestNetFunds(block *Block) { account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200) block.state.UpdateStateObject(account) } - log.Printf("%x\n", block.RlpEncode()) } func (bc *BlockChain) setLastBlock() { diff --git a/ethchain/closure.go b/ethchain/closure.go index 5c9c3e47c..32b297e90 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -17,7 +17,7 @@ type ClosureRef interface { // Basic inline closure object which implement the 'closure' interface type Closure struct { - callee ClosureRef + caller ClosureRef object *StateObject Script []byte State *State @@ -28,12 +28,14 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure { - c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} +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} - // In most cases gas, price and value are pointers to transaction objects + // 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.Gas = new(big.Int).Set(gas) c.Price = new(big.Int).Set(price) c.UsedGas = new(big.Int) @@ -83,11 +85,11 @@ func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, *big.Int, e } func (c *Closure) Return(ret []byte) []byte { - // Return the remaining gas to the callee - // If no callee is present return it to + // Return the remaining gas to the caller + // If no caller is present return it to // the origin (i.e. contract or tx) - if c.callee != nil { - c.callee.ReturnGas(c.Gas, c.Price, c.State) + if c.caller != nil { + c.caller.ReturnGas(c.Gas, c.Price, c.State) } else { c.object.ReturnGas(c.Gas, c.Price, c.State) } @@ -107,7 +109,7 @@ func (c *Closure) UseGas(gas *big.Int) bool { return true } -// Implement the Callee interface +// 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) @@ -118,8 +120,8 @@ func (c *Closure) Object() *StateObject { return c.object } -func (c *Closure) Callee() ClosureRef { - return c.callee +func (c *Closure) Caller() ClosureRef { + return c.caller } func (c *Closure) N() *big.Int { diff --git a/ethchain/deprecated.go b/ethchain/deprecated.go new file mode 100644 index 000000000..0985fa25d --- /dev/null +++ b/ethchain/deprecated.go @@ -0,0 +1,236 @@ +package ethchain + +import ( + "bytes" + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { + contract := MakeContract(tx, state) + if contract != nil { + state.states[string(tx.CreationAddress())] = contract.state + + return contract + } + + return nil +} + +func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) { + account := state.GetAccount(tx.Sender()) + + err = account.ConvertGas(tx.Gas, tx.GasPrice) + if err != nil { + ethutil.Config.Log.Debugln(err) + return + } + + closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice) + vm := NewVm(state, sm, RuntimeVars{ + Origin: account.Address(), + BlockNumber: block.BlockInfo().Number, + PrevHash: block.PrevHash, + Coinbase: block.Coinbase, + Time: block.Time, + Diff: block.Difficulty, + Value: tx.Value, + //Price: tx.GasPrice, + }) + ret, gas, err = closure.Call(vm, tx.Data, nil) + + // Update the account (refunds) + state.UpdateStateObject(account) + state.UpdateStateObject(object) + + return +} + +func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, 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 { + ethutil.Config.Log.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 + self.Ethereum.Reactor().Post("newTx:post", tx) + }() + + txTotalBytes := big.NewInt(int64(len(tx.Data))) + //fmt.Println("txTotalBytes", txTotalBytes) + //txTotalBytes.Div(txTotalBytes, ethutil.Big32) + addGas(new(big.Int).Mul(txTotalBytes, GasData)) + + 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 { + state.UpdateStateObject(sender) + err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) + return + } + + coinbase.BuyGas(gas, tx.GasPrice) + state.UpdateStateObject(coinbase) + fmt.Printf("1. root %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) + state.UpdateStateObject(sender) + fmt.Printf("3. root %x\n", state.Root()) + + // Add the amount to receivers account which should conclude this transaction + receiver.AddAmount(tx.Value) + state.UpdateStateObject(receiver) + fmt.Printf("2. root %x\n", state.Root()) + } + + ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) + + return +} + +func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { + /* + Applies transactions to the given state and creates new + state objects where needed. + + If said objects needs to be created + run the initialization script provided by the transaction and + assume there's a return value. The return value will be set to + the script section of the state object. + */ + var ( + addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } + gas = new(big.Int) + script []byte + ) + totalGasUsed = big.NewInt(0) + snapshot := state.Snapshot() + + ca := state.GetAccount(coinbase) + // Apply the transaction to the current state + gas, err = sm.ProcessTransaction(tx, ca, state, false) + addTotalGas(gas) + fmt.Println("gas used by tx", gas) + + if tx.CreatesContract() { + if err == nil { + // Create a new state object and the transaction + // as it's data provider. + contract := sm.MakeStateObject(state, tx) + if contract != nil { + fmt.Println(Disassemble(contract.Init())) + // Evaluate the initialization script + // and use the return value as the + // script section for the state object. + script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block) + fmt.Println("gas used by eval", gas) + addTotalGas(gas) + fmt.Println("total =", totalGasUsed) + + fmt.Println("script len =", len(script)) + + if err != nil { + err = fmt.Errorf("[STATE] Error during init script run %v", err) + return + } + contract.script = script + state.UpdateStateObject(contract) + } else { + err = fmt.Errorf("[STATE] Unable to create contract") + } + } else { + err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender()) + } + } else { + // Find the state object at the "recipient" address. If + // there's an object attempt to run the script. + stateObject := state.GetStateObject(tx.Recipient) + if err == nil && stateObject != nil && len(stateObject.Script()) > 0 { + _, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block) + addTotalGas(gas) + } + } + + parent := sm.bc.GetBlock(block.PrevHash) + total := new(big.Int).Add(block.GasUsed, totalGasUsed) + limit := block.CalcGasLimit(parent) + if total.Cmp(limit) > 0 { + state.Revert(snapshot) + err = GasLimitError(total, limit) + } + + return +} + +// Apply transactions uses the transaction passed to it and applies them onto +// the current processing state. +func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { + // Process each transaction/contract + var receipts []*Receipt + var validTxs []*Transaction + var ignoredTxs []*Transaction // Transactions which go over the gasLimit + + totalUsedGas := big.NewInt(0) + + for _, tx := range txs { + usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx) + if err != nil { + if IsNonceErr(err) { + continue + } + if IsGasLimitErr(err) { + ignoredTxs = append(ignoredTxs, tx) + // We need to figure out if we want to do something with thse txes + ethutil.Config.Log.Debugln("Gastlimit:", err) + continue + } + + ethutil.Config.Log.Infoln(err) + } + + accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) + receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} + + receipts = append(receipts, receipt) + validTxs = append(validTxs, tx) + } + + fmt.Println("################# MADE\n", receipts, "\n############################") + + // Update the total gas used for the block (to be mined) + block.GasUsed = totalUsedGas + + return receipts, validTxs +} diff --git a/ethchain/error.go b/ethchain/error.go index 8d37b0208..2cf09a1ec 100644 --- a/ethchain/error.go +++ b/ethchain/error.go @@ -2,6 +2,7 @@ package ethchain import ( "fmt" + "math/big" ) // Parent error. In case a parent is unknown this error will be thrown @@ -43,6 +44,23 @@ func IsValidationErr(err error) bool { return ok } +type GasLimitErr struct { + Message string + Is, Max *big.Int +} + +func IsGasLimitErr(err error) bool { + _, ok := err.(*GasLimitErr) + + return ok +} +func (err *GasLimitErr) Error() string { + return err.Message +} +func GasLimitError(is, max *big.Int) *GasLimitErr { + return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max} +} + type NonceErr struct { Message string Is, Exp uint64 @@ -61,3 +79,20 @@ func IsNonceErr(err error) bool { return ok } + +type OutOfGasErr struct { + Message string +} + +func OutOfGasError() *OutOfGasErr { + return &OutOfGasErr{Message: "Out of gas"} +} +func (self *OutOfGasErr) Error() string { + return self.Message +} + +func IsOutOfGasErr(err error) bool { + _, ok := err.(*OutOfGasErr) + + return ok +} diff --git a/ethchain/fees.go b/ethchain/fees.go index c0a5d2d88..743be86a2 100644 --- a/ethchain/fees.go +++ b/ethchain/fees.go @@ -4,30 +4,6 @@ import ( "math/big" ) -var TxFeeRat *big.Int = big.NewInt(100000000000000) - -var TxFee *big.Int = big.NewInt(100) -var StepFee *big.Int = big.NewInt(1) -var StoreFee *big.Int = big.NewInt(5) -var DataFee *big.Int = big.NewInt(20) -var ExtroFee *big.Int = big.NewInt(40) -var CryptoFee *big.Int = big.NewInt(20) -var ContractFee *big.Int = big.NewInt(100) - var BlockReward *big.Int = big.NewInt(1.5e+18) var UncleReward *big.Int = big.NewInt(1.125e+18) var UncleInclusionReward *big.Int = big.NewInt(1.875e+17) - -var Period1Reward *big.Int = new(big.Int) -var Period2Reward *big.Int = new(big.Int) -var Period3Reward *big.Int = new(big.Int) -var Period4Reward *big.Int = new(big.Int) - -func InitFees() { - StepFee.Mul(StepFee, TxFeeRat) - StoreFee.Mul(StoreFee, TxFeeRat) - DataFee.Mul(DataFee, TxFeeRat) - ExtroFee.Mul(ExtroFee, TxFeeRat) - CryptoFee.Mul(CryptoFee, TxFeeRat) - ContractFee.Mul(ContractFee, TxFeeRat) -} diff --git a/ethchain/stack.go b/ethchain/stack.go index bf34e6ea9..37d1f84b9 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -111,6 +111,12 @@ func (m *Memory) Set(offset, size int64, value []byte) { 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 { return m.store[offset : offset+size] } diff --git a/ethchain/state.go b/ethchain/state.go index 5af748e00..9a9d0a278 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -16,12 +16,14 @@ type State struct { // Nested states states map[string]*State + stateObjects map[string]*StateObject + manifest *Manifest } // Create a new state from a given trie func NewState(trie *ethutil.Trie) *State { - return &State{trie: trie, states: make(map[string]*State), manifest: NewManifest()} + return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()} } // Resets the trie and all siblings @@ -29,16 +31,24 @@ func (s *State) Reset() { s.trie.Undo() // Reset all nested states - for _, state := range s.states { - state.Reset() + for _, stateObject := range s.stateObjects { + if stateObject.state == nil { + continue + } + + stateObject.state.Reset() } } // Syncs the trie and all siblings func (s *State) Sync() { // Sync all nested states - for _, state := range s.states { - state.Sync() + for _, stateObject := range s.stateObjects { + if stateObject.state == nil { + continue + } + + stateObject.state.Sync() } s.trie.Sync() @@ -54,62 +64,66 @@ func (s *State) EachStorage(cb ethutil.EachCallback) { it.Each(cb) } -func (s *State) GetStateObject(addr []byte) *StateObject { - data := s.trie.Get(string(addr)) - if data == "" { - return nil +func (self *State) UpdateStateObject(stateObject *StateObject) { + addr := stateObject.Address() + + if self.stateObjects[string(addr)] == nil { + self.stateObjects[string(addr)] = stateObject } - stateObject := NewStateObjectFromBytes(addr, []byte(data)) + ethutil.Config.Db.Put(ethutil.Sha3Bin(stateObject.Script()), stateObject.Script()) - // Check if there's a cached state for this contract - cachedStateObject := s.states[string(addr)] - if cachedStateObject != nil { - //fmt.Printf("get cached #%d %x addr: %x\n", cachedStateObject.trie.Cache().Len(), cachedStateObject.Root(), addr[0:4]) - stateObject.state = cachedStateObject - } + self.trie.Update(string(addr), string(stateObject.RlpEncode())) - return stateObject + self.manifest.AddObjectChange(stateObject) } -// Updates any given state object -func (s *State) UpdateStateObject(object *StateObject) { - addr := object.Address() - - if object.state != nil && s.states[string(addr)] == nil { - s.states[string(addr)] = object.state - //fmt.Printf("update cached #%d %x addr: %x\n", object.state.trie.Cache().Len(), object.state.Root(), addr[0:4]) +func (self *State) GetStateObject(addr []byte) *StateObject { + stateObject := self.stateObjects[string(addr)] + if stateObject != nil { + return stateObject } - ethutil.Config.Db.Put(ethutil.Sha3Bin(object.Script()), object.Script()) + data := self.trie.Get(string(addr)) + if len(data) == 0 { + return nil + } - s.trie.Update(string(addr), string(object.RlpEncode())) + stateObject = NewStateObjectFromBytes(addr, []byte(data)) + self.stateObjects[string(addr)] = stateObject - s.manifest.AddObjectChange(object) + return stateObject } -func (s *State) GetAccount(addr []byte) (account *StateObject) { - data := s.trie.Get(string(addr)) - if data == "" { - account = NewAccount(addr, big.NewInt(0)) - } else { - account = NewStateObjectFromBytes(addr, []byte(data)) +func (self *State) GetOrNewStateObject(addr []byte) *StateObject { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + stateObject = NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject } - return + return stateObject +} + +func (self *State) GetAccount(addr []byte) *StateObject { + return self.GetOrNewStateObject(addr) } func (s *State) Cmp(other *State) bool { return s.trie.Cmp(other.trie) } -func (s *State) Copy() *State { - state := NewState(s.trie.Copy()) - for k, subState := range s.states { - state.states[k] = subState.Copy() +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 state + return nil } func (s *State) Snapshot() *State { @@ -165,3 +179,84 @@ func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage } + +/* + +// Resets the trie and all siblings +func (s *State) Reset() { + s.trie.Undo() + + // Reset all nested states + for _, state := range s.states { + state.Reset() + } +} + +// Syncs the trie and all siblings +func (s *State) Sync() { + // Sync all nested states + for _, state := range s.states { + state.Sync() + } + + s.trie.Sync() +} +func (s *State) GetStateObject(addr []byte) *StateObject { + data := s.trie.Get(string(addr)) + if data == "" { + return nil + } + + stateObject := NewStateObjectFromBytes(addr, []byte(data)) + + // Check if there's a cached state for this contract + cachedStateObject := s.states[string(addr)] + if cachedStateObject != nil { + //fmt.Printf("get cached #%d %x addr: %x\n", cachedStateObject.trie.Cache().Len(), cachedStateObject.Root(), addr[0:4]) + stateObject.state = cachedStateObject + } + + return stateObject +} + +// Updates any given state object +func (s *State) UpdateStateObject(object *StateObject) { + addr := object.Address() + + if object.state != nil && s.states[string(addr)] == nil { + s.states[string(addr)] = object.state + } + + ethutil.Config.Db.Put(ethutil.Sha3Bin(object.Script()), object.Script()) + + s.trie.Update(string(addr), string(object.RlpEncode())) + + s.manifest.AddObjectChange(object) +} + +func (s *State) GetAccount(addr []byte) (account *StateObject) { + data := s.trie.Get(string(addr)) + if data == "" { + account = NewAccount(addr, big.NewInt(0)) + } else { + account = NewStateObjectFromBytes(addr, []byte(data)) + } + + // Check if there's a cached state for this contract + cachedStateObject := s.states[string(addr)] + if cachedStateObject != nil { + account.state = cachedStateObject + } + + return +} + +func (s *State) Copy() *State { + state := NewState(s.trie.Copy()) + for k, subState := range s.states { + state.states[k] = subState.Copy() + } + + return state +} +*/ diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index f1c09b819..36bb14846 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -97,100 +97,49 @@ func (sm *StateManager) BlockChain() *BlockChain { return sm.bc } -func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { - contract := MakeContract(tx, state) - if contract != nil { - state.states[string(tx.CreationAddress())] = contract.state - - return contract - } - - return nil -} +func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { + var ( + receipts Receipts + handled, unhandled Transactions + totalUsedGas = big.NewInt(0) + err error + ) -// Apply transactions uses the transaction passed to it and applies them onto -// the current processing state. -func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { - // Process each transaction/contract - var receipts []*Receipt - var validTxs []*Transaction - totalUsedGas := big.NewInt(0) - for _, tx := range txs { - usedGas, err := sm.ApplyTransaction(state, block, tx) +done: + for i, tx := range txs { + txGas := new(big.Int).Set(tx.Gas) + st := NewStateTransition(coinbase, tx, state, block) + err = st.TransitionState() if err != nil { - if IsNonceErr(err) { + switch { + case IsNonceErr(err): + err = nil // ignore error continue - } + case IsGasLimitErr(err): + unhandled = txs[i:] - ethutil.Config.Log.Infoln(err) + break done + default: + ethutil.Config.Log.Infoln(err) + } } - accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) + // Notify all subscribers + self.Ethereum.Reactor().Post("newTx:post", tx) + + txGas.Sub(txGas, st.gas) + accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas)) receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} receipts = append(receipts, receipt) - validTxs = append(validTxs, tx) + handled = append(handled, tx) } - return receipts, validTxs -} + fmt.Println("################# MADE\n", receipts, "\n############################") -func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { - /* - Applies transactions to the given state and creates new - state objects where needed. + parent.GasUsed = totalUsedGas - If said objects needs to be created - run the initialization script provided by the transaction and - assume there's a return value. The return value will be set to - the script section of the state object. - */ - var ( - addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } - gas = new(big.Int) - script []byte - ) - totalGasUsed = big.NewInt(0) - - // Apply the transaction to the current state - gas, err = sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) - addTotalGas(gas) - - if tx.CreatesContract() { - if err == nil { - // Create a new state object and the transaction - // as it's data provider. - contract := sm.MakeStateObject(state, tx) - if contract != nil { - // Evaluate the initialization script - // and use the return value as the - // script section for the state object. - script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block) - addTotalGas(gas) - - if err != nil { - err = fmt.Errorf("[STATE] Error during init script run %v", err) - return - } - contract.script = script - state.UpdateStateObject(contract) - } else { - err = fmt.Errorf("[STATE] Unable to create contract") - } - } else { - err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender()) - } - } else { - // Find the state object at the "recipient" address. If - // there's an object attempt to run the script. - stateObject := state.GetStateObject(tx.Recipient) - if err == nil && stateObject != nil && len(stateObject.Script()) > 0 { - _, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block) - addTotalGas(gas) - } - } - - return + return receipts, handled, unhandled, err } func (sm *StateManager) Process(block *Block, dontReact bool) error { @@ -212,7 +161,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea hash := block.Hash() if sm.bc.HasBlock(hash) { - //fmt.Println("[STATE] We already have this block, ignoring") return nil } @@ -227,9 +175,14 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil { return ParentError(block.PrevHash) } + fmt.Println(block.Receipts()) + + coinbase := state.GetOrNewStateObject(block.Coinbase) + coinbase.SetGasPool(block.CalcGasLimit(parent)) // Process the transactions on to current block - sm.ApplyTransactions(state, parent, block.Transactions()) + //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) + sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) // Block validation if err := sm.ValidateBlock(block); err != nil { @@ -244,7 +197,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea return err } - //if !sm.compState.Cmp(state) { if !block.State().Cmp(state) { return fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) } @@ -337,13 +289,6 @@ func CalculateBlockReward(block *Block, uncleLength int) *big.Int { base.Add(base, UncleInclusionReward) } - lastCumulGasUsed := big.NewInt(0) - for _, r := range block.Receipts() { - usedGas := new(big.Int).Sub(r.CumulativeGasUsed, lastCumulGasUsed) - usedGas.Add(usedGas, r.Tx.GasPrice) - base.Add(base, usedGas) - } - return base.Add(base, BlockReward) } @@ -375,35 +320,6 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) { - account := state.GetAccount(tx.Sender()) - - err = account.ConvertGas(tx.Gas, tx.GasPrice) - if err != nil { - ethutil.Config.Log.Debugln(err) - return - } - - closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice) - vm := NewVm(state, sm, RuntimeVars{ - Origin: account.Address(), - BlockNumber: block.BlockInfo().Number, - PrevHash: block.PrevHash, - Coinbase: block.Coinbase, - Time: block.Time, - Diff: block.Difficulty, - Value: tx.Value, - //Price: tx.GasPrice, - }) - ret, gas, err = closure.Call(vm, tx.Data, nil) - - // Update the account (refunds) - state.UpdateStateObject(account) - state.UpdateStateObject(object) - - return -} - func (sm *StateManager) notifyChanges(state *State) { for addr, stateObject := range state.manifest.objectChanges { sm.Ethereum.Reactor().Post("object:"+addr, stateObject) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 3e9c6df40..1445bcd82 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -17,6 +17,11 @@ type StateObject struct { state *State script []byte initScript []byte + + // 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 } // Converts an transaction in to a state object @@ -38,6 +43,10 @@ func MakeContract(tx *Transaction, state *State) *StateObject { return nil } +func NewStateObject(addr []byte) *StateObject { + return &StateObject{address: addr, Amount: new(big.Int)} +} + func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { contract := &StateObject{address: address, Amount: Amount, Nonce: 0} contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) @@ -77,7 +86,7 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { addr := ethutil.BigToBytes(num, 256) - //fmt.Println("storing", val.BigInt(), "@", num) + //fmt.Printf("sstore %x => %v\n", addr, val) c.SetAddr(addr, val) } @@ -102,16 +111,22 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { - remainder := new(big.Int).Mul(gas, price) - c.AddAmount(remainder) + /* + remainder := new(big.Int).Mul(gas, price) + c.AddAmount(remainder) + */ } func (c *StateObject) AddAmount(amount *big.Int) { c.SetAmount(new(big.Int).Add(c.Amount, amount)) + + ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%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)) + + ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SetAmount(amount *big.Int) { @@ -129,6 +144,53 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error { return nil } +func (self *StateObject) SetGasPool(gasLimit *big.Int) { + self.gasPool = new(big.Int).Set(gasLimit) + + ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%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 { + stCopy := &StateObject{} + stCopy.address = make([]byte, len(self.address)) + copy(stCopy.address, self.address) + stCopy.Amount = new(big.Int).Set(self.Amount) + stCopy.ScriptHash = make([]byte, len(self.ScriptHash)) + copy(stCopy.ScriptHash, self.ScriptHash) + stCopy.Nonce = self.Nonce + if self.state != nil { + stCopy.state = self.state.Copy() + } + stCopy.script = make([]byte, len(self.script)) + copy(stCopy.script, self.script) + stCopy.initScript = make([]byte, len(self.initScript)) + copy(stCopy.initScript, self.initScript) + + return stCopy +} + // Returns the address of the contract/account func (c *StateObject) Address() []byte { return c.address @@ -153,14 +215,14 @@ func (c *StateObject) RlpEncode() []byte { root = "" } - return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, ethutil.Sha3Bin(c.script)}) + return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethutil.Sha3Bin(c.script)}) } func (c *StateObject) RlpDecode(data []byte) { decoder := ethutil.NewValueFromBytes(data) - c.Amount = decoder.Get(0).BigInt() - c.Nonce = decoder.Get(1).Uint() + c.Nonce = decoder.Get(0).Uint() + c.Amount = decoder.Get(1).BigInt() c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.ScriptHash = decoder.Get(3).Bytes() diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go new file mode 100644 index 000000000..25efd64cc --- /dev/null +++ b/ethchain/state_transition.go @@ -0,0 +1,260 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +/* + * The State transitioning model + * + * A state transition is a change made when a transaction is applied to the current world state + * The state transitioning model does all all the necessary work to work out a valid new state root. + * 1) Nonce handling + * 2) Pre pay / buy gas of the coinbase (miner) + * 3) Create a new state object if the recipient is \0*32 + * 4) Value transfer + * == If contract creation == + * 4a) Attempt to run transaction data + * 4b) If valid, use result as code for the new state object + * == end == + * 5) Run Script section + * 6) Derive new state root + */ +type StateTransition struct { + coinbase []byte + tx *Transaction + gas *big.Int + state *State + block *Block + + cb, rec, sen *StateObject +} + +func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition { + return &StateTransition{coinbase.Address(), tx, new(big.Int), state, block, coinbase, nil, nil} +} + +func (self *StateTransition) Coinbase() *StateObject { + if self.cb != nil { + return self.cb + } + + self.cb = self.state.GetAccount(self.coinbase) + return self.cb +} +func (self *StateTransition) Sender() *StateObject { + if self.sen != nil { + return self.sen + } + + self.sen = self.state.GetAccount(self.tx.Sender()) + return self.sen +} +func (self *StateTransition) Receiver() *StateObject { + if self.tx.CreatesContract() { + return nil + } + + if self.rec != nil { + return self.rec + } + + self.rec = self.state.GetAccount(self.tx.Recipient) + return self.rec +} + +func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject { + contract := MakeContract(tx, state) + if contract != nil { + state.states[string(tx.CreationAddress())] = contract.state + + return contract + } + + return nil +} + +func (self *StateTransition) UseGas(amount *big.Int) error { + if self.gas.Cmp(amount) < 0 { + return OutOfGasError() + } + self.gas.Sub(self.gas, amount) + + return nil +} + +func (self *StateTransition) AddGas(amount *big.Int) { + self.gas.Add(self.gas, amount) +} + +func (self *StateTransition) BuyGas() error { + var err error + + sender := self.Sender() + if sender.Amount.Cmp(self.tx.GasValue()) < 0 { + return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), self.tx.Value) + } + + coinbase := self.Coinbase() + err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice) + if err != nil { + return err + } + //self.state.UpdateStateObject(coinbase) + + self.AddGas(self.tx.Gas) + sender.SubAmount(self.tx.GasValue()) + + return nil +} + +func (self *StateTransition) RefundGas() { + coinbase, sender := self.Coinbase(), self.Sender() + coinbase.RefundGas(self.gas, self.tx.GasPrice) + + // Return remaining gas + remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice) + sender.AddAmount(remaining) +} + +func (self *StateTransition) TransitionState() (err error) { + //snapshot := st.state.Snapshot() + + /* + defer func() { + if r := recover(); r != nil { + ethutil.Config.Log.Infoln(r) + err = fmt.Errorf("state transition err %v", r) + } + }() + */ + + var ( + tx = self.tx + sender = self.Sender() + receiver *StateObject + ) + + // Make sure this transaction's nonce is correct + if sender.Nonce != tx.Nonce { + return NonceError(tx.Nonce, sender.Nonce) + } + + // Pre-pay gas / Buy gas of the coinbase account + if err = self.BuyGas(); err != nil { + return err + } + + // XXX Transactions after this point are considered valid. + + defer func() { + self.RefundGas() + + if sender != nil { + self.state.UpdateStateObject(sender) + } + + if receiver != nil { + self.state.UpdateStateObject(receiver) + } + + self.state.UpdateStateObject(self.Coinbase()) + }() + + // Increment the nonce for the next transaction + sender.Nonce += 1 + + // Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve) + receiver = self.Receiver() + + // Transaction gas + if err = self.UseGas(GasTx); err != nil { + return err + } + + // Pay data gas + dataPrice := big.NewInt(int64(len(tx.Data))) + dataPrice.Mul(dataPrice, GasData) + if err = self.UseGas(dataPrice); err != nil { + return err + } + + // If the receiver is nil it's a contract (\0*32). + if receiver == nil { + // Create a new state object for the contract + receiver = self.MakeStateObject(self.state, tx) + if receiver == nil { + return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) + } + } + + // Transfer value from sender to receiver + if err = self.transferValue(sender, receiver); err != nil { + return err + } + + // Process the init code and create 'valid' contract + if tx.CreatesContract() { + // Evaluate the initialization script + // and use the return value as the + // script section for the state object. + //script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block) + code, err := self.Eval(receiver.Init(), receiver) + if err != nil { + return fmt.Errorf("Error during init script run %v", err) + } + + receiver.script = code + } else { + if len(receiver.Script()) > 0 { + _, err := self.Eval(receiver.Script(), receiver) + if err != nil { + return fmt.Errorf("Error during code execution %v", err) + } + } + } + + return nil +} + +func (self *StateTransition) transferValue(sender, receiver *StateObject) error { + if sender.Amount.Cmp(self.tx.Value) < 0 { + return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount) + } + + if self.tx.Value.Cmp(ethutil.Big0) > 0 { + // Subtract the amount from the senders account + sender.SubAmount(self.tx.Value) + // Add the amount to receivers account which should conclude this transaction + receiver.AddAmount(self.tx.Value) + + ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash()) + } + + return nil +} + +func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error) { + var ( + tx = self.tx + block = self.block + initiator = self.Sender() + state = self.state + ) + + closure := NewClosure(initiator, context, script, state, self.gas, tx.GasPrice) + vm := NewVm(state, nil, RuntimeVars{ + Origin: initiator.Address(), + BlockNumber: block.BlockInfo().Number, + PrevHash: block.PrevHash, + Coinbase: block.Coinbase, + Time: block.Time, + Diff: block.Difficulty, + Value: tx.Value, + }) + ret, _, err = closure.Call(vm, tx.Data, nil) + + return +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 2cb946b3b..3d52e4f73 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -1,6 +1,7 @@ package ethchain import ( + "bytes" "fmt" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" @@ -24,7 +25,7 @@ type Transaction struct { } func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { - return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} + return &Transaction{Recipient: ContractAddr, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} } func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { @@ -45,15 +46,18 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction { return tx } +func (self *Transaction) GasValue() *big.Int { + return new(big.Int).Mul(self.Gas, self.GasPrice) +} + +func (self *Transaction) TotalValue() *big.Int { + v := self.GasValue() + return v.Add(v, self.Value) +} + func (tx *Transaction) Hash() []byte { data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} - /* - if tx.contractCreation { - data = append(data, tx.Init) - } - */ - return ethutil.Sha3Bin(ethutil.NewValue(data).Encode()) } @@ -144,7 +148,8 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.v = byte(decoder.Get(6).Uint()) tx.r = decoder.Get(7).Bytes() tx.s = decoder.Get(8).Bytes() - if len(tx.Recipient) == 0 { + + if bytes.Compare(tx.Recipient, ContractAddr) == 0 { tx.contractCreation = true } } @@ -183,6 +188,7 @@ type Receipt struct { PostState []byte CumulativeGasUsed *big.Int } +type Receipts []*Receipt func NewRecieptFromValue(val *ethutil.Value) *Receipt { r := &Receipt{} diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index ba2ffcef5..24836222a 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -22,6 +22,7 @@ type TxMsgTy byte const ( TxPre = iota TxPost + minGasPrice = 1000000 ) type TxMsg struct { @@ -89,9 +90,11 @@ 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 { ethutil.Config.Log.Infoln(r) @@ -101,6 +104,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract gas = new(big.Int) addGas := func(g *big.Int) { gas.Add(gas, g) } + addGas(GasTx) // Get the sender sender := state.GetAccount(tx.Sender()) @@ -110,28 +114,37 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract 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, new(big.Int).Mul(TxFee, TxFeeRat)) - totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(tx.Gas, tx.GasPrice)) + 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) - sender.Nonce += 1 // Send Tx to self if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { - addGas(GasTx) // Subtract the fee - sender.SubAmount(new(big.Int).Mul(GasTx, tx.GasPrice)) + sender.SubAmount(rGas) } else { // Subtract the amount from the senders account sender.SubAmount(totAmount) @@ -140,17 +153,14 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract receiver.AddAmount(tx.Value) state.UpdateStateObject(receiver) + fmt.Printf("state root after receiver update %x\n", state.Root()) } - state.UpdateStateObject(sender) - ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) - // Notify all subscribers - pool.Ethereum.Reactor().Post("newTx:post", tx) - return } +*/ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { // Get the last block so we can retrieve the sender and receiver from @@ -161,17 +171,27 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { return errors.New("[TXPL] No last block on the block chain") } + if len(tx.Recipient) != 20 { + return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient)) + } + // Get the sender //sender := pool.Ethereum.StateManager().procState.GetAccount(tx.Sender()) sender := pool.Ethereum.StateManager().CurrentState().GetAccount(tx.Sender()) - totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) + 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 { return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } + if tx.IsContract() { + if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 { + return fmt.Errorf("[TXPL] Gasprice to low, %s given should be at least %d.", tx.GasPrice, minGasPrice) + } + } + // Increment the nonce making each tx valid only once to prevent replay // attacks @@ -200,6 +220,8 @@ out: // Call blocking version. pool.addTransaction(tx) + ethutil.Config.Log.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash()) + // Notify the subscribers pool.Ethereum.Reactor().Post("newTx:pre", tx) } diff --git a/ethchain/types.go b/ethchain/types.go index 293871143..d89fad147 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -1,5 +1,9 @@ package ethchain +import ( + "fmt" +) + type OpCode int // Op codes @@ -17,8 +21,10 @@ const ( NEG = 0x09 LT = 0x0a GT = 0x0b - EQ = 0x0c - NOT = 0x0d + SLT = 0x0c + SGT = 0x0d + EQ = 0x0e + NOT = 0x0f // 0x10 range - bit ops AND = 0x10 @@ -37,7 +43,10 @@ const ( CALLVALUE = 0x34 CALLDATALOAD = 0x35 CALLDATASIZE = 0x36 - GASPRICE = 0x37 + CALLDATACOPY = 0x37 + CODESIZE = 0x38 + CODECOPY = 0x39 + GASPRICE = 0x3a // 0x40 range - block operations PREVHASH = 0x40 @@ -48,18 +57,19 @@ const ( 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 + 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 @@ -120,6 +130,8 @@ var opCodeToString = map[OpCode]string{ NEG: "NEG", LT: "LT", GT: "GT", + SLT: "SLT", + SGT: "SGT", EQ: "EQ", NOT: "NOT", @@ -140,6 +152,9 @@ var opCodeToString = map[OpCode]string{ CALLVALUE: "CALLVALUE", CALLDATALOAD: "CALLDATALOAD", CALLDATASIZE: "CALLDATASIZE", + CALLDATACOPY: "CALLDATACOPY", + CODESIZE: "CODESIZE", + CODECOPY: "CODECOPY", GASPRICE: "TXGASPRICE", // 0x40 range - block operations @@ -162,6 +177,7 @@ var opCodeToString = map[OpCode]string{ JUMPI: "JUMPI", PC: "PC", MSIZE: "MSIZE", + GAS: "GAS", // 0x60 range - push PUSH1: "PUSH1", @@ -208,7 +224,12 @@ var opCodeToString = map[OpCode]string{ } func (o OpCode) String() string { - return opCodeToString[o] + str := opCodeToString[o] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode %#x", int(o)) + } + + return str } // Op codes for assembling diff --git a/ethchain/vm.go b/ethchain/vm.go index 955be847f..2ba0e2ef3 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" + "math" _ "math" "math/big" ) @@ -18,6 +19,7 @@ var ( GasCreate = big.NewInt(100) GasCall = big.NewInt(20) GasMemory = big.NewInt(1) + GasData = big.NewInt(5) GasTx = big.NewInt(500) ) @@ -112,15 +114,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } gas := new(big.Int) - setStepGasUsage := func(amount *big.Int) { + addStepGasUsage := func(amount *big.Int) { gas.Add(gas, amount) } + addStepGasUsage(GasStep) + + var newMemSize uint64 = 0 switch op { - case SHA3: - setStepGasUsage(GasSha) + case STOP: + case SUICIDE: case SLOAD: - setStepGasUsage(GasSLoad) + gas.Set(GasSLoad) case SSTORE: var mult *big.Int y, x := stack.Peekn() @@ -132,22 +137,55 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } else { mult = ethutil.Big1 } - setStepGasUsage(new(big.Int).Mul(mult, GasSStore)) + gas = new(big.Int).Mul(mult, GasSStore) case BALANCE: - setStepGasUsage(GasBalance) - case CREATE: + gas.Set(GasBalance) + case MSTORE: + require(2) + newMemSize = stack.Peek().Uint64() + 32 + case MLOAD: + + 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) - args := stack.Get(big.NewInt(3)) - initSize := new(big.Int).Add(args[1], args[0]) + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() + case CODECOPY: + require(3) - setStepGasUsage(CalculateTxGas(initSize)) + newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() case CALL: - setStepGasUsage(GasCall) - case MLOAD, MSIZE, MSTORE8, MSTORE: - setStepGasUsage(GasMemory) - default: - setStepGasUsage(GasStep) + require(7) + gas.Set(GasCall) + addStepGasUsage(stack.data[stack.Len()-2]) + + 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) { @@ -156,6 +194,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + mem.Resize(newMemSize) + switch op { case LOG: stack.Print() @@ -320,7 +360,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case ORIGIN: stack.Push(ethutil.BigD(vm.vars.Origin)) case CALLER: - stack.Push(ethutil.BigD(closure.Callee().Address())) + stack.Push(ethutil.BigD(closure.caller.Address())) case CALLVALUE: stack.Push(vm.vars.Value) case CALLDATALOAD: @@ -337,6 +377,26 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(ethutil.BigD(data)) case CALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) + case CALLDATACOPY: + case CODESIZE: + 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] + + mem.Set(mOff, l, code) case GASPRICE: stack.Push(closure.Price) @@ -423,6 +483,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(pc) case MSIZE: stack.Push(big.NewInt(int64(mem.Len()))) + case GAS: + stack.Push(closure.Gas) // 0x60 range case CREATE: require(3) @@ -435,7 +497,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro snapshot := vm.state.Snapshot() // Generate a new address - addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N()) + addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N()) // Create a new contract contract := NewContract(addr, value, []byte("")) // Set the init script @@ -443,10 +505,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Transfer all remaining gas to the new // contract so it may run the init script gas := new(big.Int).Set(closure.Gas) - closure.UseGas(gas) + //closure.UseGas(gas) // Create the closure - c := NewClosure(closure.callee, + c := NewClosure(closure.caller, closure.Object(), contract.initScript, vm.state, @@ -467,6 +529,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro vm.state.UpdateStateObject(contract) } case CALL: + // TODO RE-WRITE require(7) // Closure addr addr := stack.Pop() @@ -476,51 +539,44 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro inSize, inOffset := stack.Popn() // Pop return size and offset retSize, retOffset := stack.Popn() - // Make sure there's enough gas - if closure.Gas.Cmp(gas) < 0 { - stack.Push(ethutil.BigFalse) - - break - } // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) snapshot := vm.state.Snapshot() - // Fetch the contract which will serve as the closure body - contract := vm.state.GetStateObject(addr.Bytes()) + closure.object.Nonce += 1 + if closure.object.Amount.Cmp(value) < 0 { + ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount) - if contract != nil { - // Prepay for the gas - // If gas is set to 0 use all remaining gas for the next call - if gas.Cmp(big.NewInt(0)) == 0 { - // Copy - gas = new(big.Int).Set(closure.Gas) - } - closure.UseGas(gas) - - // Add the value to the state object - contract.AddAmount(value) - - // Create a new callable closure - closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price) - // Executer the closure and get the return value (if any) - ret, _, err := closure.Call(vm, args, hook) - if err != nil { - stack.Push(ethutil.BigFalse) - // Reset the changes applied this object - vm.state.Revert(snapshot) + stack.Push(ethutil.BigFalse) + } else { + // Fetch the contract which will serve as the closure body + contract := vm.state.GetStateObject(addr.Bytes()) + + if contract != nil { + // Add the value to the state object + contract.AddAmount(value) + + // Create a new callable closure + closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price) + // Executer the closure and get the return value (if any) + ret, _, err := closure.Call(vm, args, hook) + if err != nil { + stack.Push(ethutil.BigFalse) + // Reset the changes applied this object + vm.state.Revert(snapshot) + } else { + stack.Push(ethutil.BigTrue) + + vm.state.UpdateStateObject(contract) + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) + } } else { - stack.Push(ethutil.BigTrue) - - vm.state.UpdateStateObject(contract) - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) + ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) + stack.Push(ethutil.BigFalse) } - } else { - ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) - stack.Push(ethutil.BigFalse) } case RETURN: require(2) diff --git a/ethminer/miner.go b/ethminer/miner.go index 19ff5dd9e..1ef9ca229 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -136,11 +136,21 @@ func (self *Miner) mineNewBlock() { // Sort the transactions by nonce in case of odd network propagation sort.Sort(ethchain.TxByNonce{self.txs}) + // Accumulate all valid transaction and apply them to the new state - receipts, txs := stateManager.ApplyTransactions(self.block.State(), self.block, self.txs) - self.txs = txs + // Error may be ignored. It's not important during mining + parent := self.ethereum.BlockChain().GetBlock(self.block.PrevHash) + coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase) + coinbase.SetGasPool(self.block.CalcGasLimit(parent)) + receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs) + if err != nil { + ethutil.Config.Log.Debugln("[MINER]", err) + } + self.txs = append(txs, unhandledTxs...) + // Set the transactions to the block so the new SHA3 can be calculated self.block.SetReceipts(receipts, txs) + // Accumulate the rewards included for this block stateManager.AccumelateRewards(self.block.State(), self.block) @@ -155,6 +165,7 @@ func (self *Miner) mineNewBlock() { } else { self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val}) ethutil.Config.Log.Infof("[MINER] 🔨 Mined block %x\n", self.block.Hash()) + ethutil.Config.Log.Infoln(self.block) // Gather the new batch of transactions currently in the tx pool self.txs = self.ethereum.TxPool().CurrentTransactions() } diff --git a/ethpub/pub.go b/ethpub/pub.go index e00bd0dbe..20ba79d0b 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -115,9 +115,13 @@ var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010") func GetAddressFromNameReg(stateManager *ethchain.StateManager, name string) []byte { recp := new(big.Int).SetBytes([]byte(name)) object := stateManager.CurrentState().GetStateObject(namereg) - reg := object.GetStorage(recp) + if object != nil { + reg := object.GetStorage(recp) - return reg.Bytes() + return reg.Bytes() + } + + return nil } func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) { @@ -193,8 +197,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc if contractCreation { ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress()) - } else { - ethutil.Config.Log.Infof("Tx hash %x", tx.Hash()) } return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil diff --git a/ethpub/types.go b/ethpub/types.go index 6893c7e09..a76421007 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -46,6 +46,8 @@ type PBlock struct { Transactions string `json:"transactions"` Time int64 `json:"time"` Coinbase string `json:"coinbase"` + GasLimit string `json:"gasLimit"` + GasUsed string `json:"gasUsed"` } // Creates a new QML Block from a chain block @@ -64,7 +66,7 @@ func NewPBlock(block *ethchain.Block) *PBlock { return nil } - return &PBlock{ref: block, Number: int(block.Number.Uint64()), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)} + return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)} } func (self *PBlock) ToString() string { @@ -109,11 +111,12 @@ func NewPTx(tx *ethchain.Transaction) *PTx { sender := hex.EncodeToString(tx.Sender()) createsContract := tx.CreatesContract() - data := strings.Join(ethchain.Disassemble(tx.Data), "\n") - - isContract := len(tx.Data) > 0 + data := string(tx.Data) + if tx.CreatesContract() { + data = strings.Join(ethchain.Disassemble(tx.Data), "\n") + } - return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: isContract, Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)} + return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)} } func (self *PTx) ToString() string { diff --git a/ethutil/big.go b/ethutil/big.go index 1c25a4784..7af6f7414 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -68,8 +68,8 @@ func BigCopy(src *big.Int) *big.Int { // Returns the maximum size big integer func BigMax(x, y *big.Int) *big.Int { if x.Cmp(y) <= 0 { - return x + return y } - return y + return x } diff --git a/ethutil/common.go b/ethutil/common.go index c7973eb92..f63ba5d83 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -18,8 +18,8 @@ var ( Wei = big.NewInt(1) ) -// Currency to string // +// Currency to string // Returns a string representing a human readable format func CurrencyToString(num *big.Int) string { switch { @@ -47,7 +47,7 @@ func CurrencyToString(num *big.Int) string { // Common big integers often used var ( Big1 = big.NewInt(1) - Big2 = big.NewInt(1) + Big2 = big.NewInt(2) Big0 = big.NewInt(0) Big32 = big.NewInt(32) Big256 = big.NewInt(0xff) diff --git a/ethutil/config.go b/ethutil/config.go index e992bda12..a24c39bfe 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -75,11 +75,11 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s if Config == nil { path := ApplicationFolder(base) - Config = &config{ExecPath: path, Debug: true, Ver: "0.5.0 RC12"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5.13"} Config.conf = g Config.Identifier = id Config.Log = NewLogger(logTypes, LogLevelDebug) - Config.SetClientString("/Ethereum(G)") + Config.SetClientString("Ethereum(G)") } return Config @@ -88,11 +88,9 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s // Set client string // func (c *config) SetClientString(str string) { - id := runtime.GOOS - if len(c.Identifier) > 0 { - id = c.Identifier - } - Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, id) + os := runtime.GOOS + cust := c.Identifier + Config.ClientString = fmt.Sprintf("%s/v%s/%s/%s/Go", str, c.Ver, cust, os) } func (c *config) SetIdentifier(id string) { @@ -145,12 +143,17 @@ func NewLogger(flag LoggerType, level int) *Logger { return &Logger{logSys: loggers, logLevel: level} } +func (self *Logger) SetLevel(level int) { + self.logLevel = level +} + func (log *Logger) AddLogSystem(logger LogSystem) { log.logSys = append(log.logSys, logger) } const ( - LogLevelDebug = iota + LogLevelSystem = iota + LogLevelDebug LogLevelInfo ) @@ -206,6 +209,26 @@ func (log *Logger) Fatal(v ...interface{}) { os.Exit(1) } +func (log *Logger) Println(level int, v ...interface{}) { + if log.logLevel > level { + return + } + + for _, logger := range log.logSys { + logger.Println(v...) + } +} + +func (log *Logger) Printf(level int, format string, v ...interface{}) { + if log.logLevel > level { + return + } + + for _, logger := range log.logSys { + logger.Printf(format, v...) + } +} + type confValue struct { value string } diff --git a/ethutil/keypair.go b/ethutil/keypair.go index cf5882e2c..29fb1bac5 100644 --- a/ethutil/keypair.go +++ b/ethutil/keypair.go @@ -12,6 +12,12 @@ type KeyPair struct { account *StateObject } +func GenerateNewKeyPair() (*KeyPair, error) { + _, prv := secp256k1.GenerateKeyPair() + + return NewKeyPairFromSec(prv) +} + func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) { pubkey, err := secp256k1.GeneratePubKey(seckey) if err != nil { diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 69f80a0a6..195ef0efb 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -11,6 +11,7 @@ import ( type RlpEncodable interface { RlpEncode() []byte + RlpValue() []interface{} } type RlpEncoder struct { diff --git a/ethwire/messaging.go b/ethwire/messaging.go index cbcbbb8b7..f13b72353 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -1,3 +1,5 @@ +// Package ethwire provides low level access to the Ethereum network and allows +// you to broadcast data over the network. package ethwire import ( @@ -9,11 +11,13 @@ import ( "time" ) -// Message: -// [4 bytes token] RLP([TYPE, DATA]) -// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol +// Connection interface describing the methods required to implement the wire protocol. +type Conn interface { + Write(typ MsgType, v ...interface{}) error + Read() *Msg +} -// The magic token which should be the first 4 bytes of every message. +// The magic token which should be the first 4 bytes of every message and can be used as separator between messages. var MagicToken = []byte{34, 64, 8, 145} type MsgType byte @@ -68,6 +72,157 @@ func NewMessage(msgType MsgType, data interface{}) *Msg { } } +type Messages []*Msg + +// The connection object allows you to set up a connection to the Ethereum network. +// The Connection object takes care of all encoding and sending objects properly over +// the network. +type Connection struct { + conn net.Conn + nTimeout time.Duration + pendingMessages Messages +} + +// Create a new connection to the Ethereum network +func New(conn net.Conn) *Connection { + return &Connection{conn: conn, nTimeout: 500} +} + +// Read, reads from the network. It will block until the next message is received. +func (self *Connection) Read() *Msg { + if len(self.pendingMessages) == 0 { + self.readMessages() + } + + ret := self.pendingMessages[0] + self.pendingMessages = self.pendingMessages[1:] + + return ret + +} + +// Write to the Ethereum network specifying the type of the message and +// the data. Data can be of type RlpEncodable or []interface{}. Returns +// nil or if something went wrong an error. +func (self *Connection) Write(typ MsgType, v ...interface{}) error { + var pack []byte + + slice := [][]interface{}{[]interface{}{byte(typ)}} + for _, value := range v { + if encodable, ok := value.(ethutil.RlpEncodable); ok { + slice = append(slice, encodable.RlpValue()) + } else if raw, ok := value.([]interface{}); ok { + slice = append(slice, raw) + } else { + panic(fmt.Sprintf("Unable to 'write' object of type %T", value)) + } + } + + // Encode the type and the (RLP encoded) data for sending over the wire + encoded := ethutil.NewValue(slice).Encode() + payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32) + + // Write magic token and payload length (first 8 bytes) + pack = append(MagicToken, payloadLength...) + pack = append(pack, encoded...) + + // Write to the connection + _, err := self.conn.Write(pack) + if err != nil { + return err + } + + return nil +} + +func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) { + if len(data) == 0 { + return nil, nil, true, nil + } + + if len(data) <= 8 { + return nil, remaining, false, errors.New("Invalid message") + } + + // Check if the received 4 first bytes are the magic token + if bytes.Compare(MagicToken, data[:4]) != 0 { + return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4]) + } + + messageLength := ethutil.BytesToNumber(data[4:8]) + remaining = data[8+messageLength:] + if int(messageLength) > len(data[8:]) { + return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength) + } + + message := data[8 : 8+messageLength] + decoder := ethutil.NewValueFromBytes(message) + // Type of message + t := decoder.Get(0).Uint() + // Actual data + d := decoder.SliceFrom(1) + + msg = &Msg{ + Type: MsgType(t), + Data: d, + } + + return +} + +// The basic message reader waits for data on the given connection, decoding +// and doing a few sanity checks such as if there's a data type and +// unmarhals the given data +func (self *Connection) readMessages() (err error) { + // The recovering function in case anything goes horribly wrong + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("ethwire.ReadMessage error: %v", r) + } + }() + + // Buff for writing network message to + //buff := make([]byte, 1440) + var buff []byte + var totalBytes int + for { + // Give buffering some time + self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond)) + // Create a new temporarily buffer + b := make([]byte, 1440) + // Wait for a message from this peer + n, _ := self.conn.Read(b) + if err != nil && n == 0 { + if err.Error() != "EOF" { + fmt.Println("err now", err) + return err + } else { + break + } + + // Messages can't be empty + } else if n == 0 { + break + } + + buff = append(buff, b[:n]...) + totalBytes += n + } + + // Reslice buffer + buff = buff[:totalBytes] + msg, remaining, done, err := self.readMessage(buff) + for ; done != true; msg, remaining, done, err = self.readMessage(remaining) { + //log.Println("rx", msg) + + if msg != nil { + self.pendingMessages = append(self.pendingMessages, msg) + } + } + + return +} + func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) { if len(data) == 0 { return nil, nil, true, nil @@ -17,7 +17,9 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 17 + ProtocolVersion = 20 + // Interval for ping/pong message + pingPongTimer = 2 * time.Second ) type DiscReason byte @@ -151,11 +153,11 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { pubkey: pubkey, blocksRequested: 10, caps: ethereum.ServerCaps(), + version: ethutil.Config.ClientString, } } func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { - p := &Peer{ outputQueue: make(chan *ethwire.Msg, outputBufferSize), quit: make(chan bool), @@ -241,9 +243,11 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { } } + ethutil.Config.Log.Println(ethutil.LogLevelSystem, "<=", msg.Type, msg.Data) + err := ethwire.WriteMessage(p.conn, msg) if err != nil { - ethutil.Config.Log.Debugln("Can't send message:", err) + ethutil.Config.Log.Debugln("[PEER] Can't send message:", err) // Stop the client if there was an error writing to it p.Stop() return @@ -253,7 +257,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { // Outbound message handler. Outbound messages are handled here func (p *Peer) HandleOutbound() { // The ping timer. Makes sure that every 2 minutes a ping is send to the peer - pingTimer := time.NewTicker(30 * time.Second) + pingTimer := time.NewTicker(pingPongTimer) serviceTimer := time.NewTicker(5 * time.Minute) out: @@ -264,8 +268,14 @@ out: p.writeMessage(msg) p.lastSend = time.Now() - // Ping timer sends a ping to the peer each 2 minutes + // 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) { + ethutil.Config.Log.Infof("[PEER] 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() @@ -307,6 +317,8 @@ func (p *Peer) HandleInbound() { ethutil.Config.Log.Debugln(err) } for _, msg := range msgs { + ethutil.Config.Log.Println(ethutil.LogLevelSystem, "=>", msg.Type, msg.Data) + switch msg.Type { case ethwire.MsgHandshakeTy: // Version message @@ -393,7 +405,7 @@ func (p *Peer) HandleInbound() { if err != nil { // If the parent is unknown try to catch up with this peer if ethchain.IsParentErr(err) { - ethutil.Config.Log.Infoln("Attempting to catch up since we don't know the parent") + ethutil.Config.Log.Infoln("Attempting to catch. Parent known") p.catchingUp = false p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) } else if ethchain.IsValidationErr(err) { @@ -405,7 +417,7 @@ func (p *Peer) HandleInbound() { if p.catchingUp && msg.Data.Len() > 1 { if lastBlock != nil { blockInfo := lastBlock.BlockInfo() - ethutil.Config.Log.Debugf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) + ethutil.Config.Log.Debugf("Synced chain to #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) } p.catchingUp = false @@ -571,7 +583,7 @@ func (p *Peer) pushHandshake() error { pubkey := keyRing.PublicKey msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ - uint32(ProtocolVersion), uint32(0), p.version, byte(p.caps), p.port, pubkey[1:], + uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:], }) p.QueueMessage(msg) @@ -603,7 +615,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data if c.Get(0).Uint() != ProtocolVersion { - ethutil.Config.Log.Debugln("Invalid peer version. Require protocol:", ProtocolVersion) + ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint()) p.Stop() return } |