diff --git a/core/state/state_object.go b/core/state/state_object.go
index b05afec936aa9deb3a322af333b5c8748c27c7e5..f41ab0409221b2a382d2d7e93f80b3a6eef44b35 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -77,9 +77,9 @@ type stateObject struct {
 	trie Trie // storage trie, which becomes non-nil on first access
 	code Code // contract bytecode, which gets set when code is loaded
 
-	cachedStorage Storage // Storage entry cache to avoid duplicate reads
+	originStorage Storage // Storage cache of original entries to dedup rewrites
 	dirtyStorage  Storage // Storage entries that need to be flushed to disk
-	originalValue Storage // Map of original storage values, at the beginning of current call context
+
 	// Cache flags.
 	// When an object is marked suicided it will be delete from the trie
 	// during the "update" phase of the state transition.
@@ -115,9 +115,8 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
 		address:       address,
 		addrHash:      crypto.Keccak256Hash(address[:]),
 		data:          data,
-		cachedStorage: make(Storage),
+		originStorage: make(Storage),
 		dirtyStorage:  make(Storage),
-		originalValue: make(Storage),
 	}
 }
 
@@ -160,13 +159,25 @@ func (c *stateObject) getTrie(db Database) Trie {
 	return c.trie
 }
 
-// GetState returns a value in account storage.
+// GetState retrieves a value from the account storage trie.
 func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
-	value, exists := self.cachedStorage[key]
-	if exists {
+	// If we have a dirty value for this state entry, return it
+	value, dirty := self.dirtyStorage[key]
+	if dirty {
+		return value
+	}
+	// Otherwise return the entry's original value
+	return self.GetCommittedState(db, key)
+}
+
+// GetCommittedState retrieves a value from the committed account storage trie.
+func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
+	// If we have the original value cached, return that
+	value, cached := self.originStorage[key]
+	if cached {
 		return value
 	}
-	// Load from DB in case it is missing.
+	// Otherwise load the value from the database
 	enc, err := self.getTrie(db).TryGet(key[:])
 	if err != nil {
 		self.setError(err)
@@ -179,37 +190,27 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
 		}
 		value.SetBytes(content)
 	}
-	self.cachedStorage[key] = value
+	self.originStorage[key] = value
 	return value
 }
 
-// GetOriginalStateValue returns the state value that is currently in the Trie, that is, ignoring any
-// changes that have been made but not yet written to trie.
-func (self *stateObject) GetOriginalStateValue(db Database, key common.Hash) common.Hash{
-	if original, exist:= self.originalValue[key]; exist {
-		// original value has been set, return it
-		return original
-	}
-	return self.GetState(db, key)
-}
-
 // SetState updates a value in account storage.
 func (self *stateObject) SetState(db Database, key, value common.Hash) {
+	// If the new value is the same as old, don't set
 	prev := self.GetState(db, key)
+	if prev == value {
+		return
+	}
+	// New value is different, update and journal the change
 	self.db.journal.append(storageChange{
 		account:  &self.address,
 		key:      key,
 		prevalue: prev,
 	})
-	if _, isSet := self.originalValue[key]; !isSet {
-		// original value has not been set, so set it now
-		self.originalValue[key] = prev
-	}
 	self.setState(key, value)
 }
 
 func (self *stateObject) setState(key, value common.Hash) {
-	self.cachedStorage[key] = value
 	self.dirtyStorage[key] = value
 }
 
@@ -218,6 +219,13 @@ func (self *stateObject) updateTrie(db Database) Trie {
 	tr := self.getTrie(db)
 	for key, value := range self.dirtyStorage {
 		delete(self.dirtyStorage, key)
+
+		// Skip noop changes, persist actual changes
+		if value == self.originStorage[key] {
+			continue
+		}
+		self.originStorage[key] = value
+
 		if (value == common.Hash{}) {
 			self.setError(tr.TryDelete(key[:]))
 			continue
@@ -226,10 +234,6 @@ func (self *stateObject) updateTrie(db Database) Trie {
 		v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
 		self.setError(tr.TryUpdate(key[:], v))
 	}
-	// Clean the map containing 'original' value of storage entries
-	for k, _ := range self.originalValue {
-		delete(self.originalValue, k)
-	}
 	return tr
 }
 
@@ -299,8 +303,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject {
 	}
 	stateObject.code = self.code
 	stateObject.dirtyStorage = self.dirtyStorage.Copy()
-	stateObject.cachedStorage = self.dirtyStorage.Copy()
-	stateObject.originalValue = self.originalValue.Copy()
+	stateObject.originStorage = self.originStorage.Copy()
 	stateObject.suicided = self.suicided
 	stateObject.dirtyCode = self.dirtyCode
 	stateObject.deleted = self.deleted
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 123559ea9bef3f339b0591f50e4e5919b65a3a29..a09273f3b1fbf569ca91c2f3ef8f11cc783f2267 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -96,11 +96,15 @@ func (s *StateSuite) TestNull(c *checker.C) {
 	s.state.CreateAccount(address)
 	//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
 	var value common.Hash
+
 	s.state.SetState(address, common.Hash{}, value)
 	s.state.Commit(false)
-	value = s.state.GetState(address, common.Hash{})
-	if value != (common.Hash{}) {
-		c.Errorf("expected empty hash. got %x", value)
+
+	if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
+		c.Errorf("expected empty current value, got %x", value)
+	}
+	if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
+		c.Errorf("expected empty committed value, got %x", value)
 	}
 }
 
@@ -110,20 +114,24 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
 	data1 := common.BytesToHash([]byte{42})
 	data2 := common.BytesToHash([]byte{43})
 
+	// snapshot the genesis state
+	genesis := s.state.Snapshot()
+
 	// set initial state object value
 	s.state.SetState(stateobjaddr, storageaddr, data1)
-	// get snapshot of current state
 	snapshot := s.state.Snapshot()
 
-	// set new state object value
+	// set a new state object value, revert it and ensure correct content
 	s.state.SetState(stateobjaddr, storageaddr, data2)
-	// restore snapshot
 	s.state.RevertToSnapshot(snapshot)
 
-	// get state storage value
-	res := s.state.GetState(stateobjaddr, storageaddr)
+	c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
+	c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
 
-	c.Assert(data1, checker.DeepEquals, res)
+	// revert up to the genesis state and ensure correct content
+	s.state.RevertToSnapshot(genesis)
+	c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
+	c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
 }
 
 func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
@@ -208,24 +216,30 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
 		t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
 	}
 
-	if len(so1.cachedStorage) != len(so0.cachedStorage) {
-		t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
+	if len(so1.dirtyStorage) != len(so0.dirtyStorage) {
+		t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage))
 	}
-	for k, v := range so1.cachedStorage {
-		if so0.cachedStorage[k] != v {
-			t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
+	for k, v := range so1.dirtyStorage {
+		if so0.dirtyStorage[k] != v {
+			t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v)
 		}
 	}
-	for k, v := range so0.cachedStorage {
-		if so1.cachedStorage[k] != v {
-			t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
+	for k, v := range so0.dirtyStorage {
+		if so1.dirtyStorage[k] != v {
+			t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v)
 		}
 	}
-
-	if so0.suicided != so1.suicided {
-		t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
+	if len(so1.originStorage) != len(so0.originStorage) {
+		t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
+	}
+	for k, v := range so1.originStorage {
+		if so0.originStorage[k] != v {
+			t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v)
+		}
 	}
-	if so0.deleted != so1.deleted {
-		t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
+	for k, v := range so0.originStorage {
+		if so1.originStorage[k] != v {
+			t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
+		}
 	}
 }
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 515ff57bf2b5331e3824b1d9237679ca15da12b6..216667ce983f97c4b1fe9c20ac9405a6f4c8ec3a 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -247,18 +247,20 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
 	return common.BytesToHash(stateObject.CodeHash())
 }
 
-func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
+// GetState retrieves a value from the given account's storage trie.
+func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
 	stateObject := self.getStateObject(addr)
 	if stateObject != nil {
-		return stateObject.GetState(self.db, bhash)
+		return stateObject.GetState(self.db, hash)
 	}
 	return common.Hash{}
 }
 
-func (self *StateDB) GetStateOriginal(addr common.Address, bhash common.Hash) common.Hash {
+// GetCommittedState retrieves a value from the given account's committed storage trie.
+func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
 	stateObject := self.getStateObject(addr)
 	if stateObject != nil {
-		return stateObject.GetOriginalStateValue(self.db, bhash)
+		return stateObject.GetCommittedState(self.db, hash)
 	}
 	return common.Hash{}
 }
@@ -454,19 +456,14 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
 	if so == nil {
 		return
 	}
-
-	// When iterating over the storage check the cache first
-	for h, value := range so.cachedStorage {
-		cb(h, value)
-	}
-
 	it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
 	for it.Next() {
-		// ignore cached values
 		key := common.BytesToHash(db.trie.GetKey(it.Key))
-		if _, ok := so.cachedStorage[key]; !ok {
-			cb(key, common.BytesToHash(it.Value))
+		if value, dirty := so.dirtyStorage[key]; dirty {
+			cb(key, value)
+			continue
 		}
+		cb(key, common.BytesToHash(it.Value))
 	}
 }
 
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index e2b349de86c179be85a8b013b560a4bbb8cf7f06..cbd5bc75e75f98285b62d92eafe7aa14c05c158e 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -381,11 +381,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
 		checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
 		// Check storage.
 		if obj := state.getStateObject(addr); obj != nil {
-			state.ForEachStorage(addr, func(key, val common.Hash) bool {
-				return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
+			state.ForEachStorage(addr, func(key, value common.Hash) bool {
+				return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
 			})
-			checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool {
-				return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
+			checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
+				return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
 			})
 		}
 		if err != nil {
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 77250978d924902482246a8cd6a10894a27bc040..10b4f719a7fb4f2c08d67d5753bc8cfe4f3672dc 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -116,79 +116,70 @@ 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 2e2e3e925afe92c66df0f161a915bd25998318db..fc15082f18ed27fcb4222216b8d44696ce78b578 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 8a997adc42e27d1569401beb96eeb76fa9c82d03..deedf70cdb7d5dabd2ea17781be7c89f9c5aa1d1 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 28830c445c281fc10bfe343bb8df6a245929b6e3..cdbb70dc4208a6f711c4d320965cb57322688296 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 19539d9780485ae12c6c200998169cdc6dd00769..0000000000000000000000000000000000000000
--- 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) {}
diff --git a/params/config.go b/params/config.go
index 629720550aa05f27d5673d0f0b3738e8188b6174..1c3e25eb7650d98ebf68adad167fe3866130258c 100644
--- a/params/config.go
+++ b/params/config.go
@@ -335,7 +335,7 @@ func (err *ConfigCompatError) Error() string {
 type Rules struct {
 	ChainID                                   *big.Int
 	IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
-	IsByzantium                               bool
+	IsByzantium, IsConstantinople             bool
 }
 
 // Rules ensures c's ChainID is not nil.
@@ -344,5 +344,13 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
 	if chainID == nil {
 		chainID = new(big.Int)
 	}
-	return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num)}
+	return Rules{
+		ChainID:          new(big.Int).Set(chainID),
+		IsHomestead:      c.IsHomestead(num),
+		IsEIP150:         c.IsEIP150(num),
+		IsEIP155:         c.IsEIP155(num),
+		IsEIP158:         c.IsEIP158(num),
+		IsByzantium:      c.IsByzantium(num),
+		IsConstantinople: c.IsConstantinople(num),
+	}
 }
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 4b53b3320a4c6fba3f6957f24314c7a9049baf41..c8b6609afbfea6b2469f70e221d0809dfc6f379a 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -32,15 +32,26 @@ const (
 	TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
 	TxDataZeroGas         uint64 = 4     // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
 	QuadCoeffDiv          uint64 = 512   // Divisor for the quadratic particle of the memory cost equation.
-	SstoreSetGas          uint64 = 20000 // Once per SLOAD operation.
 	LogDataGas            uint64 = 8     // Per byte in a LOG* operation's data.
 	CallStipend           uint64 = 2300  // Free gas given at beginning of call.
 
-	Sha3Gas          uint64 = 30    // Once per SHA3 operation.
-	Sha3WordGas      uint64 = 6     // Once per word of the SHA3 operation's data.
-	SstoreResetGas   uint64 = 5000  // Once per SSTORE operation if the zeroness changes from zero.
-	SstoreClearGas   uint64 = 5000  // Once per SSTORE operation if the zeroness doesn't change.
-	SstoreRefundGas  uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
+	Sha3Gas     uint64 = 30 // Once per SHA3 operation.
+	Sha3WordGas uint64 = 6  // Once per word of the SHA3 operation's data.
+
+	SstoreSetGas    uint64 = 20000 // Once per SLOAD operation.
+	SstoreResetGas  uint64 = 5000  // Once per SSTORE operation if the zeroness changes from zero.
+	SstoreClearGas  uint64 = 5000  // Once per SSTORE operation if the zeroness doesn't change.
+	SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
+
+	NetSstoreNoopGas  uint64 = 200   // Once per SSTORE operation if the value doesn't change.
+	NetSstoreInitGas  uint64 = 20000 // Once per SSTORE operation from clean zero.
+	NetSstoreCleanGas uint64 = 5000  // Once per SSTORE operation from clean non-zero.
+	NetSstoreDirtyGas uint64 = 200   // Once per SSTORE operation from dirty.
+
+	NetSstoreClearRefund      uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
+	NetSstoreResetRefund      uint64 = 4800  // Once per SSTORE operation for resetting to the original non-zero value
+	NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
+
 	JumpdestGas      uint64 = 1     // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
 	EpochDuration    uint64 = 30000 // Duration between proof-of-work epochs.
 	CallGas          uint64 = 40    // Once per CALL operation & message call transaction.