diff --git a/core/vm/eips.go b/core/vm/eips.go
new file mode 100644
index 0000000000000000000000000000000000000000..89e76c0271fa48002e482716b096c0a5c2a01dea
--- /dev/null
+++ b/core/vm/eips.go
@@ -0,0 +1,63 @@
+// Copyright 2019 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 (
+	"fmt"
+	
+	"github.com/ethereum/go-ethereum/params"
+)
+
+// EnableEIP enables the given EIP on the config.
+// This operation writes in-place, and callers need to ensure that the globally
+// defined jump tables are not polluted.
+func EnableEIP(eipNum int, jt *JumpTable) error {
+	switch eipNum {
+	case 1884:
+		enable1884(jt)
+	default:
+		return fmt.Errorf("undefined eip %d", eipNum)
+	}
+	return nil
+}
+
+// enable1884 applies EIP-1884 to the given jump table:
+// - Increase cost of BALANCE to 700
+// - Increase cost of EXTCODEHASH to 700
+// - Increase cost of SLOAD to 800
+// - Define SELFBALANCE, with cost GasFastStep (5)
+func enable1884(jt *JumpTable) {
+	// Gas cost changes
+	jt[BALANCE].constantGas = params.BalanceGasEIP1884
+	jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
+	jt[SLOAD].constantGas = params.SloadGasEIP1884
+
+	// New opcode
+	jt[SELFBALANCE] = operation{
+		execute:     opSelfBalance,
+		constantGas: GasFastStep,
+		minStack:    minStack(0, 1),
+		maxStack:    maxStack(0, 1),
+		valid:       true,
+	}
+}
+
+func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+	balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(contract.Address()))
+	stack.push(balance)
+	return nil, nil
+}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 18081ec4c9a4921180e309bf896143aca1b28908..be6e00a6e624a60dc57d7158a7831636aba4632f 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -23,6 +23,7 @@ import (
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/math"
+	"github.com/ethereum/go-ethereum/log"
 )
 
 // Config are the configuration options for the Interpreter
@@ -36,6 +37,8 @@ type Config struct {
 
 	EWASMInterpreter string // External EWASM interpreter options
 	EVMInterpreter   string // External EVM interpreter options
+
+	ExtraEips []int // Additional EIPS that are to be enabled
 }
 
 // Interpreter is used to run Ethereum based contracts and will utilise the
@@ -88,20 +91,29 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
 	// the jump table was initialised. If it was not
 	// we'll set the default jump table.
 	if !cfg.JumpTable[STOP].valid {
+		var jt JumpTable
 		switch {
 		case evm.chainRules.IsConstantinople:
-			cfg.JumpTable = constantinopleInstructionSet
+			jt = constantinopleInstructionSet
 		case evm.chainRules.IsByzantium:
-			cfg.JumpTable = byzantiumInstructionSet
+			jt = byzantiumInstructionSet
 		case evm.chainRules.IsEIP158:
-			cfg.JumpTable = spuriousDragonInstructionSet
+			jt = spuriousDragonInstructionSet
 		case evm.chainRules.IsEIP150:
-			cfg.JumpTable = tangerineWhistleInstructionSet
+			jt = tangerineWhistleInstructionSet
 		case evm.chainRules.IsHomestead:
-			cfg.JumpTable = homesteadInstructionSet
+			jt = homesteadInstructionSet
 		default:
-			cfg.JumpTable = frontierInstructionSet
+			jt = frontierInstructionSet
+		}
+		for i, eip := range cfg.ExtraEips {
+			if err := EnableEIP(eip, &jt); err != nil {
+				// Disable it, so caller can check if it's activated or not
+				cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
+				log.Error("EIP activation failed", "eip", eip, "error", err)
+			}
 		}
+		cfg.JumpTable = jt
 	}
 
 	return &EVMInterpreter{
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index a14f3fd9802c672d21bbe12da8bfb78d1363c37a..da532541c68b487092467ac82a5256629c268d6b 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -62,9 +62,12 @@ var (
 	constantinopleInstructionSet   = newConstantinopleInstructionSet()
 )
 
+// JumpTable contains the EVM opcodes supported at a given fork.
+type JumpTable [256]operation
+
 // NewConstantinopleInstructionSet returns the frontier, homestead
 // byzantium and contantinople instructions.
-func newConstantinopleInstructionSet() [256]operation {
+func newConstantinopleInstructionSet() JumpTable {
 	// instructions that can be executed during the byzantium phase.
 	instructionSet := newByzantiumInstructionSet()
 	instructionSet[SHL] = operation{
@@ -90,7 +93,7 @@ func newConstantinopleInstructionSet() [256]operation {
 	}
 	instructionSet[EXTCODEHASH] = operation{
 		execute:     opExtCodeHash,
-		constantGas: params.ExtcodeHashGas,
+		constantGas: params.ExtcodeHashGasConstantinople,
 		minStack:    minStack(1, 1),
 		maxStack:    maxStack(1, 1),
 		valid:       true,
@@ -111,7 +114,7 @@ func newConstantinopleInstructionSet() [256]operation {
 
 // NewByzantiumInstructionSet returns the frontier, homestead and
 // byzantium instructions.
-func newByzantiumInstructionSet() [256]operation {
+func newByzantiumInstructionSet() JumpTable {
 	// instructions that can be executed during the homestead phase.
 	instructionSet := newSpuriousDragonInstructionSet()
 	instructionSet[STATICCALL] = operation{
@@ -154,7 +157,7 @@ func newByzantiumInstructionSet() [256]operation {
 }
 
 // EIP 158 a.k.a Spurious Dragon
-func newSpuriousDragonInstructionSet() [256]operation {
+func newSpuriousDragonInstructionSet() JumpTable {
 	instructionSet := newTangerineWhistleInstructionSet()
 	instructionSet[EXP].dynamicGas = gasExpEIP158
 	return instructionSet
@@ -162,7 +165,7 @@ func newSpuriousDragonInstructionSet() [256]operation {
 }
 
 // EIP 150 a.k.a Tangerine Whistle
-func newTangerineWhistleInstructionSet() [256]operation {
+func newTangerineWhistleInstructionSet() JumpTable {
 	instructionSet := newHomesteadInstructionSet()
 	instructionSet[BALANCE].constantGas = params.BalanceGasEIP150
 	instructionSet[EXTCODESIZE].constantGas = params.ExtcodeSizeGasEIP150
@@ -176,7 +179,7 @@ func newTangerineWhistleInstructionSet() [256]operation {
 
 // NewHomesteadInstructionSet returns the frontier and homestead
 // instructions that can be executed during the homestead phase.
-func newHomesteadInstructionSet() [256]operation {
+func newHomesteadInstructionSet() JumpTable {
 	instructionSet := newFrontierInstructionSet()
 	instructionSet[DELEGATECALL] = operation{
 		execute:     opDelegateCall,
@@ -193,8 +196,8 @@ func newHomesteadInstructionSet() [256]operation {
 
 // NewFrontierInstructionSet returns the frontier instructions
 // that can be executed during the frontier phase.
-func newFrontierInstructionSet() [256]operation {
-	return [256]operation{
+func newFrontierInstructionSet() JumpTable {
+	return JumpTable{
 		STOP: {
 			execute:     opStop,
 			constantGas: 0,
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 4349ffd29525d6b73b257713ac2ad0deecbc6b2f..b385fd14adf1aa60c78a72658a549df3ab8d6b5d 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -101,6 +101,7 @@ const (
 	NUMBER
 	DIFFICULTY
 	GASLIMIT
+	SELFBALANCE = 0x47
 )
 
 // 0x50 range - 'storage' and execution.
@@ -271,12 +272,13 @@ var opCodeToString = map[OpCode]string{
 	EXTCODEHASH:    "EXTCODEHASH",
 
 	// 0x40 range - block operations.
-	BLOCKHASH:  "BLOCKHASH",
-	COINBASE:   "COINBASE",
-	TIMESTAMP:  "TIMESTAMP",
-	NUMBER:     "NUMBER",
-	DIFFICULTY: "DIFFICULTY",
-	GASLIMIT:   "GASLIMIT",
+	BLOCKHASH:   "BLOCKHASH",
+	COINBASE:    "COINBASE",
+	TIMESTAMP:   "TIMESTAMP",
+	NUMBER:      "NUMBER",
+	DIFFICULTY:  "DIFFICULTY",
+	GASLIMIT:    "GASLIMIT",
+	SELFBALANCE: "SELFBALANCE",
 
 	// 0x50 range - 'storage' and execution.
 	POP: "POP",
@@ -444,6 +446,7 @@ var stringToOp = map[string]OpCode{
 	"NUMBER":         NUMBER,
 	"DIFFICULTY":     DIFFICULTY,
 	"GASLIMIT":       GASLIMIT,
+	"SELFBALANCE":    SELFBALANCE,
 	"POP":            POP,
 	"MLOAD":          MLOAD,
 	"MSTORE":         MSTORE,
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 110fc16ecb347428e97b12a45a3f966b0bd2f84f..943788be8cf2e4539067fb80989f03a951bafd6a 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -70,16 +70,19 @@ const (
 	TxDataNonZeroGas      uint64 = 68    // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
 
 	// These have been changed during the course of the chain
-	CallGasFrontier        uint64 = 40  // Once per CALL operation & message call transaction.
-	CallGasEIP150          uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
-	BalanceGasFrontier     uint64 = 20  // The cost of a BALANCE operation
-	BalanceGasEIP150       uint64 = 400 // The cost of a BALANCE operation after Tangerine
-	ExtcodeSizeGasFrontier uint64 = 20  // Cost of EXTCODESIZE before EIP 150 (Tangerine)
-	ExtcodeSizeGasEIP150   uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine)
-	SloadGasFrontier       uint64 = 50
-	SloadGasEIP150         uint64 = 200
-	ExtcodeHashGas         uint64 = 400  // Cost of EXTCODEHASH (introduced in Constantinople)
-	SelfdestructGasEIP150  uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine)
+	CallGasFrontier              uint64 = 40  // Once per CALL operation & message call transaction.
+	CallGasEIP150                uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
+	BalanceGasFrontier           uint64 = 20  // The cost of a BALANCE operation
+	BalanceGasEIP150             uint64 = 400 // The cost of a BALANCE operation after Tangerine
+	BalanceGasEIP1884            uint64 = 700 // The cost of a BALANCE operation after EIP 1884 (part of Istanbul)
+	ExtcodeSizeGasFrontier       uint64 = 20  // Cost of EXTCODESIZE before EIP 150 (Tangerine)
+	ExtcodeSizeGasEIP150         uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine)
+	SloadGasFrontier             uint64 = 50
+	SloadGasEIP150               uint64 = 200
+	SloadGasEIP1884              uint64 = 800  // Cost of SLOAD after EIP 1884 (part of Istanbul)
+	ExtcodeHashGasConstantinople uint64 = 400  // Cost of EXTCODEHASH (introduced in Constantinople)
+	ExtcodeHashGasEIP1884        uint64 = 700  // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul)
+	SelfdestructGasEIP150        uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine)
 
 	// EXP has a dynamic portion depending on the size of the exponent
 	ExpByteFrontier uint64 = 10 // was set to 10 in Frontier
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 0b78f26ed1af21fadfb05fc697286643c80e164a..c6341e5248ebbfa8af3150060ff79be7a92f53e0 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -21,6 +21,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"math/big"
+	"strconv"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/common"
@@ -109,6 +110,29 @@ type stTransactionMarshaling struct {
 	PrivateKey hexutil.Bytes
 }
 
+// getVMConfig takes a fork definition and returns a chain config.
+// The fork definition can be
+// - a plain forkname, e.g. `Byzantium`,
+// - a fork basename, and a list of EIPs to enable; e.g. `Byzantium+1884+1283`.
+func getVMConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) {
+	var (
+		splitForks            = strings.Split(forkString, "+")
+		ok                    bool
+		baseName, eipsStrings = splitForks[0], splitForks[1:]
+	)
+	if baseConfig, ok = Forks[baseName]; !ok {
+		return nil, nil, UnsupportedForkError{baseName}
+	}
+	for _, eip := range eipsStrings {
+		if eipNum, err := strconv.Atoi(eip); err != nil {
+			return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum)
+		} else {
+			eips = append(eips, eipNum)
+		}
+	}
+	return baseConfig, eips, nil
+}
+
 // Subtests returns all valid subtests of the test.
 func (t *StateTest) Subtests() []StateSubtest {
 	var sub []StateSubtest
@@ -122,10 +146,11 @@ func (t *StateTest) Subtests() []StateSubtest {
 
 // Run executes a specific subtest.
 func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
-	config, ok := Forks[subtest.Fork]
-	if !ok {
+	config, eips, err := getVMConfig(subtest.Fork)
+	if err != nil {
 		return nil, UnsupportedForkError{subtest.Fork}
 	}
+	vmconfig.ExtraEips = eips
 	block := t.genesis(config).ToBlock(nil)
 	statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre)