fix ton of bugs for poc-7 compatibility

This commit is contained in:
romanman 2014-12-24 15:55:36 +02:00
parent aad53ba2c5
commit b1c82cb59e
20 changed files with 241 additions and 146 deletions

View File

@ -16,6 +16,12 @@
(*) if [artifact.snapshot] is set the deploy will add timestamp to the package
delete api with bash:
curl -X DELETE https://api.bintray.com/packages/ethereum/maven/org.ethereum/versions/0.7.14.20141224.1153 &
-->
@ -26,7 +32,7 @@
<property name="artifact.groupId" value="org.ethereum"/>
<property name="artifact.id" value="ethereumj"/>
<property name="artifact.version" value="0.7.9"/>
<property name="artifact.version" value="0.7.14"/>
<property name="artifact.path" value="" />
<property name="artifact.snapshot" value="snapshot" />
@ -144,7 +150,7 @@
<echo message="${artifact.full.path}/${artifact.id}-${artifact.version}${artifact.suffix}"/>
<http url="https://api.bintray.com/content/${bintray.subject}/${bintray.repo}/${bintray.package}/${bintray.version}/${encoded.bintray.file.path}${artifact.suffix};publish=1"
<http url="https://api.bintray.com/content/${bintray.subject}/${bintray.repo}/${bintray.package}/${bintray.version}/${encoded.bintray.file.path}${artifact.suffix};publish=1;override=1"
method="PUT"
printrequest="false"
printrequestheaders="true"

View File

@ -4,7 +4,7 @@
<groupId>org.ethereum</groupId>
<artifactId>ethereumj</artifactId>
<packaging>jar</packaging>
<version>0.7.9</version>
<version>0.7.14</version>
<name>EthereumJ</name>
<url>http://www.ethereumj.org</url>

View File

@ -145,6 +145,12 @@ public class BlockchainImpl implements Blockchain {
block.getNumber());
if (blockStore.getBlockByHash(block.getHash()) != null){
if (logger.isDebugEnabled())
logger.debug("Block already exist hash: {}, number: {}",
Hex.toHexString(block.getHash()).substring(0, 6),
block.getNumber());
// retry of well known block
return;
}
@ -318,7 +324,7 @@ public class BlockchainImpl implements Blockchain {
stateLogger.info("apply block: [{}] tx: [{}] ", block.getNumber(), i);
TransactionExecutor executor = new TransactionExecutor(tx, block.getCoinbase(), track,
programInvokeFactory, bestBlock);
programInvokeFactory, block);
executor.execute();
TransactionReceipt receipt = executor.getReceipt();
@ -385,7 +391,7 @@ public class BlockchainImpl implements Blockchain {
String worldStateRootHash = Hex.toHexString(repository.getRoot());
if(!blockStateRootHash.equals(worldStateRootHash)){
stateLogger.warn("BLOCK: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
stateLogger.info("BLOCK: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
adminInfo.lostConsensus();
// in case of rollback hard move the root

View File

@ -170,6 +170,25 @@ public class Transaction {
return gasLimit;
}
public long nonZeroDataBytes(){
if (data == null) return 0;
int counter = 0;
for (int i = 0; i < data.length; ++i){
if (data[i] != 0) ++counter;
}
return counter;
}
public long zeroDataBytes(){
if (data == null) return 0;
int counter = 0;
for (int i = 0; i < data.length; ++i){
if (data[i] == 0) ++counter;
}
return counter;
}
public byte[] getData() {
if (!parsed) rlpParse();
return data;

View File

@ -31,21 +31,31 @@ public class TransactionExecutor {
private TransactionReceipt receipt;
private ProgramResult result;
private Block bestBlock;
private Block currentBlock;
public TransactionExecutor(Transaction tx, byte[] coinbase, Repository track,
ProgramInvokeFactory programInvokeFactory, Block bestBlock) {
ProgramInvokeFactory programInvokeFactory, Block currentBlock) {
this.tx = tx;
this.coinbase = coinbase;
this.track = track;
this.programInvokeFactory = programInvokeFactory;
this.bestBlock = bestBlock;
this.currentBlock = currentBlock;
}
/* jeff:
execution happens like this:
create account, transfer value (if any), create a snapshot,
run INIT code, if err, rollback to snapshot, if success set return value.
*/
// https://github.com/ethereum/cpp-ethereum/blob/develop/libethereum/Executive.cpp#L55
public void execute(){
logger.info("applyTransaction: [{}]", Hex.toHexString(tx.getHash()));
TransactionReceipt receipt = new TransactionReceipt();
@ -70,17 +80,20 @@ public class TransactionExecutor {
// FIND OUT THE TRANSACTION TYPE
byte[] receiverAddress, code = null;
byte[] receiverAddress = null;
byte[] code = EMPTY_BYTE_ARRAY;
boolean isContractCreation = tx.isContractCreation();
if (isContractCreation) {
receiverAddress = tx.getContractAddress();
code = tx.getData(); // init code
// on CREATE the contract is created event if it will rollback
track.addBalance(receiverAddress, BigInteger.ZERO);
} else {
receiverAddress = tx.getReceiveAddress();
code = track.getCode(receiverAddress);
// on invocation the contract is created event if doesn't exist.
track.addBalance(receiverAddress, BigInteger.ZERO);
if (code != EMPTY_BYTE_ARRAY) {
if (stateLogger.isDebugEnabled())
stateLogger.debug("calling for existing contract: address={}",
@ -110,7 +123,6 @@ public class TransactionExecutor {
// THE SIMPLE VALUE/BALANCE CHANGE
BigInteger txValue = new BigInteger(1, tx.getValue());
if (tx.isValueTx()) {
if (track.getBalance(senderAddress).compareTo(txValue) >= 0) {
track.addBalance(receiverAddress, txValue); // balance will be read again below
@ -123,12 +135,11 @@ public class TransactionExecutor {
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
}
}
// UPDATE THE NONCE
track.increaseNonce(senderAddress);
logger.info("increased tx.nonce to: [{}]", track.getNonce(senderAddress));
logger.info("increased nonce to: [{}], addr: [{}]", track.getNonce(senderAddress), Hex.toHexString(senderAddress));
// CHARGE FOR GAS
track.addBalance(senderAddress, gasDebit.negate());
@ -162,7 +173,7 @@ public class TransactionExecutor {
}
ProgramInvoke programInvoke =
programInvokeFactory.createProgramInvoke(tx, bestBlock, trackTx);
programInvokeFactory.createProgramInvoke(tx, currentBlock, trackTx);
VM vm = new VM();
Program program = new Program(code, programInvoke);
@ -174,7 +185,7 @@ public class TransactionExecutor {
result = program.getResult();
applyProgramResult(result, gasDebit, gasPrice, trackTx,
senderAddress, receiverAddress, coinbase, isContractCreation);
gasUsed = result.getGasUsed();
List<LogInfo> logs = result.getLogInfoList();
receipt.setLogInfoList(logs);
@ -188,7 +199,9 @@ public class TransactionExecutor {
trackTx.commit();
} else {
// REFUND GASDEBIT EXCEPT FOR FEE (500 + 5*TX_NO_ZERO_DATA)
long dataCost = tx.getData() == null ? 0: tx.getData().length * GasCost.TX_NO_ZERO_DATA;
long dataCost = tx.getData() == null ? 0:
tx.nonZeroDataBytes() * GasCost.TX_NO_ZERO_DATA +
tx.zeroDataBytes() * GasCost.TX_ZERO_DATA;
gasUsed = GasCost.TRANSACTION + dataCost;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(gasUsed).multiply(gasPrice));
@ -270,6 +283,18 @@ public class TransactionExecutor {
.debug("saving code of the contract to the db:\n contract={} code={}",
Hex.toHexString(contractAddress),
Hex.toHexString(bodyCode));
BigInteger storageCost = gasPrice.multiply( BigInteger.valueOf( bodyCode.length * GasCost.CREATE_DATA_BYTE) );
BigInteger balance = repository.getBalance(senderAddress);
// check if can be charged for the contract data save
if (storageCost.compareTo(balance) > 1){
bodyCode = EMPTY_BYTE_ARRAY;
} else {
repository.addBalance(coinbase, storageCost);
repository.addBalance(senderAddress, storageCost.negate());
}
repository.saveCode(contractAddress, bodyCode);
}
}

View File

@ -72,8 +72,10 @@ public class ContractDetails {
return null;
int foundIndex = storageKeys.indexOf(key);
if (foundIndex != -1)
return storageValues.get(foundIndex);
if (foundIndex != -1) {
DataWord value = storageValues.get(foundIndex);
return value.clone();
}
else
return null;
}

View File

@ -281,6 +281,12 @@ public class RepositoryDummy implements Repository {
return accountState;
}
@Override
public boolean isExist(byte[] addr) {
return getAccountState(addr) != null;
}
@Override
public byte[] getRoot() {
throw new UnsupportedOperationException();

View File

@ -409,6 +409,11 @@ public class RepositoryImpl implements Repository {
return accountState;
}
@Override
public boolean isExist(byte[] addr) {
return getAccountState(addr) != null;
}
@Override
public void loadAccount(byte[] addr,
HashMap<ByteArrayWrapper, AccountState> cacheAccounts,

View File

@ -66,6 +66,22 @@ public class RepositoryTrack implements Repository {
return accountState;
}
@Override
public boolean isExist(byte[] addr) {
boolean exist = false;
AccountState accountState = cacheAccounts.get(wrap(addr));
if (accountState != null && !accountState.isDeleted()) exist = true;
if (accountState == null){
accountState = repository.getAccountState(addr);
if (accountState != null && !accountState.isDeleted()) exist = true;
}
return exist;
}
@Override
public ContractDetails getContractDetails(byte[] addr) {

View File

@ -27,6 +27,14 @@ public interface Repository {
*/
public AccountState createAccount(byte[] addr);
/**
* @param addr - account to check
* @return - true if account exist,
* false otherwise
*/
public boolean isExist(byte[] addr);
/**
* Retrieve an account
*

View File

@ -488,7 +488,7 @@ public class TestRunner {
return results;
} finally {
repository.close();
// repository.close();
}
}

View File

@ -209,8 +209,12 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
BlockQueue chainQueue = blockchain.getQueue();
BigInteger peerTotalDifficulty = new BigInteger(1, msg.getTotalDifficulty());
BigInteger highestKnownTotalDifficulty = blockchain.getTotalDifficulty();
if ( highestKnownTotalDifficulty == null ||
peerTotalDifficulty.compareTo(highestKnownTotalDifficulty) > 0) {
boolean synced=
FastByteComparisons.compareTo(msg.getBestHash(), 0, 32, blockchain.getBestBlockHash(), 0, 32) == 0;
if ( !synced && (highestKnownTotalDifficulty == null ||
peerTotalDifficulty.compareTo(highestKnownTotalDifficulty) > 0)) {
logger.info(" Their chain is better: total difficulty : {} vs {}",
peerTotalDifficulty.toString(),

View File

@ -30,6 +30,8 @@ public class GasCost {
public static int REFUND_SSTORE = 100;
/** Cost 100 gas */
public static int CREATE = 100;
/** Cost 1 gas */
public static int CREATE_DATA_BYTE = 5;
/** Cost 20 gas */
public static int CALL = 20;
/** Cost 1 gas */

View File

@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
* www.ethereumJ.com
@ -57,7 +58,7 @@ public class Program {
public Program(byte[] ops, ProgramInvoke invokeData) {
if (ops == null) ops = ByteUtil.EMPTY_BYTE_ARRAY;
if (ops == null) ops = EMPTY_BYTE_ARRAY;
this.ops = ops;
if (invokeData != null) {
@ -358,7 +359,17 @@ public class Program {
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = result.getHReturn().array();
long storageCost = code.length * GasCost.CREATE_DATA_BYTE;
long afterSpend = invokeData.getGas().longValue() - storageCost - result.getGasUsed();
if (afterSpend < 0){
track.saveCode(newAddress, EMPTY_BYTE_ARRAY);
} else {
result.spendGas(code.length * GasCost.CREATE_DATA_BYTE);
track.saveCode(newAddress, code);
}
track.commit();
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
@ -529,7 +540,11 @@ public class Program {
}
public byte[] getCodeAt(DataWord address) {
return invokeData.getRepository().getCode(address.getLast20Bytes());
byte[] code = invokeData.getRepository().getCode(address.getLast20Bytes());
if (code == null) code = ByteUtil.EMPTY_BYTE_ARRAY;
return code;
}
public DataWord getOwnerAddress() {
@ -583,7 +598,7 @@ public class Program {
}
public byte[] getDataCopy(DataWord offset, DataWord length) {
if (invokeData == null) return ByteUtil.EMPTY_BYTE_ARRAY;
if (invokeData == null) return EMPTY_BYTE_ARRAY;
return invokeData.getDataCopy(offset, length);
}
@ -743,6 +758,8 @@ public class Program {
public void saveOpTrace(){
if (pc >= ops.length) return;
Op op = new Op();
op.setPc(pc);
@ -762,7 +779,7 @@ public class Program {
if (!CONFIG.vmTrace()) return;
String dir = CONFIG.vmTraceDir() + "/";
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName + ".json");
FileWriter fw = null;

View File

@ -176,10 +176,10 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
Hex.toHexString(address.getLast20Bytes()),
Hex.toHexString(origin.getLast20Bytes()),
Hex.toHexString(caller.getLast20Bytes()),
balance.longValue(),
balance.toString(),
gasPrice.longValue(),
gas.longValue(),
callValue.longValue(),
callValue.toString(),
data == null ? "": Hex.toHexString(data),
Hex.toHexString(lastHash.getData()),
Hex.toHexString(coinbase.getLast20Bytes()),

View File

@ -5,6 +5,7 @@ import org.ethereum.db.ContractDetails;
import static org.ethereum.config.SystemProperties.CONFIG;
import org.ethereum.util.ByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.BigIntegers;
@ -548,7 +549,7 @@ public class VM {
if (logger.isInfoEnabled())
hint = "address: "
+ Hex.toHexString(address.getLast20Bytes())
+ " balance: " + balance.longValue();
+ " balance: " + balance.toString();
program.stackPush(balance);
program.step();
@ -630,39 +631,34 @@ public class VM {
program.step();
} break;
case CODECOPY: case EXTCODECOPY: {
byte[] fullCode;
byte[] fullCode = ByteUtil.EMPTY_BYTE_ARRAY;
if (op == OpCode.CODECOPY)
fullCode = program.getCode();
else {
if (op == OpCode.EXTCODECOPY) {
DataWord address = program.stackPop();
fullCode = program.getCodeAt(address);
}
DataWord memOffsetData = program.stackPop();
BigInteger codeOffsetData = program.stackPop().value();
BigInteger lengthData = program.stackPop().value();
int memOffset = program.stackPop().intValue();
int codeOffset = program.stackPop().intValue();
int lengthData = program.stackPop().intValue();
/*
todo: find out what to do where params are exits the actual code
if (fullCode == null ||
BigInteger.valueOf(fullCode.length).
compareTo(
codeOffsetData.add(lengthData)) > 0) {
program.stop();
break;
}
*/
int sizeToBeCopied =
codeOffset + lengthData > fullCode.length ?
(fullCode.length < codeOffset ? 0 : fullCode.length - codeOffset)
: lengthData;
int length = lengthData.intValue();
int codeOffset = codeOffsetData.intValue();
byte[] codeCopy = new byte[lengthData];
byte[] codeCopy = new byte[fullCode.length - codeOffset];
System.arraycopy(fullCode, codeOffset, codeCopy, 0, fullCode.length - codeOffset);
if (codeOffset < fullCode.length)
System.arraycopy(fullCode, codeOffset, codeCopy, 0, sizeToBeCopied);
if (logger.isInfoEnabled())
hint = "code: " + Hex.toHexString(codeCopy);
program.memorySave(memOffsetData.intValue(), codeCopy);
program.memorySave(memOffset, codeCopy);
program.step();
} break;
case GASPRICE:{
@ -969,12 +965,7 @@ public class VM {
DataWord size = program.stackPop();
ByteBuffer hReturn = program.memoryChunk(offset, size);
if (hReturn.array().length * 5 <= program.getGas().longValue()){
program.spendGas(hReturn.array().length * 5, op.name());
program.setHReturn(hReturn);
} else {
program.setHReturn(ByteBuffer.wrap(new byte[]{}));
}
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(hReturn.array())

View File

@ -130,7 +130,7 @@ max.blocks.ask = 120
max.blocks.queued = 3000
# project version auto copied during build phase
project.version = 0.7.9
project.version = 0.7.14
# hello phrase will be included in
# the hello message of the peer

View File

@ -3,6 +3,7 @@ package test.ethereum.jsontestsuite;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.ethereum.jsontestsuite.*;
import org.json.simple.JSONObject;
@ -78,6 +79,40 @@ public class GitHubJSONTestSuite {
Assert.assertTrue(result.isEmpty());
}
protected static void runGitHubJsonStateTest(String json, Set<String> exclude) throws ParseException {
Assume.assumeFalse("Online test is not available", json.equals(""));
JSONParser parser = new JSONParser();
JSONObject testSuiteObj = (JSONObject)parser.parse(json);
StateTestSuite testSuite = new StateTestSuite(testSuiteObj);
Collection<StateTestCase> testCollection = testSuite.getAllTests();
for(StateTestCase testCase : testSuite.getAllTests()){
String prefix = " ";
if (exclude.contains(testCase.getName())) prefix = "[X] ";
logger.info(prefix + testCase.getName());
}
for (StateTestCase testCase : testCollection){
if (exclude.contains(testCase.getName())) continue;
TestRunner runner = new TestRunner();
List<String> result = runner.runTestCase(testCase);
if (!result.isEmpty()){
for (String single : result){
logger.info(single);
}
}
Assert.assertTrue(result.isEmpty());
}
}
protected static void runGitHubJsonStateTest(String json) throws ParseException {
Assume.assumeFalse("Online test is not available", json.equals(""));

View File

@ -6,6 +6,9 @@ import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import java.util.HashSet;
import java.util.Set;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class GitHubStateTest {
@ -13,11 +16,22 @@ public class GitHubStateTest {
public void stSingleTest() throws ParseException {
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
GitHubJSONTestSuite.runGitHubJsonStateTest(json, "CallRecursiveBombLog");
GitHubJSONTestSuite.runGitHubJsonStateTest(json, "createNameRegistrator");
}
@Test
public void stExample() throws ParseException {
public void runWithExcludedTest() throws ParseException {
Set<String> excluded = new HashSet<>();
excluded.add("createNameRegistratorValueTooHigh");
String json = JSONReader.loadJSON("StateTests/stSystemOperationsTest.json");
GitHubJSONTestSuite.runGitHubJsonStateTest(json, excluded);
}
@Test
public void stExample() throws ParseException { // [V]
String json = JSONReader.loadJSON("StateTests/stExample.json");
GitHubJSONTestSuite.runGitHubJsonStateTest(json);
@ -60,7 +74,7 @@ public class GitHubStateTest {
@Test
public void stSpecialTest() throws ParseException {
public void stSpecialTest() throws ParseException { // [V]
String json = JSONReader.loadJSON("StateTests/stSpecialTest.json");
GitHubJSONTestSuite.runGitHubJsonStateTest(json);

View File

@ -1,5 +1,6 @@
package test.ethereum.vm;
import junit.framework.Assert;
import org.ethereum.facade.Repository;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.*;
@ -2482,53 +2483,6 @@ public class VMTest {
assertTrue(program.isStopped());
}
@Test // RETURN OP
public void testRETURN_5() {
invoke.setGas(300);
VM vm = new VM();
program =
new Program(Hex.decode("7FA0B0C0D0E0F0A1B1C1D1E1F1A2B2C2D2E2F2A3B3C3D3E3F3A4B4C4D4E4F4A1B160005260206010F3"),
invoke);
String s_expected_1 = "E2F2A3B3C3D3E3F3A4B4C4D4E4F4A1B100000000000000000000000000000000";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
assertEquals(s_expected_1, Hex.toHexString(program.getResult().getHReturn().array()).toUpperCase());
assertEquals(132, program.getGas().longValue());
assertTrue(program.isStopped());
}
@Test // RETURN OP
public void testRETURN_6() {
invoke.setGas(100);
VM vm = new VM();
program =
new Program(Hex.decode("7FA0B0C0D0E0F0A1B1C1D1E1F1A2B2C2D2E2F2A3B3C3D3E3F3A4B4C4D4E4F4A1B160005260206010F3"),
invoke);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
assertArrayEquals("".getBytes(), program.getResult().getHReturn().array());
assertEquals(92, program.getGas().longValue());
assertTrue(program.isStopped());
}
@Test // CODECOPY OP
public void testCODECOPY_1() {
@ -2570,6 +2524,10 @@ public class VMTest {
@Test // CODECOPY OP
public void testCODECOPY_3() {
// cost for that:
// 94 - data copied
// 95 - new bytes allocated
VM vm = new VM();
program =
new Program(Hex.decode("605E60076000396000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235"),
@ -2580,7 +2538,7 @@ public class VMTest {
vm.step(program);
vm.step(program);
assertTrue(program.isStopped());
assertEquals( 10, program.getResult().getGasUsed() );
}
@Test // CODECOPY OP
@ -2596,13 +2554,9 @@ public class VMTest {
vm.step(program);
vm.step(program);
assertTrue(program.isStopped());
assertEquals( 10, program.getResult().getGasUsed() );
}
// DataWord memOffsetData
// DataWord codeOffsetData
// DataWord lengthData
@Test // CODECOPY OP
public void testCODECOPY_5() {
@ -2685,34 +2639,19 @@ public class VMTest {
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e75660005460005360200235"),
invoke);
String m_expected_1 = "6000605F556014600054601E60205463ABCDDCBA6040545B51602001600A5254516040016014525451606001601E5254516080016028525460A052546016604860003960166000F26000603F556103E756600054600053602002350000000000";
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
assertTrue(program.isStopped());
assertEquals(m_expected_1, Hex.toHexString(program.getMemory().array()).toUpperCase());
}
@Test // EXTCODECOPY OP
public void testEXTCODECOPY_4() {
VM vm = new VM();
program =
new Program(Hex.decode("605E6007600073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
invoke);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
vm.step(program);
assertTrue(program.isStopped());
}
@Test // EXTCODECOPY OP
public void testEXTCODECOPY_5() {
VM vm = new VM();
program =
new Program(Hex.decode("611234600054615566602054603E6000602073471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C6000605f556014600054601e60205463abcddcba6040545b51602001600a5254516040016014525451606001601e5254516080016028525460a052546016604860003960166000f26000603f556103e756600054600053602002351234"),
@ -2735,7 +2674,7 @@ public class VMTest {
@Test(expected=StackTooSmallException.class) // EXTCODECOPY OP mal
public void testEXTCODECOPY_6() {
public void testEXTCODECOPY_5() {
VM vm = new VM();
program =
new Program(Hex.decode("605E600773471FD3AD3E9EEADEEC4608B92D16CE6B500704CC3C"),