diff --git a/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java b/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java index ce4d2d567cf..c225159292e 100644 --- a/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java +++ b/rskj-core/src/main/java/co/rsk/db/ContractDetailsImpl.java @@ -19,7 +19,6 @@ package co.rsk.db; import co.rsk.crypto.Keccak256; -import co.rsk.panic.PanicProcessor; import co.rsk.trie.*; import com.google.common.annotations.VisibleForTesting; import org.ethereum.crypto.Keccak256Helper; @@ -47,7 +46,6 @@ * Created by ajlopez on 05/04/2017. */ public class ContractDetailsImpl implements ContractDetails { - private static final PanicProcessor panicProcessor = new PanicProcessor(); private static final Logger logger = LoggerFactory.getLogger("contractdetails"); private Trie trie; diff --git a/rskj-core/src/main/java/co/rsk/db/ContractStorageStoreFactory.java b/rskj-core/src/main/java/co/rsk/db/ContractStorageStoreFactory.java index e2318e4abdd..6db14c18309 100644 --- a/rskj-core/src/main/java/co/rsk/db/ContractStorageStoreFactory.java +++ b/rskj-core/src/main/java/co/rsk/db/ContractStorageStoreFactory.java @@ -66,11 +66,6 @@ private static boolean addressIsDedicated(byte[] address) { RskAddress addr = new RskAddress(address); - if (addr.equals(PrecompiledContracts.REMASC_ADDR) || - addr.equals(PrecompiledContracts.BRIDGE_ADDR)) { - return true; - } - - return false; + return addr.equals(PrecompiledContracts.REMASC_ADDR) || addr.equals(PrecompiledContracts.BRIDGE_ADDR); } } diff --git a/rskj-core/src/main/java/org/ethereum/config/BlockchainConfig.java b/rskj-core/src/main/java/org/ethereum/config/BlockchainConfig.java index 638529d0067..3644a4929e0 100644 --- a/rskj-core/src/main/java/org/ethereum/config/BlockchainConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/BlockchainConfig.java @@ -51,6 +51,8 @@ public interface BlockchainConfig { boolean isRskip91(); + boolean isRskip103(); + boolean isRskip87(); boolean isRskip92(); diff --git a/rskj-core/src/main/java/org/ethereum/config/CommonConfig.java b/rskj-core/src/main/java/org/ethereum/config/CommonConfig.java index 86d78fc6d9a..6581b49720b 100644 --- a/rskj-core/src/main/java/org/ethereum/config/CommonConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/CommonConfig.java @@ -41,7 +41,6 @@ import java.util.List; import static java.util.Arrays.asList; -import static org.ethereum.datasource.DataSourcePool.levelDbByName; @Configuration @ComponentScan( diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java index 375076041ed..f1840cd5cf6 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/AbstractConfig.java @@ -143,6 +143,9 @@ public boolean isRskip88() { @Override public boolean isRskip91() { return false; } + @Override + public boolean isRskip103() { return false; } + @Override public boolean isRskip87() { return false; } diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/HardForkActivationConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/HardForkActivationConfig.java index ff5e787f727..2bee0daec4c 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/HardForkActivationConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/HardForkActivationConfig.java @@ -21,20 +21,34 @@ public class HardForkActivationConfig { private final int orchidActivationHeight; + private final int orchid060ActivationHeight; private static final String PROPERTY_ORCHID_NAME = "orchid"; + private static final String PROPERTY_ORCHID_060_NAME = "orchid060"; public HardForkActivationConfig(Config config) { // If I don't have any config for orchidActivationHeight I will set it to 0 - this(config.hasPath(PROPERTY_ORCHID_NAME) ? config.getInt(PROPERTY_ORCHID_NAME) : 0); + this( + config.hasPath(PROPERTY_ORCHID_NAME) ? config.getInt(PROPERTY_ORCHID_NAME) : 0, + config.hasPath(PROPERTY_ORCHID_060_NAME) ? config.getInt(PROPERTY_ORCHID_060_NAME) : 0 + ); } - public HardForkActivationConfig(int orchidActivationHeight) { + public HardForkActivationConfig(int orchidActivationHeight, int orchid060ActivationHeight) { this.orchidActivationHeight = orchidActivationHeight; + this.orchid060ActivationHeight = orchid060ActivationHeight; } public int getOrchidActivationHeight() { return orchidActivationHeight; } + /** + * TODO(mc): Only Devnet knows about Orchid060 activation config. + * This is a quick solution but the whole HF activation needs work. + * E.g. we don't handle the case where Orchid060 < Orchid (fails at runtime). + */ + public int getOrchid060ActivationHeight() { + return orchid060ActivationHeight; + } } diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/devnet/DevNetOrchid060Config.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/devnet/DevNetOrchid060Config.java new file mode 100644 index 00000000000..32c6c86b1f7 --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/devnet/DevNetOrchid060Config.java @@ -0,0 +1,27 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.ethereum.config.blockchain.devnet; + +public class DevNetOrchid060Config extends DevNetOrchidConfig { + @Override + public boolean isRskip103() { + return true; + } +} diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/mainnet/MainNetOrchid060Config.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/mainnet/MainNetOrchid060Config.java new file mode 100644 index 00000000000..2bd7ef13636 --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/mainnet/MainNetOrchid060Config.java @@ -0,0 +1,27 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.ethereum.config.blockchain.mainnet; + +public class MainNetOrchid060Config extends MainNetOrchidConfig { + @Override + public boolean isRskip103() { + return true; + } +} diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/regtest/RegTestOrchidConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/regtest/RegTestOrchidConfig.java index df3fe0a76b5..9f7fcbd43ef 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/regtest/RegTestOrchidConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/regtest/RegTestOrchidConfig.java @@ -44,6 +44,11 @@ public boolean isRskip91() { return true; } + @Override + public boolean isRskip103() { + return true; + } + @Override public boolean isRskip87() { return true; } diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/testnet/TestNetOrchid060Config.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/testnet/TestNetOrchid060Config.java new file mode 100644 index 00000000000..ae2dbea31b5 --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/testnet/TestNetOrchid060Config.java @@ -0,0 +1,28 @@ +/* + * This file is part of RskJ + * Copyright (C) 2018 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.ethereum.config.blockchain.testnet; + +public class TestNetOrchid060Config extends TestNetDifficultyDropEnabledConfig { + + @Override + public boolean isRskip103() { + return true; + } +} diff --git a/rskj-core/src/main/java/org/ethereum/config/net/DevNetConfig.java b/rskj-core/src/main/java/org/ethereum/config/net/DevNetConfig.java index 77fd62db030..297e8499b5a 100644 --- a/rskj-core/src/main/java/org/ethereum/config/net/DevNetConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/net/DevNetConfig.java @@ -20,8 +20,9 @@ package org.ethereum.config.net; import org.ethereum.config.blockchain.HardForkActivationConfig; -import org.ethereum.config.blockchain.devnet.DevNetOrchidConfig; import org.ethereum.config.blockchain.devnet.DevNetGenesisConfig; +import org.ethereum.config.blockchain.devnet.DevNetOrchid060Config; +import org.ethereum.config.blockchain.devnet.DevNetOrchidConfig; /** @@ -45,11 +46,15 @@ public static DevNetConfig getFromConfig(HardForkActivationConfig hardForkActiva return getDefaultDevNetConfig(); } DevNetConfig customConfig = new DevNetConfig(); - if (hardForkActivationConfig.getOrchidActivationHeight() != 0) { - // Only add genesis config if the fork configs are set - customConfig.add(0, new DevNetGenesisConfig()); + if (hardForkActivationConfig.getOrchid060ActivationHeight() != 0) { + if (hardForkActivationConfig.getOrchidActivationHeight() != 0) { + customConfig.add(0, new DevNetGenesisConfig()); + } + + customConfig.add(hardForkActivationConfig.getOrchidActivationHeight(), new DevNetOrchidConfig()); } - customConfig.add(hardForkActivationConfig.getOrchidActivationHeight(), new DevNetOrchidConfig()); + + customConfig.add(hardForkActivationConfig.getOrchid060ActivationHeight(), new DevNetOrchid060Config()); return customConfig; } } diff --git a/rskj-core/src/main/java/org/ethereum/config/net/MainNetConfig.java b/rskj-core/src/main/java/org/ethereum/config/net/MainNetConfig.java index adcdda69f0c..38ad0d56f5a 100644 --- a/rskj-core/src/main/java/org/ethereum/config/net/MainNetConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/net/MainNetConfig.java @@ -21,6 +21,7 @@ import org.ethereum.config.blockchain.mainnet.MainNetAfterBridgeSyncConfig; import org.ethereum.config.blockchain.mainnet.MainNetBeforeBridgeSyncConfig; +import org.ethereum.config.blockchain.mainnet.MainNetOrchid060Config; import org.ethereum.config.blockchain.mainnet.MainNetOrchidConfig; @@ -34,5 +35,6 @@ public MainNetConfig() { // On blockchain launch blocks will be faster until difficulty is adjusted to available hashing power. add(370_000, new MainNetAfterBridgeSyncConfig()); add(729_000, new MainNetOrchidConfig()); + add(1_052_700, new MainNetOrchid060Config()); } } diff --git a/rskj-core/src/main/java/org/ethereum/config/net/TestNetConfig.java b/rskj-core/src/main/java/org/ethereum/config/net/TestNetConfig.java index 386ae1d12cc..44b7ece6e7a 100644 --- a/rskj-core/src/main/java/org/ethereum/config/net/TestNetConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/net/TestNetConfig.java @@ -22,6 +22,7 @@ import org.ethereum.config.blockchain.testnet.TestNetBeforeBridgeSyncConfig; import org.ethereum.config.blockchain.testnet.TestNetDifficultyDropEnabledConfig; +import org.ethereum.config.blockchain.testnet.TestNetOrchid060Config; public class TestNetConfig extends AbstractNetConfig { public static final TestNetConfig INSTANCE = new TestNetConfig(); @@ -31,5 +32,7 @@ public TestNetConfig() { // 21 days of 1 block every 14 seconds. // On blockchain launch blocks will be faster until difficulty is adjusted to available hashing power. add(114_000, new TestNetDifficultyDropEnabledConfig()); + // Static call fix. + add(233_000, new TestNetOrchid060Config()); } } diff --git a/rskj-core/src/main/java/org/ethereum/datasource/DataSourcePool.java b/rskj-core/src/main/java/org/ethereum/datasource/DataSourcePool.java index 13fdbd80a43..dae4f4a4808 100644 --- a/rskj-core/src/main/java/org/ethereum/datasource/DataSourcePool.java +++ b/rskj-core/src/main/java/org/ethereum/datasource/DataSourcePool.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.ConcurrentHashMap; diff --git a/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java b/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java index 71ae0bf5d43..88195b8c36b 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/LogFilter.java @@ -23,9 +23,7 @@ import org.ethereum.db.TransactionInfo; import org.ethereum.vm.LogInfo; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import static org.ethereum.rpc.TypeConverter.stringHexToByteArray; diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index 4787ebb57df..8c34bb643f2 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -21,7 +21,6 @@ import co.rsk.config.VmConfig; import co.rsk.core.RskAddress; -import org.ethereum.core.Blockchain; import org.ethereum.crypto.HashUtil; import org.ethereum.config.BlockchainConfig; import org.ethereum.db.ContractDetails; @@ -1411,8 +1410,17 @@ protected void doCALL(){ DataWord gas = program.stackPop(); DataWord codeAddress = program.stackPop(); - // value is always zero in a DELEGATECALL operation - DataWord value = op.equals(OpCode.DELEGATECALL) ? DataWord.ZERO : program.stackPop(); + DataWord value; + + BlockchainConfig config = program.getBlockchainConfig(); + + if (config.isRskip103()) { + // value is always zero in a DELEGATECALL or STATICCALL operation + value = op == OpCode.DELEGATECALL || op == OpCode.STATICCALL ? DataWord.ZERO : program.stackPop(); + } else { + // value is always zero in a DELEGATECALL operation + value = op == OpCode.DELEGATECALL ? DataWord.ZERO : program.stackPop(); + } if (program.isStaticCall() && op == CALL && !value.isZero()) { throw Program.ExceptionHelper.modificationException(); @@ -1484,8 +1492,14 @@ protected void doCALL(){ program.disposeWord(outDataSize); program.disposeWord(codeAddress); program.disposeWord(gas); - if (!op.equals(OpCode.DELEGATECALL)) { - program.disposeWord(value); + if (config.isRskip103()) { + if (op != OpCode.DELEGATECALL && op != OpCode.STATICCALL) { + program.disposeWord(value); + } + } else { + if (!op.equals(OpCode.DELEGATECALL)) { + program.disposeWord(value); + } } program.step(); @@ -1515,7 +1529,8 @@ private long computeCallGas(DataWord codeAddress, callGas += GasCost.NEW_ACCT_CALL; } - if (op != OpCode.DELEGATECALL && !value.isZero()) { + // RSKIP103: we don't need to check static call nor delegate call since value will always be zero + if (!value.isZero()) { callGas += GasCost.VT_CALL; } diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index c8a0964b432..5b3345f2957 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -378,11 +378,16 @@ public void stackPush(DataWord stackWord) { } public void disposeWord(DataWord dw) { - if (dataWordPool==null) { + if (dataWordPool == null) { return ; } + + if (dw == DataWord.ZERO || dw == DataWord.ONE) { + throw new IllegalArgumentException("Can't dispose a global DataWord"); + } + // If there are enough cached values, just really dispose - if (dataWordPool.size()<1024) { + if (dataWordPool.size() < 1024) { dataWordPool.push(dw); } } diff --git a/rskj-core/src/test/java/co/rsk/mine/ParameterizedNetworkUpgradeTest.java b/rskj-core/src/test/java/co/rsk/mine/ParameterizedNetworkUpgradeTest.java index e8db75fae03..02b759034af 100644 --- a/rskj-core/src/test/java/co/rsk/mine/ParameterizedNetworkUpgradeTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/ParameterizedNetworkUpgradeTest.java @@ -42,7 +42,7 @@ public static Object[] data() { TestSystemProperties bambooConfig = new TestSystemProperties() { @Override protected BlockchainNetConfig buildBlockchainConfig() { - return RegTestConfig.getFromConfig(new HardForkActivationConfig(Integer.MAX_VALUE)); + return RegTestConfig.getFromConfig(new HardForkActivationConfig(Integer.MAX_VALUE, Integer.MAX_VALUE)); } @Override @@ -53,7 +53,8 @@ public String toString() { TestSystemProperties orchidConfig = new TestSystemProperties() { @Override protected BlockchainNetConfig buildBlockchainConfig() { - return RegTestConfig.getFromConfig(new HardForkActivationConfig(0)); + // this method ignores the orchid060 activation height configuration + return RegTestConfig.getFromConfig(new HardForkActivationConfig(0, Integer.MAX_VALUE)); } @Override diff --git a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java index ffd5fdff2a4..5a06143b621 100644 --- a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -40,6 +40,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.util.EmptyStackException; import java.util.List; import static org.ethereum.util.ByteUtil.oneByteToHexString; @@ -119,12 +120,10 @@ public void testSTATICCALLWithStatusZeroUsingOpCode(String opcode) { @Test public void testSTATICCALLWithStatusOne() { invoke = new ProgramInvokeMockImpl(compile("PUSH1 0x01 PUSH1 0x02 SUB"), null); - RskAddress address = invoke.getContractAddress(); program = getProgram(compile("PUSH1 0x00" + " PUSH1 0x00" + " PUSH1 0x00" + " PUSH1 0x00" + - " PUSH1 0x00" + " PUSH20 0x" + invoke.getContractAddress() + " PUSH4 0x005B8D80" + " STATICCALL")); @@ -133,6 +132,63 @@ public void testSTATICCALLWithStatusOne() { vm.steps(program, Long.MAX_VALUE); assertEquals(DataWord.ONE, program.stackPop()); + assertTrue(program.getStack().isEmpty()); + } + + @Test(expected = EmptyStackException.class) + public void testSTATICCALLWithStatusOneFailsWithOldCode() { + invoke = new ProgramInvokeMockImpl(compile("PUSH1 0x01 PUSH1 0x02 SUB"), null); + program = getProgram(compile("PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH20 0x" + invoke.getContractAddress() + + " PUSH4 0x005B8D80" + + " STATICCALL"), null, true); + + program.fullTrace(); + + vm.steps(program, Long.MAX_VALUE); + } + + @Test + public void testSTATICCALLWithStatusOneAndAdditionalValueInStackUsingPreFixStaticCall() { + invoke = new ProgramInvokeMockImpl(compile("PUSH1 0x01 PUSH1 0x02 SUB"), null); + program = getProgram(compile("PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH20 0x" + invoke.getContractAddress() + + " PUSH4 0x005B8D80" + + " STATICCALL"), null, true); + + program.fullTrace(); + vm.steps(program, Long.MAX_VALUE); + + assertEquals(DataWord.ONE, program.stackPop()); + assertTrue(program.getStack().isEmpty()); + } + + @Test + public void testSTATICCALLWithStatusOneAndAdditionalValueInStackUsingFixStaticCallLeavesValueInStack() { + invoke = new ProgramInvokeMockImpl(compile("PUSH1 0x01 PUSH1 0x02 SUB"), null); + program = getProgram(compile("PUSH1 0x2a" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH20 0x" + invoke.getContractAddress() + + " PUSH4 0x005B8D80" + + " STATICCALL")); + + program.fullTrace(); + vm.steps(program, Long.MAX_VALUE); + + assertEquals(DataWord.ONE, program.stackPop()); + assertFalse(program.getStack().isEmpty()); + assertEquals(1, program.getStack().size()); + assertEquals(new DataWord(42), program.getStack().pop()); } @Test // PUSH1 OP @@ -2986,16 +3042,23 @@ private Program getProgram(byte[] code) { return getProgram(code, null); } - private BlockchainConfig getBlockchainConfig() { + private BlockchainConfig getBlockchainConfig(boolean preFixStaticCall) { BlockchainConfig blockchainConfig = mock(BlockchainConfig.class); when(blockchainConfig.isRskip91()).thenReturn(true); + + when(blockchainConfig.isRskip103()).thenReturn(!preFixStaticCall); + when(blockchainConfig.isRskip90()).thenReturn(true); when(blockchainConfig.isRskip89()).thenReturn(true); return blockchainConfig; } private Program getProgram(byte[] code, Transaction transaction) { - return new Program(vmConfig, precompiledContracts, getBlockchainConfig(), code, invoke, transaction); + return getProgram(code, transaction, false); + } + + private Program getProgram(byte[] code, Transaction transaction, boolean preFixStaticCall) { + return new Program(vmConfig, precompiledContracts, getBlockchainConfig(preFixStaticCall), code, invoke, transaction); } private byte[] compile(String code) {