From 771f64397fd8638e9a40a4b9ecc64a9b70d6f2e4 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 13:51:34 +0200 Subject: [PATCH 01/42] Stop peers when they don't respond to ping/pong. Might fix ethereum/go-ethereum#78 --- peer.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/peer.go b/peer.go index eed5bec30..9cc892d8b 100644 --- a/peer.go +++ b/peer.go @@ -18,6 +18,8 @@ const ( outputBufferSize = 50 // Current protocol version ProtocolVersion = 17 + // Interval for ping/pong message + pingPongTimer = 30 * time.Second ) type DiscReason byte @@ -243,7 +245,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { 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 +255,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 +266,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() == false && timeSince > (pingPongTimer+10*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() @@ -563,6 +571,7 @@ func (p *Peer) Stop() { // Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here p.ethereum.RemovePeer(p) + ethutil.Config.Log.Debugln("[PEER] Stopped peer:", p.conn.RemoteAddr()) } func (p *Peer) pushHandshake() error { From 1b40f69ce5166fbe8a13709caf31f50107fa3bdf Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 14:59:38 +0200 Subject: [PATCH 02/42] Prevent peer stop crash by removing logging --- peer.go | 1 - 1 file changed, 1 deletion(-) diff --git a/peer.go b/peer.go index 9cc892d8b..80975ff81 100644 --- a/peer.go +++ b/peer.go @@ -571,7 +571,6 @@ func (p *Peer) Stop() { // Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here p.ethereum.RemovePeer(p) - ethutil.Config.Log.Debugln("[PEER] Stopped peer:", p.conn.RemoteAddr()) } func (p *Peer) pushHandshake() error { From 2995d6c281b83f5bb055a22093b2b94e64c477d3 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 15:02:41 +0200 Subject: [PATCH 03/42] Validate minimum gasPrice and reject if not met --- ethchain/transaction_pool.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index ba2ffcef5..d4175d973 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 { @@ -172,6 +173,12 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { 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 From 2e6cf42011a4176a01f3e3f777cc1ddc4125511f Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 17:15:18 +0200 Subject: [PATCH 04/42] Fix BigMax to return the biggest number, not the smallest --- ethutil/big.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 } From 753f749423df7d5fba55a4080383d215db8e0fc7 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 17:22:06 +0200 Subject: [PATCH 05/42] Implement CalcGasPrice for ethereum/go-ethereum#77 --- ethchain/block.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ethchain/block.go b/ethchain/block.go index 73e29f878..780c60869 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -154,6 +154,35 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { return true } +func (block *Block) CalcGasLimit(parent *Block) *big.Int { + if block.Number == big.NewInt(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")...)) From 69044fe5774840a49de3f881a490db52e907affb Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 17:22:43 +0200 Subject: [PATCH 06/42] Refactor to use new method --- ethchain/block_chain.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index b45d254b5..5e6ce46e1 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -72,19 +72,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 From bdc206885a1d9c730464f60ec65557403720be1e Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 10 Jun 2014 17:23:32 +0200 Subject: [PATCH 07/42] Don't mine transactions if they would go over the GasLimit implements ethereum/go-ethereum#77 further. --- ethchain/error.go | 18 ++++++++++++++++++ ethchain/state_manager.go | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/ethchain/error.go b/ethchain/error.go index 8d37b0208..29896bc59 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 diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index f1c09b819..aea5433ff 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -114,6 +114,8 @@ func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Tra // 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(state, block, tx) @@ -121,6 +123,12 @@ func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Tra 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) } @@ -151,6 +159,7 @@ func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transac script []byte ) totalGasUsed = big.NewInt(0) + snapshot := state.Snapshot() // Apply the transaction to the current state gas, err = sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) @@ -190,6 +199,14 @@ func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transac } } + 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 } From 97cc76214350b3ef9b0c15f53d06c684e01ede37 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 11 Jun 2014 10:28:18 +0200 Subject: [PATCH 08/42] Expose GasLimit to ethPub --- ethpub/types.go | 3 ++- ethutil/common.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ethpub/types.go b/ethpub/types.go index 6893c7e09..40ac32a27 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -46,6 +46,7 @@ type PBlock struct { Transactions string `json:"transactions"` Time int64 `json:"time"` Coinbase string `json:"coinbase"` + GasLimit string `json:"gasLimit"` } // Creates a new QML Block from a chain block @@ -64,7 +65,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()), 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 { diff --git a/ethutil/common.go b/ethutil/common.go index c7973eb92..ddaf78f88 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 { From e090d131c38bef3c5f2d0f5e9fa28f5d0ed06659 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 11 Jun 2014 11:40:40 +0200 Subject: [PATCH 09/42] Implemented counting of usedGas --- ethchain/state_manager.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index aea5433ff..6fb664e3d 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -140,6 +140,9 @@ func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Tra validTxs = append(validTxs, tx) } + // Update the total gas used for the block (to be mined) + block.GasUsed = totalUsedGas + return receipts, validTxs } From 71ab5d52b692a42cc3af034e5ff1aab5b4b6477d Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 11 Jun 2014 11:40:50 +0200 Subject: [PATCH 10/42] Exposed usedGas through ethPub --- ethpub/types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethpub/types.go b/ethpub/types.go index 40ac32a27..868fd2713 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -47,6 +47,7 @@ type PBlock struct { 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 @@ -65,7 +66,7 @@ func NewPBlock(block *ethchain.Block) *PBlock { return nil } - return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasLimit: block.GasLimit.String(), 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 { From 1938bfcddfd2722880a692c59cad344b611711c8 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 11 Jun 2014 16:16:57 +0200 Subject: [PATCH 11/42] Fix compare --- ethchain/block.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethchain/block.go b/ethchain/block.go index 780c60869..fee4a2d59 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -155,9 +155,10 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { } func (block *Block) CalcGasLimit(parent *Block) *big.Int { - if block.Number == big.NewInt(0) { + 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()) From 9ff97a98a7449f10db71be88146c5a03d932c373 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Jun 2014 21:51:21 +0200 Subject: [PATCH 12/42] Namereg lookup fix --- ethpub/pub.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ethpub/pub.go b/ethpub/pub.go index e00bd0dbe..51426adc5 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) { From 4d3209ad1d6ea6f7e4fc311c387392f23ebc7dde Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Jun 2014 21:55:23 +0200 Subject: [PATCH 13/42] Moved process transaction to state manager * Buy gas of the coinbase address --- ethchain/state_manager.go | 93 +++++++++++++++++++++++++++++++----- ethchain/transaction_pool.go | 29 +++++++---- 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index f1c09b819..e783ffdd8 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -108,15 +108,90 @@ func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObj return nil } +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))) + 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 { + 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) + + // 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) + + fmt.Printf("state root after sender update %x\n", state.Root()) + + // 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()) + } + + state.UpdateStateObject(sender) + + ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) + + return +} + // 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) { +func (sm *StateManager) ApplyTransactions(coinbase []byte, 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) + usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx) if err != nil { if IsNonceErr(err) { continue @@ -135,7 +210,7 @@ func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Tra return receipts, validTxs } -func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { +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. @@ -152,8 +227,9 @@ func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transac ) totalGasUsed = big.NewInt(0) + ca := state.GetAccount(coinbase) // Apply the transaction to the current state - gas, err = sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) + gas, err = sm.ProcessTransaction(tx, ca, state, false) addTotalGas(gas) if tx.CreatesContract() { @@ -229,7 +305,7 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea } // Process the transactions on to current block - sm.ApplyTransactions(state, parent, block.Transactions()) + sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) // Block validation if err := sm.ValidateBlock(block); err != nil { @@ -337,13 +413,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) } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index ba2ffcef5..bc7bde797 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -89,9 +89,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 +103,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 +113,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 +152,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 From 1bf6f8b4a6936d8ad14b345b2cfa4dc9d1f8a360 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Jun 2014 21:55:34 +0200 Subject: [PATCH 14/42] Added a buy gas method --- ethchain/state_object.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 3e9c6df40..a1dd531de 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -108,10 +108,14 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { func (c *StateObject) AddAmount(amount *big.Int) { c.SetAmount(new(big.Int).Add(c.Amount, amount)) + + ethutil.Config.Log.Debugf("%x: #%d %v (+ %v)", 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.Debugf("%x: #%d %v (- %v)", c.Address(), c.Nonce, c.Amount, amount) } func (c *StateObject) SetAmount(amount *big.Int) { @@ -129,6 +133,17 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error { return nil } +func (self *StateObject) BuyGas(gas, price *big.Int) error { + rGas := new(big.Int).Set(gas) + rGas.Mul(gas, price) + + self.AddAmount(rGas) + + // TODO Do sub from TotalGasPool + // and check if enough left + return nil +} + // Returns the address of the contract/account func (c *StateObject) Address() []byte { return c.address @@ -153,14 +168,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() From 9ee6295c752a518603de01e4feaec787c61a5dcf Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 11 Jun 2014 21:55:45 +0200 Subject: [PATCH 15/42] Minor changes --- ethchain/block_chain.go | 3 +-- ethminer/miner.go | 3 ++- peer.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index b45d254b5..68ef9d47e 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -271,7 +271,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 +285,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/ethminer/miner.go b/ethminer/miner.go index 19ff5dd9e..d05405391 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -137,7 +137,7 @@ 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) + receipts, txs := stateManager.ApplyTransactions(self.block.Coinbase, self.block.State(), self.block, self.txs) self.txs = txs // Set the transactions to the block so the new SHA3 can be calculated self.block.SetReceipts(receipts, txs) @@ -155,6 +155,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/peer.go b/peer.go index eed5bec30..acebd2756 100644 --- a/peer.go +++ b/peer.go @@ -17,7 +17,7 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 17 + ProtocolVersion = 20 ) type DiscReason byte @@ -603,7 +603,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 } From 3a9d7d318abb3cd01ecd012ae85da5e586436d65 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 12 Jun 2014 10:07:27 +0200 Subject: [PATCH 16/42] log changes --- ethchain/transaction_pool.go | 6 ++++++ ethpub/pub.go | 2 -- ethutil/config.go | 12 +++++------- peer.go | 3 ++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index d4175d973..103c305fe 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -162,6 +162,10 @@ 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()) @@ -207,6 +211,8 @@ out: // Call blocking version. pool.addTransaction(tx) + ethutil.Config.Log.Debugf("%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/ethpub/pub.go b/ethpub/pub.go index e00bd0dbe..5c636f7a2 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -193,8 +193,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/ethutil/config.go b/ethutil/config.go index e992bda12..f935e8f75 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.12"} 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) { diff --git a/peer.go b/peer.go index 80975ff81..c7591ac31 100644 --- a/peer.go +++ b/peer.go @@ -153,6 +153,7 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { pubkey: pubkey, blocksRequested: 10, caps: ethereum.ServerCaps(), + version: ethutil.Config.ClientString, } } @@ -579,7 +580,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) From b855e5f7df194c84651d7cc7ee32d307a2fa0a2e Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 12 Jun 2014 11:19:32 +0200 Subject: [PATCH 17/42] Changed opcode numbers and added missing opcodes --- ethchain/state_manager.go | 8 +++---- ethchain/transaction.go | 4 +++- ethchain/types.go | 45 +++++++++++++++++++++++++++------------ ethchain/vm.go | 5 +++++ 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 9631b55fe..7b44ba3b8 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -166,13 +166,9 @@ func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObj // Subtract the amount from the senders account sender.SubAmount(totAmount) - fmt.Printf("state root after sender update %x\n", state.Root()) - // 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()) } state.UpdateStateObject(sender) @@ -215,6 +211,8 @@ func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block * validTxs = append(validTxs, tx) } + fmt.Println("################# MADE\n", receipts, "\n############################") + // Update the total gas used for the block (to be mined) block.GasUsed = totalUsedGas @@ -250,6 +248,7 @@ func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *B // 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. @@ -323,6 +322,7 @@ 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()) // Process the transactions on to current block sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 2cb946b3b..32dbd8388 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" @@ -144,7 +145,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 } } diff --git a/ethchain/types.go b/ethchain/types.go index 293871143..fdfd5792b 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -1,5 +1,9 @@ package ethchain +import ( + "fmt" +) + type OpCode int // Op codes @@ -37,7 +41,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 +55,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 @@ -140,6 +148,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 +173,7 @@ var opCodeToString = map[OpCode]string{ JUMPI: "JUMPI", PC: "PC", MSIZE: "MSIZE", + GAS: "GAS", // 0x60 range - push PUSH1: "PUSH1", @@ -208,7 +220,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 0x%x", int(o)) + } + + return str } // Op codes for assembling diff --git a/ethchain/vm.go b/ethchain/vm.go index 955be847f..ebdc58659 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -337,6 +337,9 @@ 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: case GASPRICE: stack.Push(closure.Price) @@ -423,6 +426,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) From d078e9b8c92fb3bd5789a8e39c169b19864e0a04 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 12:45:11 +0200 Subject: [PATCH 18/42] Refactoring state transitioning --- ethchain/asm.go | 12 +- ethchain/deprecated.go | 225 ++++++++++++++++++++++ ethchain/error.go | 17 ++ ethchain/stack.go | 6 + ethchain/state_manager.go | 351 +++++++++++++++++++---------------- ethchain/state_object.go | 2 +- ethchain/transaction.go | 16 +- ethchain/transaction_pool.go | 2 +- ethchain/types.go | 8 +- ethchain/vm.go | 77 ++++++-- 10 files changed, 517 insertions(+), 199 deletions(-) create mode 100644 ethchain/deprecated.go diff --git a/ethchain/asm.go b/ethchain/asm.go index 430a89450..277326ff9 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("0x%x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethchain/deprecated.go b/ethchain/deprecated.go new file mode 100644 index 000000000..ed2697e2a --- /dev/null +++ b/ethchain/deprecated.go @@ -0,0 +1,225 @@ +package ethchain + +import ( + "bytes" + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +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 29896bc59..2cf09a1ec 100644 --- a/ethchain/error.go +++ b/ethchain/error.go @@ -79,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/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_manager.go b/ethchain/state_manager.go index 7b44ba3b8..f44afecac 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -108,8 +108,86 @@ func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObj return nil } -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()) +type StateTransition struct { + coinbase []byte + tx *Transaction + gas *big.Int + state *State + block *Block + + cb, rec, sen *StateObject +} + +func NewStateTransition(coinbase []byte, gas *big.Int, tx *Transaction, state *State, block *Block) *StateTransition { + return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, 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) 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 *StateManager) TransitionState(st *StateTransition) (err error) { + //snapshot := st.state.Snapshot() + defer func() { if r := recover(); r != nil { ethutil.Config.Log.Infoln(r) @@ -117,173 +195,144 @@ func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObj } }() - gas = new(big.Int) - addGas := func(g *big.Int) { gas.Add(gas, g) } - addGas(GasTx) - - // Get the sender - sender := state.GetAccount(tx.Sender()) + var ( + tx = st.tx + sender = st.Sender() + receiver *StateObject + ) if sender.Nonce != tx.Nonce { - err = NonceError(tx.Nonce, sender.Nonce) - return + return NonceError(tx.Nonce, sender.Nonce) } 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))) - 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 { - state.UpdateStateObject(sender) - err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) - return + if err = st.BuyGas(); err != nil { + return err } - coinbase.BuyGas(gas, tx.GasPrice) - state.UpdateStateObject(coinbase) + receiver = st.Receiver() - // 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) + if err = st.UseGas(GasTx); err != nil { + return err } - state.UpdateStateObject(sender) + dataPrice := big.NewInt(int64(len(tx.Data))) + dataPrice.Mul(dataPrice, GasData) + if err = st.UseGas(dataPrice); err != nil { + return err + } - ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) + if receiver == nil { // Contract + receiver = self.MakeStateObject(st.state, tx) + if receiver == nil { + return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) + } + } - return -} + if err = self.transferValue(st, sender, receiver); err != nil { + return err + } -// 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 tx.CreatesContract() { + fmt.Println(Disassemble(receiver.Init())) + // 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(st, receiver.Init(), receiver) 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) + return fmt.Errorf("Error during init script run %v", err) } - accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) + receiver.script = code + } + + st.state.UpdateStateObject(sender) + st.state.UpdateStateObject(receiver) + + return nil +} + +func (self *StateManager) transferValue(st *StateTransition, sender, receiver *StateObject) error { + if sender.Amount.Cmp(st.tx.Value) < 0 { + return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", st.tx.Value, sender.Amount) + } + + // Subtract the amount from the senders account + sender.SubAmount(st.tx.Value) + // Add the amount to receivers account which should conclude this transaction + receiver.AddAmount(st.tx.Value) + + ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], st.tx.Value, st.tx.Hash()) + + return nil +} + +func (self *StateManager) ProcessTransactions(coinbase []byte, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { + var ( + receipts Receipts + handled, unhandled Transactions + totalUsedGas = big.NewInt(0) + err error + ) + +done: + for i, tx := range txs { + txGas := new(big.Int).Set(tx.Gas) + st := NewStateTransition(coinbase, tx.Gas, tx, state, block) + err = self.TransitionState(st) + if err != nil { + switch { + case IsNonceErr(err): + err = nil // ignore error + continue + case IsGasLimitErr(err): + unhandled = txs[i:] + + break done + default: + ethutil.Config.Log.Infoln(err) + } + } + + 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) } fmt.Println("################# MADE\n", receipts, "\n############################") - // Update the total gas used for the block (to be mined) - block.GasUsed = totalUsedGas + parent.GasUsed = totalUsedGas - return receipts, validTxs + return receipts, handled, unhandled, err } -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. - */ +func (self *StateManager) Eval(st *StateTransition, script []byte, context *StateObject) (ret []byte, err error) { var ( - addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } - gas = new(big.Int) - script []byte + tx = st.tx + block = st.block + initiator = st.Sender() ) - 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) - - 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) - 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) - } - } - - 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) - } + closure := NewClosure(initiator, context, script, st.state, st.gas, tx.GasPrice) + vm := NewVm(st.state, self, 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 } @@ -325,7 +374,8 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea fmt.Println(block.Receipts()) // Process the transactions on to current block - sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) + //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) + sm.ProcessTransactions(block.Coinbase, state, block, parent, block.Transactions()) // Block validation if err := sm.ValidateBlock(block); err != nil { @@ -464,35 +514,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 a1dd531de..b92374882 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -135,7 +135,7 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error { func (self *StateObject) BuyGas(gas, price *big.Int) error { rGas := new(big.Int).Set(gas) - rGas.Mul(gas, price) + rGas.Mul(rGas, price) self.AddAmount(rGas) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 32dbd8388..7aaab2fd1 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -46,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()) } @@ -185,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 52c850ba3..6538b0029 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -220,7 +220,7 @@ out: // Call blocking version. pool.addTransaction(tx) - ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash()) + 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 fdfd5792b..ee70a8d28 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -21,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 @@ -128,6 +130,8 @@ var opCodeToString = map[OpCode]string{ NEG: "NEG", LT: "LT", GT: "GT", + SLT: "SLT", + SGT: "SGT", EQ: "EQ", NOT: "NOT", diff --git a/ethchain/vm.go b/ethchain/vm.go index ebdc58659..e17264697 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) ) @@ -116,9 +118,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro gas.Add(gas, amount) } + var newMemSize uint64 = 0 switch op { - case SHA3: - setStepGasUsage(GasSha) case SLOAD: setStepGasUsage(GasSLoad) case SSTORE: @@ -135,27 +136,61 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro setStepGasUsage(new(big.Int).Mul(mult, GasSStore)) case BALANCE: setStepGasUsage(GasBalance) - case CREATE: + case MSTORE: + require(2) + 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) + + setStepGasUsage(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: + require(7) setStepGasUsage(GasCall) - case MLOAD, MSIZE, MSTORE8, MSTORE: - setStepGasUsage(GasMemory) + + 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) + setStepGasUsage(GasCreate) + + newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() default: setStepGasUsage(GasStep) } + newMemSize = (newMemSize + 31) / 32 * 32 + if newMemSize > uint64(mem.Len()) { + m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 + setStepGasUsage(big.NewInt(int64(m))) + } + if !closure.UseGas(gas) { ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + mem.Resize(newMemSize) + switch op { case LOG: stack.Print() @@ -340,6 +375,23 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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) @@ -448,7 +500,7 @@ 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, @@ -498,12 +550,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro 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) + //closure.UseGas(gas) // Add the value to the state object contract.AddAmount(value) From 5e2bf12a31e3e3a026d0f8d28c2d5bba919fc224 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 12:57:52 +0200 Subject: [PATCH 19/42] Refactored state transitioning to its own model --- ethchain/deprecated.go | 11 +++ ethchain/state_manager.go | 202 +------------------------------------- 2 files changed, 13 insertions(+), 200 deletions(-) diff --git a/ethchain/deprecated.go b/ethchain/deprecated.go index ed2697e2a..0985fa25d 100644 --- a/ethchain/deprecated.go +++ b/ethchain/deprecated.go @@ -7,6 +7,17 @@ import ( "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()) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index f44afecac..576afa8d3 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -97,182 +97,6 @@ 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 -} - -type StateTransition struct { - coinbase []byte - tx *Transaction - gas *big.Int - state *State - block *Block - - cb, rec, sen *StateObject -} - -func NewStateTransition(coinbase []byte, gas *big.Int, tx *Transaction, state *State, block *Block) *StateTransition { - return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, 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) 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 *StateManager) TransitionState(st *StateTransition) (err error) { - //snapshot := st.state.Snapshot() - - defer func() { - if r := recover(); r != nil { - ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("%v", r) - } - }() - - var ( - tx = st.tx - sender = st.Sender() - receiver *StateObject - ) - - if sender.Nonce != tx.Nonce { - return NonceError(tx.Nonce, sender.Nonce) - } - - sender.Nonce += 1 - defer func() { - // Notify all subscribers - self.Ethereum.Reactor().Post("newTx:post", tx) - }() - - if err = st.BuyGas(); err != nil { - return err - } - - receiver = st.Receiver() - - if err = st.UseGas(GasTx); err != nil { - return err - } - - dataPrice := big.NewInt(int64(len(tx.Data))) - dataPrice.Mul(dataPrice, GasData) - if err = st.UseGas(dataPrice); err != nil { - return err - } - - if receiver == nil { // Contract - receiver = self.MakeStateObject(st.state, tx) - if receiver == nil { - return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) - } - } - - if err = self.transferValue(st, sender, receiver); err != nil { - return err - } - - if tx.CreatesContract() { - fmt.Println(Disassemble(receiver.Init())) - // 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(st, receiver.Init(), receiver) - if err != nil { - return fmt.Errorf("Error during init script run %v", err) - } - - receiver.script = code - } - - st.state.UpdateStateObject(sender) - st.state.UpdateStateObject(receiver) - - return nil -} - -func (self *StateManager) transferValue(st *StateTransition, sender, receiver *StateObject) error { - if sender.Amount.Cmp(st.tx.Value) < 0 { - return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", st.tx.Value, sender.Amount) - } - - // Subtract the amount from the senders account - sender.SubAmount(st.tx.Value) - // Add the amount to receivers account which should conclude this transaction - receiver.AddAmount(st.tx.Value) - - ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], st.tx.Value, st.tx.Hash()) - - return nil -} - func (self *StateManager) ProcessTransactions(coinbase []byte, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { var ( receipts Receipts @@ -284,8 +108,8 @@ func (self *StateManager) ProcessTransactions(coinbase []byte, state *State, blo done: for i, tx := range txs { txGas := new(big.Int).Set(tx.Gas) - st := NewStateTransition(coinbase, tx.Gas, tx, state, block) - err = self.TransitionState(st) + st := NewStateTransition(coinbase, tx, state, block) + err = st.TransitionState() if err != nil { switch { case IsNonceErr(err): @@ -315,28 +139,6 @@ done: return receipts, handled, unhandled, err } -func (self *StateManager) Eval(st *StateTransition, script []byte, context *StateObject) (ret []byte, err error) { - var ( - tx = st.tx - block = st.block - initiator = st.Sender() - ) - - closure := NewClosure(initiator, context, script, st.state, st.gas, tx.GasPrice) - vm := NewVm(st.state, self, 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 -} - func (sm *StateManager) Process(block *Block, dontReact bool) error { if !sm.bc.HasBlock(block.PrevHash) { return ParentError(block.PrevHash) From cebf4e3697dcd20e290ff56ad6e5dfca2059c063 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 12:58:01 +0200 Subject: [PATCH 20/42] Refactored state transitioning to its own model --- ethchain/state_transition.go | 206 +++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 ethchain/state_transition.go diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go new file mode 100644 index 000000000..6ec9205e9 --- /dev/null +++ b/ethchain/state_transition.go @@ -0,0 +1,206 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type StateTransition struct { + coinbase []byte + tx *Transaction + gas *big.Int + state *State + block *Block + + cb, rec, sen *StateObject +} + +func NewStateTransition(coinbase []byte, tx *Transaction, state *State, block *Block) *StateTransition { + return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, 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) TransitionState() (err error) { + //snapshot := st.state.Snapshot() + + defer func() { + if r := recover(); r != nil { + ethutil.Config.Log.Infoln(r) + err = fmt.Errorf("%v", r) + } + }() + + var ( + tx = self.tx + sender = self.Sender() + receiver *StateObject + ) + + if sender.Nonce != tx.Nonce { + return NonceError(tx.Nonce, sender.Nonce) + } + + sender.Nonce += 1 + defer func() { + // Notify all subscribers + //self.Ethereum.Reactor().Post("newTx:post", tx) + }() + + if err = self.BuyGas(); err != nil { + return err + } + + receiver = self.Receiver() + + if err = self.UseGas(GasTx); err != nil { + return err + } + + dataPrice := big.NewInt(int64(len(tx.Data))) + dataPrice.Mul(dataPrice, GasData) + if err = self.UseGas(dataPrice); err != nil { + return err + } + + if receiver == nil { // Contract + receiver = self.MakeStateObject(self.state, tx) + if receiver == nil { + return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx) + } + } + + if err = self.transferValue(sender, receiver); err != nil { + return err + } + + if tx.CreatesContract() { + fmt.Println(Disassemble(receiver.Init())) + // 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 + } + + self.state.UpdateStateObject(sender) + self.state.UpdateStateObject(receiver) + + 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) + } + + // 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 +} From c734dde982c4ce778afa074e94efb09e552dbd84 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 13:06:27 +0200 Subject: [PATCH 21/42] comments & refactor --- ethchain/state_manager.go | 4 +++- ethchain/state_transition.go | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 576afa8d3..c68d5e001 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -124,6 +124,9 @@ done: } } + // 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} @@ -158,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 } diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 6ec9205e9..1256d019c 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -6,6 +6,22 @@ import ( "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 @@ -115,10 +131,6 @@ func (self *StateTransition) TransitionState() (err error) { } sender.Nonce += 1 - defer func() { - // Notify all subscribers - //self.Ethereum.Reactor().Post("newTx:post", tx) - }() if err = self.BuyGas(); err != nil { return err From 81245473486dd680b7121d4b227ca8a57d07b4b1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 13 Jun 2014 16:06:27 +0200 Subject: [PATCH 22/42] Moving a head closer to interop --- ethchain/closure.go | 26 ++++++++++++++------------ ethchain/state_object.go | 8 +++++--- ethchain/state_transition.go | 32 ++++++++++++++++++++++++-------- ethchain/vm.go | 31 ++++++++++++++++++------------- ethutil/common.go | 2 +- peer.go | 4 ++-- 6 files changed, 64 insertions(+), 39 deletions(-) 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/state_object.go b/ethchain/state_object.go index b92374882..12ebf8e9b 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -77,7 +77,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,8 +102,10 @@ 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) { diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 1256d019c..a080c5602 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -116,7 +116,7 @@ func (self *StateTransition) TransitionState() (err error) { defer func() { if r := recover(); r != nil { ethutil.Config.Log.Infoln(r) - err = fmt.Errorf("%v", r) + err = fmt.Errorf("state transition err %v", r) } }() @@ -126,41 +126,51 @@ func (self *StateTransition) TransitionState() (err error) { receiver *StateObject ) + // Make sure this transaction's nonce is correct if sender.Nonce != tx.Nonce { return NonceError(tx.Nonce, sender.Nonce) } + // Increment the nonce for the next transaction sender.Nonce += 1 + // Pre-pay gas / Buy gas of the coinbase account if err = self.BuyGas(); err != nil { return err } + // 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 receiver == nil { // Contract + // 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() { - fmt.Println(Disassemble(receiver.Init())) + //fmt.Println(Disassemble(receiver.Init())) // Evaluate the initialization script // and use the return value as the // script section for the state object. @@ -173,6 +183,10 @@ func (self *StateTransition) TransitionState() (err error) { receiver.script = code } + // Return remaining gas + remaining := new(big.Int).Mul(self.gas, tx.GasPrice) + sender.AddAmount(remaining) + self.state.UpdateStateObject(sender) self.state.UpdateStateObject(receiver) @@ -184,12 +198,14 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount) } - // 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) + 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()) + ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash()) + } return nil } diff --git a/ethchain/vm.go b/ethchain/vm.go index e17264697..f0059f6ac 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -114,14 +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 STOP: + case SUICIDE: case SLOAD: - setStepGasUsage(GasSLoad) + gas.Set(GasSLoad) case SSTORE: var mult *big.Int y, x := stack.Peekn() @@ -133,12 +137,14 @@ 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) + gas.Set(GasBalance) case MSTORE: require(2) newMemSize = stack.Peek().Uint64() + 32 + case MLOAD: + case MSTORE8: require(2) newMemSize = stack.Peek().Uint64() + 1 @@ -149,7 +155,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case SHA3: require(2) - setStepGasUsage(GasSha) + gas.Set(GasSha) newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() case CALLDATACOPY: @@ -162,7 +168,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() case CALL: require(7) - setStepGasUsage(GasCall) + 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() @@ -170,17 +177,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro newMemSize = uint64(math.Max(float64(x), float64(y))) case CREATE: require(3) - setStepGasUsage(GasCreate) + gas.Set(GasCreate) newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() - default: - setStepGasUsage(GasStep) } newMemSize = (newMemSize + 31) / 32 * 32 if newMemSize > uint64(mem.Len()) { m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 - setStepGasUsage(big.NewInt(int64(m))) + addStepGasUsage(big.NewInt(int64(m))) } if !closure.UseGas(gas) { @@ -355,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: @@ -492,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 @@ -503,7 +508,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro //closure.UseGas(gas) // Create the closure - c := NewClosure(closure.callee, + c := NewClosure(closure.caller, closure.Object(), contract.initScript, vm.state, diff --git a/ethutil/common.go b/ethutil/common.go index ddaf78f88..f63ba5d83 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -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/peer.go b/peer.go index 587bc1974..4434715fb 100644 --- a/peer.go +++ b/peer.go @@ -402,7 +402,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) { @@ -414,7 +414,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 From 63883bf27d8b87f601e1603e9024a279b91bffb7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 14 Jun 2014 11:46:09 +0200 Subject: [PATCH 23/42] Moving closer to interop --- ethchain/asm.go | 2 +- ethchain/block_chain.go | 2 ++ ethchain/state_transition.go | 16 ++++++++++------ ethchain/types.go | 2 +- ethminer/miner.go | 11 +++++++++-- peer.go | 4 ++-- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ethchain/asm.go b/ethchain/asm.go index 277326ff9..c267f9b55 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -28,7 +28,7 @@ func Disassemble(script []byte) (asm []string) { if len(data) == 0 { data = []byte{0} } - asm = append(asm, fmt.Sprintf("0x%x", data)) + asm = append(asm, fmt.Sprintf("%#x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 8cede2403..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 { diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index a080c5602..5ded0cddd 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -131,14 +131,21 @@ func (self *StateTransition) TransitionState() (err error) { return NonceError(tx.Nonce, sender.Nonce) } - // Increment the nonce for the next transaction - sender.Nonce += 1 - // 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.state.UpdateStateObject(sender) + self.state.UpdateStateObject(receiver) + }() + + // 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() @@ -187,9 +194,6 @@ func (self *StateTransition) TransitionState() (err error) { remaining := new(big.Int).Mul(self.gas, tx.GasPrice) sender.AddAmount(remaining) - self.state.UpdateStateObject(sender) - self.state.UpdateStateObject(receiver) - return nil } diff --git a/ethchain/types.go b/ethchain/types.go index ee70a8d28..d89fad147 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -226,7 +226,7 @@ var opCodeToString = map[OpCode]string{ func (o OpCode) String() string { str := opCodeToString[o] if len(str) == 0 { - return fmt.Sprintf("Missing opcode 0x%x", int(o)) + return fmt.Sprintf("Missing opcode %#x", int(o)) } return str diff --git a/ethminer/miner.go b/ethminer/miner.go index d05405391..30b7ef35d 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -136,11 +136,18 @@ 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.Coinbase, self.block.State(), self.block, self.txs) - self.txs = txs + // Error may be ignored. It's not important during mining + receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(self.block.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) diff --git a/peer.go b/peer.go index 4434715fb..9da2ed002 100644 --- a/peer.go +++ b/peer.go @@ -19,7 +19,7 @@ const ( // Current protocol version ProtocolVersion = 20 // Interval for ping/pong message - pingPongTimer = 30 * time.Second + pingPongTimer = 1 * time.Second ) type DiscReason byte @@ -270,7 +270,7 @@ out: // Ping timer case <-pingTimer.C: timeSince := time.Since(time.Unix(p.lastPong, 0)) - if p.pingStartTime.IsZero() == false && timeSince > (pingPongTimer+10*time.Second) { + if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+10*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 From 6d52da58d9337b786a0c869974daa91ce0e34a98 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 14 Jun 2014 15:44:13 +0200 Subject: [PATCH 24/42] Logging mechanism --- ethutil/config.go | 27 ++++++++++++++++++++++++++- peer.go | 5 +++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ethutil/config.go b/ethutil/config.go index f935e8f75..90037df87 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -143,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 ) @@ -204,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/peer.go b/peer.go index 9da2ed002..56dd18c65 100644 --- a/peer.go +++ b/peer.go @@ -244,6 +244,8 @@ 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("[PEER] Can't send message:", err) @@ -264,6 +266,7 @@ out: select { // Main message queue. All outbound messages are processed through here case msg := <-p.outputQueue: + p.writeMessage(msg) p.lastSend = time.Now() @@ -316,6 +319,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 From b0e023e43248d09cb273d5b6025d28ee718151c0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 15 Jun 2014 00:04:18 +0200 Subject: [PATCH 25/42] Increase ping timeout to 30 seconds --- peer.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/peer.go b/peer.go index 56dd18c65..0c4d76355 100644 --- a/peer.go +++ b/peer.go @@ -19,7 +19,7 @@ const ( // Current protocol version ProtocolVersion = 20 // Interval for ping/pong message - pingPongTimer = 1 * time.Second + pingPongTimer = 2 * time.Second ) type DiscReason byte @@ -266,14 +266,13 @@ out: select { // Main message queue. All outbound messages are processed through here case msg := <-p.outputQueue: - p.writeMessage(msg) p.lastSend = time.Now() // Ping timer case <-pingTimer.C: timeSince := time.Since(time.Unix(p.lastPong, 0)) - if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+10*time.Second) { + 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 From 5871dbaf5a832a4fd34bdb22cf479d6b0b4da9fb Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 15 Jun 2014 00:10:42 +0200 Subject: [PATCH 26/42] Set contract addr for new transactions --- ethchain/transaction.go | 2 +- ethpub/types.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 7aaab2fd1..3d52e4f73 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -25,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 { diff --git a/ethpub/types.go b/ethpub/types.go index 868fd2713..a76421007 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -111,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") + data := string(tx.Data) + if tx.CreatesContract() { + data = strings.Join(ethchain.Disassemble(tx.Data), "\n") + } - isContract := len(tx.Data) > 0 - - 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 { From d80f999a215b74e23d21f3548486f996c3eb028d Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 15 Jun 2014 00:11:06 +0200 Subject: [PATCH 27/42] Run contracts --- ethchain/state_transition.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 5ded0cddd..2e2a51f72 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -188,6 +188,13 @@ func (self *StateTransition) TransitionState() (err error) { } 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 remaining gas From 8198fd7913ea4066afb5c0cf5e57fa5ec4888fac Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 00:51:04 +0200 Subject: [PATCH 28/42] Cache whole objects instead of states only --- ethchain/state.go | 98 +++++++++++++++++++++++++++++++++++++++- ethchain/state_object.go | 21 +++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index 5af748e00..f413fb8ed 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -16,14 +16,17 @@ 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 func (s *State) Reset() { s.trie.Undo() @@ -43,6 +46,35 @@ func (s *State) Sync() { s.trie.Sync() } +*/ + +// 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() + } +} + +// Syncs the trie and all siblings +func (s *State) Sync() { + // Sync all nested states + for _, stateObject := range s.stateObjects { + if stateObject.state == nil { + continue + } + + stateObject.state.Sync() + } + + s.trie.Sync() +} // Purges the current trie. func (s *State) Purge() int { @@ -54,6 +86,7 @@ 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 == "" { @@ -78,7 +111,6 @@ func (s *State) UpdateStateObject(object *StateObject) { 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]) } ethutil.Config.Db.Put(ethutil.Sha3Bin(object.Script()), object.Script()) @@ -96,13 +128,66 @@ func (s *State) GetAccount(addr []byte) (account *StateObject) { 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 (self *State) UpdateStateObject(stateObject *StateObject) { + addr := stateObject.Address() + + if self.stateObjects[string(addr)] == nil { + self.stateObjects[string(addr)] = stateObject + } + + ethutil.Config.Db.Put(ethutil.Sha3Bin(stateObject.Script()), stateObject.Script()) + + self.trie.Update(string(addr), string(stateObject.RlpEncode())) + + self.manifest.AddObjectChange(stateObject) +} + +func (self *State) GetStateObject(addr []byte) *StateObject { + 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 +} + +func (self *State) GetOrNewStateObject(addr []byte) *StateObject { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + stateObject = NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject + } + + 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 { @@ -111,6 +196,15 @@ func (s *State) Copy() *State { return state } +*/ +func (self *State) Copy() *State { + state := NewState(self.trie.Copy()) + for k, stateObject := range self.stateObjects { + state.stateObjects[k] = stateObject.Copy() + } + + return state +} func (s *State) Snapshot() *State { return s.Copy() diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 12ebf8e9b..3775d436c 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -38,6 +38,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))) @@ -146,6 +150,23 @@ func (self *StateObject) BuyGas(gas, price *big.Int) error { return nil } +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 + 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 From 1fbea2e438d56484ebfa509d7433cc418e17a79b Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 00:51:21 +0200 Subject: [PATCH 29/42] Reworking messaging interface --- ethwire/messaging.go | 163 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 4 deletions(-) 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 From dccef707280bd852ae536e69dea82ad0555ba0a9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 00:51:55 +0200 Subject: [PATCH 30/42] Method for creating a new key from scratch --- ethutil/keypair.go | 6 ++++++ 1 file changed, 6 insertions(+) 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 { From 58a0e8e3e2979b07e9e8f697f947a412ac81e386 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 00:52:10 +0200 Subject: [PATCH 31/42] Changed RlpEncodable --- ethutil/rlp.go | 1 + 1 file changed, 1 insertion(+) 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 { From 02d8ad030fd1293a03cf905d50533678aaea40fd Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 10:35:35 +0200 Subject: [PATCH 32/42] Keeping old code for reference --- ethchain/state.go | 177 +++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index f413fb8ed..9a9d0a278 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -26,28 +26,6 @@ func NewState(trie *ethutil.Trie) *State { return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()} } -/* -// 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() -} -*/ - // Resets the trie and all siblings func (s *State) Reset() { s.trie.Undo() @@ -86,58 +64,6 @@ 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 - } - - 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 (self *State) UpdateStateObject(stateObject *StateObject) { addr := stateObject.Address() @@ -187,23 +113,17 @@ 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() - } - - return state -} -*/ func (self *State) Copy() *State { - state := NewState(self.trie.Copy()) - for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() + 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 { @@ -259,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 +} +*/ From 1d76e433f7866763674e4ef06a4a4d9463276490 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 10:40:21 +0200 Subject: [PATCH 33/42] Removed some comments --- ethchain/state_transition.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 2e2a51f72..94546e556 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -102,7 +102,7 @@ func (self *StateTransition) BuyGas() error { if err != nil { return err } - self.state.UpdateStateObject(coinbase) + //self.state.UpdateStateObject(coinbase) self.AddGas(self.tx.Gas) sender.SubAmount(self.tx.GasValue()) @@ -177,7 +177,6 @@ func (self *StateTransition) TransitionState() (err error) { // Process the init code and create 'valid' contract if tx.CreatesContract() { - //fmt.Println(Disassemble(receiver.Init())) // Evaluate the initialization script // and use the return value as the // script section for the state object. From 15d1f753f7ac2f72ce0637135a17d86b29f15515 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 11:13:06 +0200 Subject: [PATCH 34/42] Removed old fees --- ethchain/fees.go | 24 ------------------------ 1 file changed, 24 deletions(-) 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) -} From 7b55bcf4840fc11315ffd055ce7d419ac9baf168 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 11:13:19 +0200 Subject: [PATCH 35/42] Removed old fees --- ethchain/transaction_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 6538b0029..24836222a 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -179,7 +179,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { //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 { From b836267401b731a2cd17c4866a9727b4a05ec124 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 11:13:37 +0200 Subject: [PATCH 36/42] .. --- peer.go | 1 - 1 file changed, 1 deletion(-) diff --git a/peer.go b/peer.go index 0c4d76355..07c93e5b4 100644 --- a/peer.go +++ b/peer.go @@ -158,7 +158,6 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { } func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { - p := &Peer{ outputQueue: make(chan *ethwire.Msg, outputBufferSize), quit: make(chan bool), From 9f62d441a7c785b88f89d52643a9deaa822af15e Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 11:14:01 +0200 Subject: [PATCH 37/42] Moved gas limit err check to buy gas --- ethchain/state_manager.go | 8 +++-- ethchain/state_object.go | 21 ++++++++++-- ethchain/state_transition.go | 4 +-- ethchain/vm.go | 65 ++++++++++++++++++------------------ ethminer/miner.go | 3 +- 5 files changed, 59 insertions(+), 42 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index c68d5e001..4b1b872cc 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -97,7 +97,7 @@ func (sm *StateManager) BlockChain() *BlockChain { return sm.bc } -func (self *StateManager) ProcessTransactions(coinbase []byte, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { +func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) { var ( receipts Receipts handled, unhandled Transactions @@ -177,9 +177,12 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea } fmt.Println(block.Receipts()) + coinbase := state.GetOrNewStateObject(block.Coinbase) + coinbase.gasPool = block.CalcGasLimit(parent) + // Process the transactions on to current block //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) - sm.ProcessTransactions(block.Coinbase, state, block, parent, block.Transactions()) + sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions()) // Block validation if err := sm.ValidateBlock(block); err != nil { @@ -194,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) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 3775d436c..03f4c9219 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 @@ -139,14 +144,22 @@ 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) - // TODO Do sub from TotalGasPool - // and check if enough left return nil } @@ -158,7 +171,9 @@ func (self *StateObject) Copy() *StateObject { stCopy.ScriptHash = make([]byte, len(self.ScriptHash)) copy(stCopy.ScriptHash, self.ScriptHash) stCopy.Nonce = self.Nonce - stCopy.state = self.state.Copy() + 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)) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 94546e556..76936aa7c 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -32,8 +32,8 @@ type StateTransition struct { cb, rec, sen *StateObject } -func NewStateTransition(coinbase []byte, tx *Transaction, state *State, block *Block) *StateTransition { - return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, nil, nil} +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 { diff --git a/ethchain/vm.go b/ethchain/vm.go index f0059f6ac..2ba0e2ef3 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -169,7 +169,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case CALL: require(7) gas.Set(GasCall) - addStepGasUsage(stack.data[stack.Len()-1]) + 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() @@ -529,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() @@ -538,46 +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 - //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) - } else { - stack.Push(ethutil.BigTrue) - - vm.state.UpdateStateObject(contract) - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) - } - } else { - ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) 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 { + 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 30b7ef35d..8ea6c51e5 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -139,7 +139,8 @@ func (self *Miner) mineNewBlock() { // Accumulate all valid transaction and apply them to the new state // Error may be ignored. It's not important during mining - receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(self.block.Coinbase, self.block.State(), self.block, self.block, self.txs) + coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase) + 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) } From 48bca30e61f869a00111abe5d818ac7379854616 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 11:51:16 +0200 Subject: [PATCH 38/42] Fixed minor issue with the gas pool --- ethchain/state_manager.go | 2 +- ethchain/state_object.go | 4 ++-- ethchain/state_transition.go | 14 ++++++++------ ethminer/miner.go | 2 ++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 4b1b872cc..36bb14846 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -178,7 +178,7 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea fmt.Println(block.Receipts()) coinbase := state.GetOrNewStateObject(block.Coinbase) - coinbase.gasPool = block.CalcGasLimit(parent) + coinbase.SetGasPool(block.CalcGasLimit(parent)) // Process the transactions on to current block //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 03f4c9219..337c5a394 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -120,13 +120,13 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { func (c *StateObject) AddAmount(amount *big.Int) { c.SetAmount(new(big.Int).Add(c.Amount, amount)) - ethutil.Config.Log.Debugf("%x: #%d %v (+ %v)", c.Address(), c.Nonce, 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.Debugf("%x: #%d %v (- %v)", c.Address(), c.Nonce, 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) { diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 76936aa7c..5beef61b4 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -113,12 +113,14 @@ func (self *StateTransition) BuyGas() error { 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) - } - }() + /* + defer func() { + if r := recover(); r != nil { + ethutil.Config.Log.Infoln(r) + err = fmt.Errorf("state transition err %v", r) + } + }() + */ var ( tx = self.tx diff --git a/ethminer/miner.go b/ethminer/miner.go index 8ea6c51e5..1ef9ca229 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -139,7 +139,9 @@ func (self *Miner) mineNewBlock() { // Accumulate all valid transaction and apply them to the new state // 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) From 8b15732c1e8a1a666ae7469bc43d989918ce754a Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 12:04:56 +0200 Subject: [PATCH 39/42] Check for nil receiver --- ethchain/state_transition.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 5beef61b4..c88f4727f 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -141,8 +141,13 @@ func (self *StateTransition) TransitionState() (err error) { // XXX Transactions after this point are considered valid. defer func() { - self.state.UpdateStateObject(sender) - self.state.UpdateStateObject(receiver) + if sender != nil { + self.state.UpdateStateObject(sender) + } + + if receiver != nil { + self.state.UpdateStateObject(receiver) + } }() // Increment the nonce for the next transaction From 0d7763283952a57e5421565cdda19ecabe3222f7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 12:25:18 +0200 Subject: [PATCH 40/42] Refund gas --- ethchain/state_object.go | 9 +++++++++ ethchain/state_transition.go | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 337c5a394..2c9dfb713 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -163,6 +163,15 @@ func (self *StateObject) BuyGas(gas, price *big.Int) error { 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)) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index c88f4727f..25efd64cc 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -110,6 +110,15 @@ func (self *StateTransition) BuyGas() error { 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() @@ -141,6 +150,8 @@ func (self *StateTransition) TransitionState() (err error) { // XXX Transactions after this point are considered valid. defer func() { + self.RefundGas() + if sender != nil { self.state.UpdateStateObject(sender) } @@ -148,6 +159,8 @@ func (self *StateTransition) TransitionState() (err error) { if receiver != nil { self.state.UpdateStateObject(receiver) } + + self.state.UpdateStateObject(self.Coinbase()) }() // Increment the nonce for the next transaction @@ -203,10 +216,6 @@ func (self *StateTransition) TransitionState() (err error) { } } - // Return remaining gas - remaining := new(big.Int).Mul(self.gas, tx.GasPrice) - sender.AddAmount(remaining) - return nil } From 887debb0559b283962530bb42998a67dd1b69347 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 18:20:38 +0200 Subject: [PATCH 41/42] comment --- ethchain/state_object.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 2c9dfb713..1445bcd82 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -147,7 +147,7 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error { 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) + ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: fuel (+ %v)", self.Address(), self.gasPool) } func (self *StateObject) BuyGas(gas, price *big.Int) error { From ff0f15f7634ca713b0ce8232a8fa63eec5c3fad7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Jun 2014 18:25:21 +0200 Subject: [PATCH 42/42] bump --- README.md | 2 +- ethutil/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b10a04c25..4a835afbf 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 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/ethutil/config.go b/ethutil/config.go index 90037df87..a24c39bfe 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -75,7 +75,7 @@ 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.12"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5.13"} Config.conf = g Config.Identifier = id Config.Log = NewLogger(logTypes, LogLevelDebug)