diff --git a/ethchain/block.go b/ethchain/block.go index 73e29f878..fee4a2d59 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -154,6 +154,36 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { return true } +func (block *Block) CalcGasLimit(parent *Block) *big.Int { + if block.Number.Cmp(big.NewInt(0)) == 0 { + return ethutil.BigPow(10, 6) + } + + previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit) + current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5)) + curInt := new(big.Int).Div(current.Num(), current.Denom()) + + result := new(big.Int).Add(previous, curInt) + result.Div(result, big.NewInt(1024)) + + min := ethutil.BigPow(10, 4) + + return ethutil.BigMax(min, result) + /* + base := new(big.Int) + base2 := new(big.Int) + parentGL := bc.CurrentBlock.GasLimit + parentUsed := bc.CurrentBlock.GasUsed + + base.Mul(parentGL, big.NewInt(1024-1)) + base2.Mul(parentUsed, big.NewInt(6)) + base2.Div(base2, big.NewInt(5)) + base.Add(base, base2) + base.Div(base, big.NewInt(1024)) + */ + +} + func (block *Block) BlockInfo() BlockInfo { bi := BlockInfo{} data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 68ef9d47e..8cede2403 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 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 e783ffdd8..9631b55fe 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -188,6 +188,8 @@ func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block * // 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 { @@ -196,6 +198,12 @@ func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block * 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) } @@ -207,6 +215,9 @@ func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block * validTxs = append(validTxs, tx) } + // Update the total gas used for the block (to be mined) + block.GasUsed = totalUsedGas + return receipts, validTxs } @@ -226,6 +237,7 @@ func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *B script []byte ) totalGasUsed = big.NewInt(0) + snapshot := state.Snapshot() ca := state.GetAccount(coinbase) // Apply the transaction to the current state @@ -266,6 +278,14 @@ func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *B } } + 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 } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index bc7bde797..6220f29a3 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 { @@ -181,6 +182,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 diff --git a/ethpub/types.go b/ethpub/types.go index 6893c7e09..868fd2713 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -46,6 +46,8 @@ type PBlock struct { Transactions string `json:"transactions"` Time int64 `json:"time"` Coinbase string `json:"coinbase"` + GasLimit string `json:"gasLimit"` + GasUsed string `json:"gasUsed"` } // Creates a new QML Block from a chain block @@ -64,7 +66,7 @@ func NewPBlock(block *ethchain.Block) *PBlock { return nil } - return &PBlock{ref: block, Number: int(block.Number.Uint64()), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)} + return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)} } func (self *PBlock) ToString() string { diff --git a/ethutil/big.go b/ethutil/big.go index 1c25a4784..7af6f7414 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -68,8 +68,8 @@ func BigCopy(src *big.Int) *big.Int { // Returns the maximum size big integer func BigMax(x, y *big.Int) *big.Int { if x.Cmp(y) <= 0 { - return x + return y } - return y + return x } diff --git a/ethutil/common.go b/ethutil/common.go index c7973eb92..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 { diff --git a/peer.go b/peer.go index acebd2756..fc3c7b1d5 100644 --- a/peer.go +++ b/peer.go @@ -18,6 +18,8 @@ const ( outputBufferSize = 50 // Current protocol version ProtocolVersion = 20 + // 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()