diff options
author | Péter Szilágyi <peterke@gmail.com> | 2018-09-18 21:24:35 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2018-09-18 21:29:51 +0800 |
commit | 5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4 (patch) | |
tree | a2898e4b4d3f8b17d32f86c6964f60f177fa7c5b /core/vm | |
parent | caa2c23a38141911a570ba098a940b4fdbf0aa88 (diff) | |
download | go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar.gz go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar.bz2 go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar.lz go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar.xz go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.tar.zst go-tangerine-5d921fa3a0cea9d87e7fd391c0ddd3115d00d0c4.zip |
core, params: polish net gas metering PR a bit
Diffstat (limited to 'core/vm')
-rw-r--r-- | core/vm/gas_table.go | 117 | ||||
-rw-r--r-- | core/vm/interface.go | 2 | ||||
-rw-r--r-- | core/vm/jump_table.go | 8 | ||||
-rw-r--r-- | core/vm/logger_test.go | 5 | ||||
-rw-r--r-- | core/vm/noop.go | 72 |
5 files changed, 55 insertions, 149 deletions
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 77250978d..10b4f719a 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -117,78 +117,69 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack * func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( - y, x = stack.Back(1), stack.Back(0) - val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) - ) - // This checks for 3 scenario's and calculates gas accordingly - // 1. From a zero-value address to a non-zero value (NEW VALUE) - // 2. From a non-zero value address to a zero-value address (DELETE) - // 3. From a non-zero to a non-zero (CHANGE) - if val == (common.Hash{}) && y.Sign() != 0 { - // 0 => non 0 - return params.SstoreSetGas, nil - } else if val != (common.Hash{}) && y.Sign() == 0 { - // non 0 => 0 - evm.StateDB.AddRefund(params.SstoreRefundGas) - return params.SstoreClearGas, nil - } else { - // non 0 => non 0 (or 0 => 0) - return params.SstoreResetGas, nil - } -} - -// gasSStoreEip1283 calculates SSTORE gas cost according to EIP-1283 -func gasSStoreEip1283(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - var ( y, x = stack.Back(1), stack.Back(0) current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) ) - //1. If current value equals new value (this is a no-op), 200 gas is deducted. - //2. If current value does not equal new value - // 2.1 If original value equals current value (this storage slot has not been changed by the current execution context) - // 2.1.1 If original value is 0, 20000 gas is deducted. - // 2.1.2 Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter. - // 2.2 If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. - // 2.2.1 If original value is not 0 - // 2.2.1.1 If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. - // 2.2.1.2 If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. - // 2.2.2 If original value equals new value (this storage slot is reset) - // 2.2.2.1 If original value is 0, add 19800 gas to refund counter. - // 2.2.2.2 Otherwise, add 4800 gas to refund counter. - new := common.BigToHash(y) - if current == new { - // 1. current == new - return 200, nil - } - original := evm.StateDB.GetStateOriginal(contract.Address(), common.BigToHash(x)) - // 2 - if original == current { // 2.1 - if original == (common.Hash{}) { // 2.1.1 - return 20000, nil + // The legacy gas metering only takes into consideration the current state + if !evm.chainRules.IsConstantinople { + // This checks for 3 scenario's and calculates gas accordingly: + // + // 1. From a zero-value address to a non-zero value (NEW VALUE) + // 2. From a non-zero value address to a zero-value address (DELETE) + // 3. From a non-zero to a non-zero (CHANGE) + switch { + case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 + return params.SstoreSetGas, nil + case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0 + evm.StateDB.AddRefund(params.SstoreRefundGas) + return params.SstoreClearGas, nil + default: // non 0 => non 0 (or 0 => 0) + return params.SstoreResetGas, nil + } + } + // The new gas metering is based on net gas costs (EIP-1283): + // + // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + // 2. If current value does not equal new value + // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + // 2.1.1. If original value is 0, 20000 gas is deducted. + // 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter. + // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + // 2.2.1. If original value is not 0 + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + // 2.2.2. If original value equals new value (this storage slot is reset) + // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. + // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + value := common.BigToHash(y) + if current == value { // noop (1) + return params.NetSstoreNoopGas, nil + } + original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + if original == current { + if original == (common.Hash{}) { // create slot (2.1.1) + return params.NetSstoreInitGas, nil } - // 2.1.2 - if new == (common.Hash{}) { - evm.StateDB.AddRefund(15000) + if value == (common.Hash{}) { // delete slot (2.1.2b) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) } - return 5000, nil - } - // 2.2 - if original != (common.Hash{}) { // 2.2.1 - if current == (common.Hash{}) { // 2.2.1.1 - evm.StateDB.SubRefund(15000) - } else { - // 2.2.1.2 - evm.StateDB.AddRefund(15000) + return params.NetSstoreCleanGas, nil // write existing slot (2.1.2) + } + if original != (common.Hash{}) { + if current == (common.Hash{}) { // recreate slot (2.2.1.1) + evm.StateDB.SubRefund(params.NetSstoreClearRefund) + } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) } } - if original == new { // 2.2.2 - if original == (common.Hash{}) { - evm.StateDB.AddRefund(19800) - } else { - evm.StateDB.AddRefund(4800) + if original == value { + if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + evm.StateDB.AddRefund(params.NetSstoreResetClearRefund) + } else { // reset to original existing slot (2.2.2.2) + evm.StateDB.AddRefund(params.NetSstoreResetRefund) } } - return 200, nil + return params.NetSstoreDirtyGas, nil } func makeGasLog(n uint64) gasFunc { diff --git a/core/vm/interface.go b/core/vm/interface.go index 2e2e3e925..fc15082f1 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -43,8 +43,8 @@ type StateDB interface { SubRefund(uint64) GetRefund() uint64 + GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash - GetStateOriginal(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) Suicide(common.Address) bool diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 8a997adc4..deedf70cd 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -95,14 +95,6 @@ func newConstantinopleInstructionSet() [256]operation { writes: true, returns: true, } - instructionSet[SSTORE] = operation{ - execute: opSstore, - gasCost: gasSStoreEip1283, - validateStack: makeStackFunc(2, 0), - valid: true, - writes: true, - } - return instructionSet } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 28830c445..cdbb70dc4 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -41,11 +41,6 @@ func (d *dummyContractRef) SetBalance(*big.Int) {} func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } -type dummyStateDB struct { - NoopStateDB - ref *dummyContractRef -} - func TestStoreCapture(t *testing.T) { var ( env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) diff --git a/core/vm/noop.go b/core/vm/noop.go deleted file mode 100644 index 19539d978..000000000 --- a/core/vm/noop.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package vm - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool { - return true -} -func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {} - -type NoopEVMCallContext struct{} - -func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return nil, nil -} -func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return nil, nil -} -func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { - return nil, common.Address{}, nil -} -func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - return nil, nil -} - -type NoopStateDB struct{} - -func (NoopStateDB) CreateAccount(common.Address) {} -func (NoopStateDB) SubBalance(common.Address, *big.Int) {} -func (NoopStateDB) AddBalance(common.Address, *big.Int) {} -func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil } -func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 } -func (NoopStateDB) SetNonce(common.Address, uint64) {} -func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} } -func (NoopStateDB) GetCode(common.Address) []byte { return nil } -func (NoopStateDB) SetCode(common.Address, []byte) {} -func (NoopStateDB) GetCodeSize(common.Address) int { return 0 } -func (NoopStateDB) AddRefund(uint64) {} -func (NoopStateDB) SubRefund(uint64) {} -func (NoopStateDB) GetRefund() uint64 { return 0 } -func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} } -func (NoopStateDB) GetStateOriginal(common.Address, common.Hash) common.Hash { return common.Hash{} } -func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {} -func (NoopStateDB) Suicide(common.Address) bool { return false } -func (NoopStateDB) HasSuicided(common.Address) bool { return false } -func (NoopStateDB) Exist(common.Address) bool { return false } -func (NoopStateDB) Empty(common.Address) bool { return false } -func (NoopStateDB) RevertToSnapshot(int) {} -func (NoopStateDB) Snapshot() int { return 0 } -func (NoopStateDB) AddLog(*types.Log) {} -func (NoopStateDB) AddPreimage(common.Hash, []byte) {} -func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {} |