deleting things we probably need later
This commit is contained in:
parent
adda509526
commit
ed43eb9a0a
|
@ -1,23 +0,0 @@
|
||||||
package org.ethereum;
|
|
||||||
|
|
||||||
import org.ethereum.cli.CLIInterface;
|
|
||||||
import org.ethereum.config.SystemProperties;
|
|
||||||
import org.ethereum.facade.Ethereum;
|
|
||||||
import org.ethereum.facade.EthereumFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 14.11.2014
|
|
||||||
*/
|
|
||||||
public class Start {
|
|
||||||
|
|
||||||
public static void main(String args[]) {
|
|
||||||
CLIInterface.call(args);
|
|
||||||
Ethereum ethereum = EthereumFactory.createEthereum();
|
|
||||||
|
|
||||||
ethereum.connect(SystemProperties.CONFIG.activePeerIP(),
|
|
||||||
SystemProperties.CONFIG.activePeerPort());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package org.ethereum.facade;
|
|
||||||
|
|
||||||
import org.ethereum.net.eth.EthHandler;
|
|
||||||
import org.ethereum.net.shh.ShhHandler;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 13.11.2014
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class EthereumFactory {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("general");
|
|
||||||
public static ApplicationContext context = null;
|
|
||||||
|
|
||||||
public static Ethereum createEthereum() {
|
|
||||||
return createEthereum(DefaultConfig.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Ethereum createEthereum(Class clazz) {
|
|
||||||
|
|
||||||
logger.info("capability eth version: [{}]", EthHandler.VERSION);
|
|
||||||
logger.info("capability shh version: [{}]", ShhHandler.VERSION);
|
|
||||||
|
|
||||||
context = new AnnotationConfigApplicationContext(clazz);
|
|
||||||
return context.getBean(Ethereum.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.db.ContractDetails;
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
import org.ethereum.vm.DataWord;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class AccountState {
|
|
||||||
|
|
||||||
byte[] address;
|
|
||||||
byte[] balance;
|
|
||||||
byte[] code;
|
|
||||||
byte[] nonce;
|
|
||||||
|
|
||||||
Map<DataWord, DataWord> storage = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
public AccountState(byte[] address, JSONObject accountState) {
|
|
||||||
|
|
||||||
this.address = address;
|
|
||||||
String balance = accountState.get("balance").toString();
|
|
||||||
String code = (String) accountState.get("code");
|
|
||||||
String nonce = accountState.get("nonce").toString();
|
|
||||||
|
|
||||||
JSONObject store = (JSONObject) accountState.get("storage");
|
|
||||||
|
|
||||||
this.balance = new BigInteger(balance).toByteArray();
|
|
||||||
|
|
||||||
if (code != null && code.length() > 2)
|
|
||||||
this.code = Hex.decode(code.substring(2));
|
|
||||||
else
|
|
||||||
this.code = ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
this.nonce = new BigInteger(nonce).toByteArray();
|
|
||||||
|
|
||||||
int size = store.keySet().size();
|
|
||||||
Object[] keys = store.keySet().toArray();
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
|
|
||||||
String keyS = keys[i].toString();
|
|
||||||
String valS = store.get(keys[i]).toString();
|
|
||||||
|
|
||||||
byte[] key = Utils.parseData(keyS);
|
|
||||||
byte[] value = Utils.parseData(valS);
|
|
||||||
storage.put(new DataWord(key), new DataWord(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBalance() {
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BigInteger getBigIntegerBalance() {
|
|
||||||
return new BigInteger(balance);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] getCode() {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getNonce() {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getNonceLong() {
|
|
||||||
return new BigInteger(nonce).longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Map<DataWord, DataWord> getStorage() {
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> compareToReal(org.ethereum.core.AccountState state, ContractDetails details) {
|
|
||||||
|
|
||||||
List<String> results = new ArrayList<>();
|
|
||||||
|
|
||||||
BigInteger expectedBalance = new BigInteger(1, this.getBalance());
|
|
||||||
if (!state.getBalance().equals(expectedBalance)) {
|
|
||||||
String formattedString = String.format("Account: %s: has unexpected balance, expected balance: %s found balance: %s",
|
|
||||||
Hex.toHexString(this.address), expectedBalance.toString(), state.getBalance().toString());
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigInteger expectedNonce = new BigInteger(1, this.getNonce());
|
|
||||||
if (!state.getNonce().equals(expectedNonce)) {
|
|
||||||
state.getNonce();
|
|
||||||
this.getNonce();
|
|
||||||
String formattedString = String.format("Account: %s: has unexpected nonce, expected nonce: %s found nonce: %s",
|
|
||||||
Hex.toHexString(this.address), expectedNonce.toString(), state.getNonce().toString());
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(details.getCode(), this.getCode())) {
|
|
||||||
String formattedString = String.format("Account: %s: has unexpected nonce, expected nonce: %s found nonce: %s",
|
|
||||||
Hex.toHexString(this.address), Hex.toHexString(this.getCode()), Hex.toHexString(details.getCode()));
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// compare storage
|
|
||||||
Set<DataWord> keys = details.getStorage().keySet();
|
|
||||||
Set<DataWord> expectedKeys = this.getStorage().keySet();
|
|
||||||
Set<DataWord> checked = new HashSet<>();
|
|
||||||
|
|
||||||
for (DataWord key : keys) {
|
|
||||||
|
|
||||||
DataWord value = details.getStorage().get(key);
|
|
||||||
DataWord expectedValue = this.getStorage().get(key);
|
|
||||||
if (expectedValue == null) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Account: %s: has unexpected storage data: %s = %s",
|
|
||||||
Hex.toHexString(this.address),
|
|
||||||
key.toString(),
|
|
||||||
value.toString());
|
|
||||||
|
|
||||||
results.add(formattedString);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expectedValue.equals(value)) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Account: %s: has unexpected value, for key: %s , expectedValue: %s real value: %s",
|
|
||||||
Hex.toHexString(this.address), key.toString(),
|
|
||||||
expectedValue.toString(), value.toString());
|
|
||||||
results.add(formattedString);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
checked.add(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DataWord key : expectedKeys) {
|
|
||||||
if (!checked.contains(key)) {
|
|
||||||
String formattedString = String.format("Account: %s: doesn't exist expected storage key: %s",
|
|
||||||
Hex.toHexString(this.address), key.toString());
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "AccountState{" +
|
|
||||||
"address=" + Hex.toHexString(address) +
|
|
||||||
", balance=" + Hex.toHexString(balance) +
|
|
||||||
", code=" + Hex.toHexString(code) +
|
|
||||||
", nonce=" + Hex.toHexString(nonce) +
|
|
||||||
", storage=" + storage +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class CallCreate {
|
|
||||||
|
|
||||||
private final byte[] data;
|
|
||||||
private final byte[] destination;
|
|
||||||
private final byte[] gasLimit;
|
|
||||||
private final byte[] value;
|
|
||||||
|
|
||||||
/* e.g.
|
|
||||||
"data" : [
|
|
||||||
],
|
|
||||||
"destination" : "cd1722f3947def4cf144679da39c4c32bdc35681",
|
|
||||||
"gasLimit" : 9792,
|
|
||||||
"value" : 74
|
|
||||||
*/
|
|
||||||
|
|
||||||
public CallCreate(JSONObject callCreateJSON) {
|
|
||||||
|
|
||||||
String data = callCreateJSON.get("data").toString();
|
|
||||||
String destination = callCreateJSON.get("destination").toString();
|
|
||||||
String gasLimit = callCreateJSON.get("gasLimit").toString();
|
|
||||||
String value = callCreateJSON.get("value").toString();
|
|
||||||
|
|
||||||
if (data != null && data.length() > 2)
|
|
||||||
this.data = Hex.decode(data.substring(2));
|
|
||||||
else
|
|
||||||
this.data = ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
this.destination = Hex.decode(destination);
|
|
||||||
this.gasLimit = ByteUtil.bigIntegerToBytes(new BigInteger(gasLimit));
|
|
||||||
this.value = ByteUtil.bigIntegerToBytes(new BigInteger(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getDestination() {
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getGasLimit() {
|
|
||||||
return gasLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CallCreate{" +
|
|
||||||
"data=" + Hex.toHexString(data) +
|
|
||||||
", destination=" + Hex.toHexString(destination) +
|
|
||||||
", gasLimit=" + Hex.toHexString(gasLimit) +
|
|
||||||
", value=" + Hex.toHexString(value) +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import org.spongycastle.util.BigIntegers;
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class Env {
|
|
||||||
|
|
||||||
private final byte[] currentCoinbase;
|
|
||||||
private final byte[] currentDifficulty;
|
|
||||||
private final byte[] currentGasLimit;
|
|
||||||
private final byte[] currentNumber;
|
|
||||||
private final byte[] currentTimestamp;
|
|
||||||
private final byte[] previousHash;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
e.g:
|
|
||||||
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
|
||||||
"currentDifficulty" : "256",
|
|
||||||
"currentGasLimit" : "1000000",
|
|
||||||
"currentNumber" : "0",
|
|
||||||
"currentTimestamp" : 1,
|
|
||||||
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
|
||||||
*/
|
|
||||||
public Env(JSONObject env) {
|
|
||||||
|
|
||||||
String coinbase = env.get("currentCoinbase").toString();
|
|
||||||
String difficulty = env.get("currentDifficulty").toString();
|
|
||||||
String timestamp = env.get("currentTimestamp").toString();
|
|
||||||
String number = env.get("currentNumber").toString();
|
|
||||||
String gasLimit = env.get("currentGasLimit").toString();
|
|
||||||
String prevHash = env.get("previousHash").toString();
|
|
||||||
|
|
||||||
this.currentCoinbase = Hex.decode(coinbase);
|
|
||||||
this.currentDifficulty = BigIntegers.asUnsignedByteArray( new BigInteger(difficulty) );
|
|
||||||
this.currentGasLimit = BigIntegers.asUnsignedByteArray(new BigInteger(gasLimit));
|
|
||||||
this.currentNumber = new BigInteger(number).toByteArray();
|
|
||||||
this.currentTimestamp = new BigInteger(timestamp).toByteArray();
|
|
||||||
this.previousHash = Hex.decode(prevHash);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentCoinbase() {
|
|
||||||
return currentCoinbase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentDifficulty() {
|
|
||||||
return currentDifficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentGasLimit() {
|
|
||||||
return currentGasLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentNumber() {
|
|
||||||
return currentNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentTimestamp() {
|
|
||||||
return currentTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getPreviousHash() {
|
|
||||||
return previousHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Env{" +
|
|
||||||
"currentCoinbase=" + Hex.toHexString(currentCoinbase) +
|
|
||||||
", currentDifficulty=" + Hex.toHexString(currentDifficulty) +
|
|
||||||
", currentGasLimit=" + Hex.toHexString(currentGasLimit) +
|
|
||||||
", currentNumber=" + Hex.toHexString(currentNumber) +
|
|
||||||
", currentTimestamp=" + Hex.toHexString(currentTimestamp) +
|
|
||||||
", previousHash=" + Hex.toHexString(previousHash) +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class Exec {
|
|
||||||
|
|
||||||
private final byte[] address;
|
|
||||||
private final byte[] caller;
|
|
||||||
private final byte[] data;
|
|
||||||
private final byte[] code;
|
|
||||||
|
|
||||||
private final byte[] gas;
|
|
||||||
private final byte[] gasPrice;
|
|
||||||
|
|
||||||
private final byte[] origin;
|
|
||||||
private final byte[] value;
|
|
||||||
|
|
||||||
/*
|
|
||||||
e.g:
|
|
||||||
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
|
|
||||||
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
|
|
||||||
"data" : [
|
|
||||||
],
|
|
||||||
|
|
||||||
"code" : [ 96,0,96,0,96,0,96,0,96,74,51,96,200,92,3,241 ],
|
|
||||||
|
|
||||||
"gas" : 10000,
|
|
||||||
"gasPrice" : 100000000000000,
|
|
||||||
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
|
|
||||||
"value" : 1000000000000000000
|
|
||||||
*/
|
|
||||||
public Exec(JSONObject exec) {
|
|
||||||
|
|
||||||
String address = exec.get("address").toString();
|
|
||||||
String caller = exec.get("caller").toString();
|
|
||||||
|
|
||||||
String code = exec.get("code").toString();
|
|
||||||
String data = exec.get("data").toString();
|
|
||||||
|
|
||||||
String gas = exec.get("gas").toString();
|
|
||||||
String gasPrice = exec.get("gasPrice").toString();
|
|
||||||
String origin = exec.get("origin").toString();
|
|
||||||
|
|
||||||
String value = exec.get("value").toString();
|
|
||||||
|
|
||||||
this.address = Hex.decode(address);
|
|
||||||
this.caller = Hex.decode(caller);
|
|
||||||
|
|
||||||
if (code != null && code.length() > 2)
|
|
||||||
this.code = Hex.decode(code.substring(2));
|
|
||||||
else
|
|
||||||
this.code = ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
if (data != null && data.length() > 2)
|
|
||||||
this.data = Hex.decode(data.substring(2));
|
|
||||||
else
|
|
||||||
this.data = ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
this.gas = ByteUtil.bigIntegerToBytes(new BigInteger(gas));
|
|
||||||
this.gasPrice = ByteUtil.bigIntegerToBytes(new BigInteger(gasPrice));
|
|
||||||
|
|
||||||
this.origin = Hex.decode(origin);
|
|
||||||
this.value = ByteUtil.bigIntegerToBytes(new BigInteger(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCaller() {
|
|
||||||
return caller;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCode() {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getGas() {
|
|
||||||
return gas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getGasPrice() {
|
|
||||||
return gasPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getOrigin() {
|
|
||||||
return origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Exec{" +
|
|
||||||
"address=" + Hex.toHexString(address) +
|
|
||||||
", caller=" + Hex.toHexString(caller) +
|
|
||||||
", data=" + Hex.toHexString(data) +
|
|
||||||
", code=" + Hex.toHexString(data) +
|
|
||||||
", gas=" + Hex.toHexString(gas) +
|
|
||||||
", gasPrice=" + Hex.toHexString(gasPrice) +
|
|
||||||
", origin=" + Hex.toHexString(origin) +
|
|
||||||
", value=" + Hex.toHexString(value) +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class Helper {
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger("misc");
|
|
||||||
|
|
||||||
public static byte[] parseDataArray(JSONArray valArray) {
|
|
||||||
|
|
||||||
// value can be:
|
|
||||||
// 1. 324234 number
|
|
||||||
// 2. "0xAB3F23A" - hex string
|
|
||||||
// 3. "239472398472" - big number
|
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
for (Object val : valArray) {
|
|
||||||
|
|
||||||
if (val instanceof String) {
|
|
||||||
|
|
||||||
// Hex num
|
|
||||||
boolean hexVal = Pattern.matches("0[xX][0-9a-fA-F]+", val.toString());
|
|
||||||
if (hexVal) {
|
|
||||||
String number = ((String) val).substring(2);
|
|
||||||
if (number.length() % 2 == 1) number = "0" + number;
|
|
||||||
byte[] data = Hex.decode(number);
|
|
||||||
try {
|
|
||||||
bos.write(data);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("should not happen", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// BigInt num
|
|
||||||
boolean isNumeric = Pattern.matches("[0-9a-fA-F]+", val.toString());
|
|
||||||
if (!isNumeric) throw new Error("Wrong test case JSON format");
|
|
||||||
else {
|
|
||||||
BigInteger value = new BigInteger(val.toString());
|
|
||||||
try {
|
|
||||||
bos.write(value.toByteArray());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("should not happen", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (val instanceof Long) {
|
|
||||||
|
|
||||||
// Simple long
|
|
||||||
byte[] data = ByteUtil.bigIntegerToBytes(BigInteger.valueOf((Long) val));
|
|
||||||
try {
|
|
||||||
bos.write(data);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("should not happen", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error("Wrong test case JSON format");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bos.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.config.SystemProperties;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.JSONParser;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
public class JSONReader {
|
|
||||||
|
|
||||||
public static String loadJSON(String filename) {
|
|
||||||
String json = "";
|
|
||||||
if (!SystemProperties.CONFIG.vmTestLoadLocal())
|
|
||||||
json = getFromUrl("https://raw.githubusercontent.com/ethereum/tests/develop/" + filename);
|
|
||||||
return json.isEmpty() ? getFromLocal(filename) : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String loadJSONFromCommit(String filename, String shacommit) {
|
|
||||||
String json = "";
|
|
||||||
if (!SystemProperties.CONFIG.vmTestLoadLocal())
|
|
||||||
json = getFromUrl("https://raw.githubusercontent.com/ethereum/tests/" + shacommit + "/" + filename);
|
|
||||||
return json.isEmpty() ? getFromLocal(filename) : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFromLocal(String filename) {
|
|
||||||
System.out.println("Loading local file: " + filename);
|
|
||||||
try {
|
|
||||||
if (System.getProperty("ETHEREUM_TEST_PATH") == null) {
|
|
||||||
System.out.println("ETHEREUM_TEST_PATH is not passed as a VM argument, please make sure you pass it " +
|
|
||||||
"with the correct path");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
System.out.println("From: " + System.getProperty("ETHEREUM_TEST_PATH"));
|
|
||||||
File vmTestFile = new File(System.getProperty("ETHEREUM_TEST_PATH") + filename);
|
|
||||||
return new String(Files.readAllBytes(vmTestFile.toPath()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFromUrl(String urlToRead) {
|
|
||||||
URL url;
|
|
||||||
HttpURLConnection conn;
|
|
||||||
BufferedReader rd;
|
|
||||||
String line;
|
|
||||||
String result = "";
|
|
||||||
try {
|
|
||||||
url = new URL(urlToRead);
|
|
||||||
conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setRequestMethod("GET");
|
|
||||||
conn.setDoOutput(true);
|
|
||||||
conn.connect();
|
|
||||||
InputStream in = conn.getInputStream();
|
|
||||||
rd = new BufferedReader(new InputStreamReader(in));
|
|
||||||
System.out.println("Loading remote file: " + urlToRead);
|
|
||||||
while ((line = rd.readLine()) != null) {
|
|
||||||
result += line;
|
|
||||||
}
|
|
||||||
rd.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTestBlobForTreeSha(String shacommit, String testcase){
|
|
||||||
|
|
||||||
String result = getFromUrl("https://api.github.com/repos/ethereum/tests/git/trees/" + shacommit);
|
|
||||||
|
|
||||||
JSONParser parser = new JSONParser();
|
|
||||||
JSONObject testSuiteObj = null;
|
|
||||||
|
|
||||||
List<String> fileNames = new ArrayList<String>();
|
|
||||||
try {
|
|
||||||
testSuiteObj = (JSONObject) parser.parse(result);
|
|
||||||
JSONArray tree = (JSONArray)testSuiteObj.get("tree");
|
|
||||||
|
|
||||||
for (Object oEntry : tree) {
|
|
||||||
JSONObject entry = (JSONObject) oEntry;
|
|
||||||
String testName = (String) entry.get("path");
|
|
||||||
if ( testName.equals(testcase) ) {
|
|
||||||
String blobresult = getFromUrl( (String) entry.get("url") );
|
|
||||||
|
|
||||||
testSuiteObj = (JSONObject) parser.parse(blobresult);
|
|
||||||
String blob = (String) testSuiteObj.get("content");
|
|
||||||
byte[] valueDecoded= Base64.decodeBase64(blob.getBytes() );
|
|
||||||
//System.out.println("Decoded value is " + new String(valueDecoded));
|
|
||||||
return new String(valueDecoded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ParseException e) {e.printStackTrace();}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> getFileNamesForTreeSha(String sha){
|
|
||||||
|
|
||||||
String result = getFromUrl("https://api.github.com/repos/ethereum/tests/git/trees/" + sha);
|
|
||||||
|
|
||||||
JSONParser parser = new JSONParser();
|
|
||||||
JSONObject testSuiteObj = null;
|
|
||||||
|
|
||||||
List<String> fileNames = new ArrayList<String>();
|
|
||||||
try {
|
|
||||||
testSuiteObj = (JSONObject) parser.parse(result);
|
|
||||||
JSONArray tree = (JSONArray)testSuiteObj.get("tree");
|
|
||||||
|
|
||||||
for (Object oEntry : tree) {
|
|
||||||
JSONObject entry = (JSONObject) oEntry;
|
|
||||||
String testName = (String) entry.get("path");
|
|
||||||
fileNames.add(testName);
|
|
||||||
}
|
|
||||||
} catch (ParseException e) {e.printStackTrace();}
|
|
||||||
|
|
||||||
return fileNames;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.vm.DataWord;
|
|
||||||
import org.ethereum.vm.LogInfo;
|
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Logs {
|
|
||||||
List<LogInfo> logs = new ArrayList<>();
|
|
||||||
|
|
||||||
public Logs(JSONArray jLogs) {
|
|
||||||
|
|
||||||
for (Object jLog1 : jLogs) {
|
|
||||||
|
|
||||||
JSONObject jLog = (JSONObject) jLog1;
|
|
||||||
byte[] address = Hex.decode((String) jLog.get("address"));
|
|
||||||
byte[] data = Hex.decode(((String) jLog.get("data")).substring(2));
|
|
||||||
|
|
||||||
List<DataWord> topics = new ArrayList<>();
|
|
||||||
|
|
||||||
JSONArray jTopics = (JSONArray) jLog.get("topics");
|
|
||||||
for (Object t : jTopics.toArray()) {
|
|
||||||
byte[] topic = Hex.decode(((String) t));
|
|
||||||
topics.add(new DataWord(topic));
|
|
||||||
}
|
|
||||||
|
|
||||||
LogInfo li = new LogInfo(address, topics, data);
|
|
||||||
logs.add(li);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Iterator<LogInfo> getIterator() {
|
|
||||||
return logs.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public List<String> compareToReal(List<LogInfo> logs) {
|
|
||||||
|
|
||||||
List<String> results = new ArrayList<>();
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (LogInfo postLog : this.logs) {
|
|
||||||
|
|
||||||
LogInfo realLog = logs.get(i);
|
|
||||||
|
|
||||||
String postAddress = Hex.toHexString(postLog.getAddress());
|
|
||||||
String realAddress = Hex.toHexString(realLog.getAddress());
|
|
||||||
|
|
||||||
if (!postAddress.equals(realAddress)) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Log: %s: has unexpected address, expected address: %s found address: %s",
|
|
||||||
i, postAddress, realAddress);
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
String postData = Hex.toHexString(postLog.getData());
|
|
||||||
String realData = Hex.toHexString(realLog.getData());
|
|
||||||
|
|
||||||
if (!postData.equals(realData)) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Log: %s: has unexpected data, expected data: %s found data: %s",
|
|
||||||
i, postData, realData);
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
String postBloom = Hex.toHexString(postLog.getBloom().getData());
|
|
||||||
String realBloom = Hex.toHexString(realLog.getBloom().getData());
|
|
||||||
|
|
||||||
if (!postData.equals(realData)) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Log: %s: has unexpected bloom, expected bloom: %s found bloom: %s",
|
|
||||||
i, postBloom, realBloom);
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DataWord> postTopics = postLog.getTopics();
|
|
||||||
List<DataWord> realTopics = realLog.getTopics();
|
|
||||||
|
|
||||||
int j = 0;
|
|
||||||
for (DataWord postTopic : postTopics) {
|
|
||||||
|
|
||||||
DataWord realTopic = realTopics.get(j);
|
|
||||||
|
|
||||||
if (!postTopic.equals(realTopic)) {
|
|
||||||
|
|
||||||
String formattedString = String.format("Log: %s: has unexpected topic: %s, expected topic: %s found topic: %s",
|
|
||||||
i, j, postTopic, realTopic);
|
|
||||||
results.add(formattedString);
|
|
||||||
}
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.db.ByteArrayWrapper;
|
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 15.12.2014
|
|
||||||
*/
|
|
||||||
public class StateTestCase {
|
|
||||||
|
|
||||||
private String name = "";
|
|
||||||
|
|
||||||
private Env env;
|
|
||||||
private Logs logs;
|
|
||||||
private byte[] out;
|
|
||||||
|
|
||||||
// "pre": { ... },
|
|
||||||
private Map<ByteArrayWrapper, AccountState> pre = new HashMap<>();
|
|
||||||
|
|
||||||
// "post": { ... },
|
|
||||||
private Map<ByteArrayWrapper, AccountState> post = new HashMap<>();
|
|
||||||
|
|
||||||
private Transaction transaction;
|
|
||||||
|
|
||||||
public StateTestCase(String name, JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
this(testCaseJSONObj);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StateTestCase(JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
JSONObject envJSON = (JSONObject) testCaseJSONObj.get("env");
|
|
||||||
JSONArray logsJSON = (JSONArray) testCaseJSONObj.get("logs");
|
|
||||||
String outStr = testCaseJSONObj.get("out").toString();
|
|
||||||
JSONObject txJSON = (JSONObject) testCaseJSONObj.get("transaction");
|
|
||||||
|
|
||||||
JSONObject preJSON = (JSONObject) testCaseJSONObj.get("pre");
|
|
||||||
JSONObject postJSON = (JSONObject) testCaseJSONObj.get("post");
|
|
||||||
|
|
||||||
this.env = new Env(envJSON);
|
|
||||||
this.logs = new Logs(logsJSON);
|
|
||||||
this.out = Utils.parseData(outStr);
|
|
||||||
this.transaction = new Transaction(txJSON);
|
|
||||||
|
|
||||||
for (Object key : preJSON.keySet()) {
|
|
||||||
|
|
||||||
byte[] keyBytes = Hex.decode(key.toString());
|
|
||||||
AccountState accountState =
|
|
||||||
new AccountState(keyBytes, (JSONObject) preJSON.get(key));
|
|
||||||
|
|
||||||
pre.put(new ByteArrayWrapper(keyBytes), accountState);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object key : postJSON.keySet()) {
|
|
||||||
|
|
||||||
byte[] keyBytes = Hex.decode(key.toString());
|
|
||||||
AccountState accountState =
|
|
||||||
new AccountState(keyBytes, (JSONObject) postJSON.get(key));
|
|
||||||
|
|
||||||
post.put(new ByteArrayWrapper(keyBytes), accountState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new ParseException(0, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Env getEnv() {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logs getLogs() {
|
|
||||||
return logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getOut() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<ByteArrayWrapper, AccountState> getPre() {
|
|
||||||
return pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<ByteArrayWrapper, AccountState> getPost() {
|
|
||||||
return post;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transaction getTransaction() {
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "StateTestCase{" +
|
|
||||||
"name='" + name + '\'' +
|
|
||||||
", env=" + env +
|
|
||||||
", logs=" + logs +
|
|
||||||
", out=" + Arrays.toString(out) +
|
|
||||||
", pre=" + pre +
|
|
||||||
", post=" + post +
|
|
||||||
", transaction=" + transaction +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 10.07.2014
|
|
||||||
*/
|
|
||||||
public class StateTestSuite {
|
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger("TCK-Test");
|
|
||||||
Map<String, StateTestCase> testCases = new HashMap<>();
|
|
||||||
|
|
||||||
public StateTestSuite(JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
for (Object key : testCaseJSONObj.keySet()) {
|
|
||||||
|
|
||||||
Object testCaseJSON = testCaseJSONObj.get(key);
|
|
||||||
|
|
||||||
StateTestCase testCase = new StateTestCase(key.toString(), (JSONObject) testCaseJSON);
|
|
||||||
|
|
||||||
testCases.put(key.toString(), testCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public StateTestCase getTestCase(String name) {
|
|
||||||
|
|
||||||
StateTestCase testCase = testCases.get(name);
|
|
||||||
if (testCase == null) throw new NullPointerException("Test cases doesn't exist: " + name);
|
|
||||||
|
|
||||||
return testCase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<StateTestCase> getAllTests() {
|
|
||||||
return testCases.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.db.ByteArrayWrapper;
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import org.spongycastle.util.BigIntegers;
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class TestCase {
|
|
||||||
|
|
||||||
private String name = "";
|
|
||||||
|
|
||||||
// "env": { ... },
|
|
||||||
private Env env;
|
|
||||||
|
|
||||||
//
|
|
||||||
private Logs logs;
|
|
||||||
|
|
||||||
// "exec": { ... },
|
|
||||||
private Exec exec;
|
|
||||||
|
|
||||||
// "gas": { ... },
|
|
||||||
private byte[] gas;
|
|
||||||
|
|
||||||
// "out": { ... },
|
|
||||||
private byte[] out;
|
|
||||||
|
|
||||||
// "pre": { ... },
|
|
||||||
private Map<ByteArrayWrapper, AccountState> pre = new HashMap<>();
|
|
||||||
|
|
||||||
// "post": { ... },
|
|
||||||
private Map<ByteArrayWrapper, AccountState> post = new HashMap<>();
|
|
||||||
|
|
||||||
// "callcreates": { ... }
|
|
||||||
private List<CallCreate> callCreateList = new ArrayList<>();
|
|
||||||
|
|
||||||
public TestCase(String name, JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
this(testCaseJSONObj);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestCase(JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
JSONObject envJSON = (JSONObject) testCaseJSONObj.get("env");
|
|
||||||
JSONObject execJSON = (JSONObject) testCaseJSONObj.get("exec");
|
|
||||||
JSONObject preJSON = (JSONObject) testCaseJSONObj.get("pre");
|
|
||||||
JSONObject postJSON = new JSONObject();
|
|
||||||
if (testCaseJSONObj.containsKey("post")) // in cases where there is no post dictionary (when testing for
|
|
||||||
// exceptions for example)
|
|
||||||
postJSON = (JSONObject) testCaseJSONObj.get("post");
|
|
||||||
JSONArray callCreates = new JSONArray();
|
|
||||||
if (testCaseJSONObj.containsKey("callcreates"))
|
|
||||||
callCreates = (JSONArray) testCaseJSONObj.get("callcreates");
|
|
||||||
|
|
||||||
JSONArray logsJSON = new JSONArray();
|
|
||||||
if (testCaseJSONObj.containsKey("logs"))
|
|
||||||
logsJSON = (JSONArray) testCaseJSONObj.get("logs");
|
|
||||||
logs = new Logs(logsJSON);
|
|
||||||
|
|
||||||
String gasString = "0";
|
|
||||||
if (testCaseJSONObj.containsKey("gas"))
|
|
||||||
gasString = testCaseJSONObj.get("gas").toString();
|
|
||||||
this.gas = BigIntegers.asUnsignedByteArray(new BigInteger(gasString));
|
|
||||||
|
|
||||||
String outString = null;
|
|
||||||
if (testCaseJSONObj.containsKey("out"))
|
|
||||||
outString = testCaseJSONObj.get("out").toString();
|
|
||||||
if (outString != null && outString.length() > 2)
|
|
||||||
this.out = Hex.decode(outString.substring(2));
|
|
||||||
else
|
|
||||||
this.out = ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
for (Object key : preJSON.keySet()) {
|
|
||||||
|
|
||||||
byte[] keyBytes = Hex.decode(key.toString());
|
|
||||||
AccountState accountState =
|
|
||||||
new AccountState(keyBytes, (JSONObject) preJSON.get(key));
|
|
||||||
|
|
||||||
pre.put(new ByteArrayWrapper(keyBytes), accountState);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object key : postJSON.keySet()) {
|
|
||||||
|
|
||||||
byte[] keyBytes = Hex.decode(key.toString());
|
|
||||||
AccountState accountState =
|
|
||||||
new AccountState(keyBytes, (JSONObject) postJSON.get(key));
|
|
||||||
|
|
||||||
post.put(new ByteArrayWrapper(keyBytes), accountState);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object callCreate : callCreates) {
|
|
||||||
|
|
||||||
CallCreate cc = new CallCreate((JSONObject) callCreate);
|
|
||||||
this.callCreateList.add(cc);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.env = new Env(envJSON);
|
|
||||||
this.exec = new Exec(execJSON);
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new ParseException(0, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Env getEnv() {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Exec getExec() {
|
|
||||||
return exec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logs getLogs() {
|
|
||||||
return logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getGas() {
|
|
||||||
return gas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getOut() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<ByteArrayWrapper, AccountState> getPre() {
|
|
||||||
return pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<ByteArrayWrapper, AccountState> getPost() {
|
|
||||||
return post;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CallCreate> getCallCreateList() {
|
|
||||||
return callCreateList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TestCase{" +
|
|
||||||
"" + env +
|
|
||||||
", " + exec +
|
|
||||||
", gas=" + Hex.toHexString(gas) +
|
|
||||||
", out=" + Hex.toHexString(out) +
|
|
||||||
", pre=" + pre +
|
|
||||||
", post=" + post +
|
|
||||||
", callcreates=" + callCreateList +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.core.Block;
|
|
||||||
import org.ethereum.core.Transaction;
|
|
||||||
import org.ethereum.db.BlockStore;
|
|
||||||
import org.ethereum.facade.Repository;
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
import org.ethereum.vm.DataWord;
|
|
||||||
import org.ethereum.vm.Program;
|
|
||||||
import org.ethereum.vm.ProgramInvoke;
|
|
||||||
import org.ethereum.vm.ProgramInvokeFactory;
|
|
||||||
import org.ethereum.vm.ProgramInvokeImpl;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 19.12.2014
|
|
||||||
*/
|
|
||||||
public class TestProgramInvokeFactory implements ProgramInvokeFactory {
|
|
||||||
|
|
||||||
private final Env env;
|
|
||||||
|
|
||||||
TestProgramInvokeFactory(Env env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository, BlockStore blockStore) {
|
|
||||||
return generalInvoke(tx, repository, blockStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord inValue, DataWord inGas,
|
|
||||||
BigInteger balanceInt, byte[] dataIn,
|
|
||||||
Repository repository, BlockStore blockStore, boolean byTestingSuite) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private ProgramInvoke generalInvoke(Transaction tx, Repository repository, BlockStore blockStore) {
|
|
||||||
|
|
||||||
/*** ADDRESS op ***/
|
|
||||||
// YP: Get address of currently executing account.
|
|
||||||
byte[] address = tx.isContractCreation() ? tx.getContractAddress() : tx.getReceiveAddress();
|
|
||||||
|
|
||||||
/*** ORIGIN op ***/
|
|
||||||
// YP: This is the sender of original transaction; it is never a contract.
|
|
||||||
byte[] origin = tx.getSender();
|
|
||||||
|
|
||||||
/*** CALLER op ***/
|
|
||||||
// YP: This is the address of the account that is directly responsible for this execution.
|
|
||||||
byte[] caller = tx.getSender();
|
|
||||||
|
|
||||||
/*** BALANCE op ***/
|
|
||||||
byte[] balance = repository.getBalance(address).toByteArray();
|
|
||||||
|
|
||||||
/*** GASPRICE op ***/
|
|
||||||
byte[] gasPrice = tx.getGasPrice();
|
|
||||||
|
|
||||||
/*** GAS op ***/
|
|
||||||
byte[] gas = tx.getGasLimit();
|
|
||||||
|
|
||||||
/*** CALLVALUE op ***/
|
|
||||||
byte[] callValue = tx.getValue() == null ? new byte[]{0} : tx.getValue();
|
|
||||||
|
|
||||||
/*** CALLDATALOAD op ***/
|
|
||||||
/*** CALLDATACOPY op ***/
|
|
||||||
/*** CALLDATASIZE op ***/
|
|
||||||
byte[] data = tx.getData() == null ? ByteUtil.EMPTY_BYTE_ARRAY : tx.getData();
|
|
||||||
|
|
||||||
/*** PREVHASH op ***/
|
|
||||||
byte[] lastHash = env.getPreviousHash();
|
|
||||||
|
|
||||||
/*** COINBASE op ***/
|
|
||||||
byte[] coinbase = env.getCurrentCoinbase();
|
|
||||||
|
|
||||||
/*** TIMESTAMP op ***/
|
|
||||||
long timestamp = ByteUtil.byteArrayToLong(env.getCurrentTimestamp());
|
|
||||||
|
|
||||||
/*** NUMBER op ***/
|
|
||||||
long number = ByteUtil.byteArrayToLong(env.getCurrentNumber());
|
|
||||||
|
|
||||||
/*** DIFFICULTY op ***/
|
|
||||||
byte[] difficulty = env.getCurrentDifficulty();
|
|
||||||
|
|
||||||
/*** GASLIMIT op ***/
|
|
||||||
long gaslimit = ByteUtil.byteArrayToLong(env.getCurrentGasLimit());
|
|
||||||
|
|
||||||
return new ProgramInvokeImpl(address, origin, caller, balance,
|
|
||||||
gasPrice, gas, callValue, data, lastHash, coinbase,
|
|
||||||
timestamp, number, difficulty, gaslimit, repository, blockStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,562 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.core.BlockchainImpl;
|
|
||||||
import org.ethereum.core.Block;
|
|
||||||
import org.ethereum.core.TransactionExecutor;
|
|
||||||
import org.ethereum.db.*;
|
|
||||||
import org.ethereum.facade.Repository;
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
import org.ethereum.vm.DataWord;
|
|
||||||
import org.ethereum.vm.LogInfo;
|
|
||||||
import org.ethereum.vm.Program;
|
|
||||||
import org.ethereum.vm.ProgramInvoke;
|
|
||||||
import org.ethereum.vm.ProgramInvokeFactory;
|
|
||||||
import org.ethereum.vm.ProgramInvokeImpl;
|
|
||||||
import org.ethereum.vm.VM;
|
|
||||||
import org.ethereum.vmtrace.ProgramTrace;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.ethereum.util.ByteUtil.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 02.07.2014
|
|
||||||
*/
|
|
||||||
public class TestRunner {
|
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger("TCK-Test");
|
|
||||||
private ProgramTrace trace = null;
|
|
||||||
|
|
||||||
public List<String> runTestSuite(TestSuite testSuite) {
|
|
||||||
|
|
||||||
Iterator<TestCase> testIterator = testSuite.iterator();
|
|
||||||
List<String> resultCollector = new ArrayList<>();
|
|
||||||
|
|
||||||
while (testIterator.hasNext()) {
|
|
||||||
|
|
||||||
TestCase testCase = testIterator.next();
|
|
||||||
|
|
||||||
TestRunner runner = new TestRunner();
|
|
||||||
List<String> result = runner.runTestCase(testCase);
|
|
||||||
resultCollector.addAll(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultCollector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> runTestCase(StateTestCase testCase) {
|
|
||||||
|
|
||||||
List<String> results = new ArrayList<>();
|
|
||||||
logger.info("\n***");
|
|
||||||
logger.info(" Running test case: [" + testCase.getName() + "]");
|
|
||||||
logger.info("***\n");
|
|
||||||
|
|
||||||
logger.info("--------- PRE ---------");
|
|
||||||
RepositoryImpl repository = loadRepository(new RepositoryDummy(), testCase.getPre());
|
|
||||||
|
|
||||||
|
|
||||||
logger.info("loaded repository");
|
|
||||||
|
|
||||||
org.ethereum.core.Transaction tx = createTransaction(testCase.getTransaction());
|
|
||||||
logger.info("transaction: {}", tx.toString());
|
|
||||||
|
|
||||||
byte[] secretKey = testCase.getTransaction().secretKey;
|
|
||||||
logger.info("sign tx with: {}", Hex.toHexString(secretKey));
|
|
||||||
tx.sign(secretKey);
|
|
||||||
|
|
||||||
BlockchainImpl blockchain = new BlockchainImpl();
|
|
||||||
blockchain.setRepository(repository);
|
|
||||||
|
|
||||||
byte[] coinbase = testCase.getEnv().getCurrentCoinbase();
|
|
||||||
ProgramInvokeFactory invokeFactory = new TestProgramInvokeFactory(testCase.getEnv());
|
|
||||||
|
|
||||||
Block block = new Block(
|
|
||||||
ByteUtil.EMPTY_BYTE_ARRAY,
|
|
||||||
ByteUtil.EMPTY_BYTE_ARRAY,
|
|
||||||
coinbase,
|
|
||||||
ByteUtil.EMPTY_BYTE_ARRAY,
|
|
||||||
testCase.getEnv().getCurrentDifficulty(),
|
|
||||||
new BigInteger(1, testCase.getEnv().getCurrentNumber()).longValue(),
|
|
||||||
new BigInteger(1, testCase.getEnv().getCurrentGasLimit()).longValue(),
|
|
||||||
0L,
|
|
||||||
new BigInteger(1, testCase.getEnv().getCurrentTimestamp()).longValue(),
|
|
||||||
ByteUtil.ZERO_BYTE_ARRAY,
|
|
||||||
ByteUtil.ZERO_BYTE_ARRAY,
|
|
||||||
null, null);
|
|
||||||
|
|
||||||
blockchain.setBestBlock(block);
|
|
||||||
blockchain.setProgramInvokeFactory(invokeFactory);
|
|
||||||
blockchain.startTracking();
|
|
||||||
|
|
||||||
Repository track = repository.startTracking();
|
|
||||||
TransactionExecutor executor =
|
|
||||||
new TransactionExecutor(tx, coinbase, track, new BlockStoreDummy(),
|
|
||||||
invokeFactory, blockchain.getBestBlock());
|
|
||||||
executor.execute();
|
|
||||||
track.commit();
|
|
||||||
|
|
||||||
logger.info("compare results");
|
|
||||||
|
|
||||||
List<LogInfo> logs = null;
|
|
||||||
if (executor.getResult() != null)
|
|
||||||
logs = executor.getResult().getLogInfoList();
|
|
||||||
|
|
||||||
List<String> logResults = testCase.getLogs().compareToReal(logs);
|
|
||||||
results.addAll(logResults);
|
|
||||||
|
|
||||||
Set<ByteArrayWrapper> fullAddressSet = repository.getFullAddressSet();
|
|
||||||
int repoSize = 0;
|
|
||||||
for (ByteArrayWrapper addrWrapped : fullAddressSet) {
|
|
||||||
|
|
||||||
byte[] addr = addrWrapped.getData();
|
|
||||||
|
|
||||||
org.ethereum.core.AccountState accountState = repository.getAccountState(addr);
|
|
||||||
ContractDetails contractDetails = repository.getContractDetails(addr);
|
|
||||||
|
|
||||||
logger.info("{} \n{} \n{}", Hex.toHexString(addr),
|
|
||||||
accountState.toString(), contractDetails.toString());
|
|
||||||
logger.info("");
|
|
||||||
|
|
||||||
AccountState expectedAccountState = testCase.getPost().get(wrap(addr));
|
|
||||||
if (expectedAccountState == null) {
|
|
||||||
String formattedString = String.format("Unexpected account state: address: %s", Hex.toHexString(addr));
|
|
||||||
results.add(formattedString);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> result = expectedAccountState.compareToReal(accountState, contractDetails);
|
|
||||||
results.addAll(result);
|
|
||||||
|
|
||||||
++repoSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
int postRepoSize = testCase.getPost().size();
|
|
||||||
|
|
||||||
if (postRepoSize > repoSize) {
|
|
||||||
results.add("ERROR: Expected 'Post' repository contains more accounts than executed repository ");
|
|
||||||
|
|
||||||
logger.info("Full address set: " + fullAddressSet);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> runTestCase(TestCase testCase) {
|
|
||||||
|
|
||||||
logger.info("\n***");
|
|
||||||
logger.info(" Running test case: [" + testCase.getName() + "]");
|
|
||||||
logger.info("***\n");
|
|
||||||
List<String> results = new ArrayList<>();
|
|
||||||
|
|
||||||
|
|
||||||
logger.info("--------- PRE ---------");
|
|
||||||
RepositoryImpl repository = loadRepository(new RepositoryVMTestDummy(), testCase.getPre());
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
|
|
||||||
/* 2. Create ProgramInvoke - Env/Exec */
|
|
||||||
Env env = testCase.getEnv();
|
|
||||||
Exec exec = testCase.getExec();
|
|
||||||
Logs logs = testCase.getLogs();
|
|
||||||
|
|
||||||
byte[] address = exec.getAddress();
|
|
||||||
byte[] origin = exec.getOrigin();
|
|
||||||
byte[] caller = exec.getCaller();
|
|
||||||
byte[] balance = ByteUtil.bigIntegerToBytes(repository.getBalance(exec.getAddress()));
|
|
||||||
byte[] gasPrice = exec.getGasPrice();
|
|
||||||
byte[] gas = exec.getGas();
|
|
||||||
byte[] callValue = exec.getValue();
|
|
||||||
byte[] msgData = exec.getData();
|
|
||||||
byte[] lastHash = env.getPreviousHash();
|
|
||||||
byte[] coinbase = env.getCurrentCoinbase();
|
|
||||||
long timestamp = ByteUtil.byteArrayToLong(env.getCurrentTimestamp());
|
|
||||||
long number = ByteUtil.byteArrayToLong(env.getCurrentNumber());
|
|
||||||
byte[] difficulty = env.getCurrentDifficulty();
|
|
||||||
long gaslimit = new BigInteger(env.getCurrentGasLimit()).longValue();
|
|
||||||
|
|
||||||
// Origin and caller need to exist in order to be able to execute
|
|
||||||
if (repository.getAccountState(origin) == null)
|
|
||||||
repository.createAccount(origin);
|
|
||||||
if (repository.getAccountState(caller) == null)
|
|
||||||
repository.createAccount(caller);
|
|
||||||
|
|
||||||
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance,
|
|
||||||
gasPrice, gas, callValue, msgData, lastHash, coinbase,
|
|
||||||
timestamp, number, difficulty, gaslimit, repository, new BlockStoreDummy(), true);
|
|
||||||
|
|
||||||
/* 3. Create Program - exec.code */
|
|
||||||
/* 4. run VM */
|
|
||||||
VM vm = new VM();
|
|
||||||
Program program = new Program(exec.getCode(), programInvoke);
|
|
||||||
boolean vmDidThrowAnEception = false;
|
|
||||||
RuntimeException e = null;
|
|
||||||
try {
|
|
||||||
while (!program.isStopped())
|
|
||||||
vm.step(program);
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
vmDidThrowAnEception = true;
|
|
||||||
e = ex;
|
|
||||||
}
|
|
||||||
program.saveProgramTraceToFile(testCase.getName());
|
|
||||||
|
|
||||||
if (testCase.getPost().size() == 0) {
|
|
||||||
if (vmDidThrowAnEception != true) {
|
|
||||||
String output =
|
|
||||||
String.format("VM was expected to throw an exception");
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
} else
|
|
||||||
logger.info("VM did throw an exception: " + e.toString());
|
|
||||||
} else {
|
|
||||||
if (vmDidThrowAnEception) {
|
|
||||||
String output =
|
|
||||||
String.format("VM threw an unexpected exception: " + e.toString());
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.trace = program.getProgramTrace();
|
|
||||||
|
|
||||||
System.out.println("--------- POST --------");
|
|
||||||
/* 5. Assert Post values */
|
|
||||||
for (ByteArrayWrapper key : testCase.getPost().keySet()) {
|
|
||||||
|
|
||||||
AccountState accountState = testCase.getPost().get(key);
|
|
||||||
|
|
||||||
long expectedNonce = accountState.getNonceLong();
|
|
||||||
BigInteger expectedBalance = accountState.getBigIntegerBalance();
|
|
||||||
byte[] expectedCode = accountState.getCode();
|
|
||||||
|
|
||||||
boolean accountExist = (null != repository.getAccountState(key.getData()));
|
|
||||||
if (!accountExist) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("The expected account does not exist. key: [ %s ]",
|
|
||||||
Hex.toHexString(key.getData()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
long actualNonce = repository.getNonce(key.getData()).longValue();
|
|
||||||
BigInteger actualBalance = repository.getBalance(key.getData());
|
|
||||||
byte[] actualCode = repository.getCode(key.getData());
|
|
||||||
if (actualCode == null) actualCode = "".getBytes();
|
|
||||||
|
|
||||||
if (expectedNonce != actualNonce) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("The nonce result is different. key: [ %s ], expectedNonce: [ %d ] is actualNonce: [ %d ] ",
|
|
||||||
Hex.toHexString(key.getData()), expectedNonce, actualNonce);
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expectedBalance.equals(actualBalance)) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("The balance result is different. key: [ %s ], expectedBalance: [ %s ] is actualBalance: [ %s ] ",
|
|
||||||
Hex.toHexString(key.getData()), expectedBalance.toString(), actualBalance.toString());
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(expectedCode, actualCode)) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("The code result is different. account: [ %s ], expectedCode: [ %s ] is actualCode: [ %s ] ",
|
|
||||||
Hex.toHexString(key.getData()),
|
|
||||||
Hex.toHexString(expectedCode),
|
|
||||||
Hex.toHexString(actualCode));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert storage
|
|
||||||
Map<DataWord, DataWord> storage = accountState.getStorage();
|
|
||||||
for (DataWord storageKey : storage.keySet()) {
|
|
||||||
|
|
||||||
byte[] expectedStValue = storage.get(storageKey).getData();
|
|
||||||
|
|
||||||
ContractDetails contractDetails =
|
|
||||||
program.getResult().getRepository().getContractDetails(accountState.getAddress());
|
|
||||||
|
|
||||||
if (contractDetails == null) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Storage raw doesn't exist: key [ %s ], expectedValue: [ %s ]",
|
|
||||||
Hex.toHexString(storageKey.getData()),
|
|
||||||
Hex.toHexString(expectedStValue)
|
|
||||||
);
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<DataWord, DataWord> testStorage = contractDetails.getStorage();
|
|
||||||
DataWord actualValue = testStorage.get(new DataWord(storageKey.getData()));
|
|
||||||
|
|
||||||
if (actualValue == null ||
|
|
||||||
!Arrays.equals(expectedStValue, actualValue.getData())) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Storage value different: key [ %s ], expectedValue: [ %s ], actualValue: [ %s ]",
|
|
||||||
Hex.toHexString(storageKey.getData()),
|
|
||||||
Hex.toHexString(expectedStValue),
|
|
||||||
actualValue == null ? "" : Hex.toHexString(actualValue.getNoLeadZeroesData()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* asset logs */
|
|
||||||
List<LogInfo> logResult = program.getResult().getLogInfoList();
|
|
||||||
|
|
||||||
Iterator<LogInfo> postLogs = logs.getIterator();
|
|
||||||
int i = 0;
|
|
||||||
while (postLogs.hasNext()) {
|
|
||||||
|
|
||||||
LogInfo expectedLogInfo = postLogs.next();
|
|
||||||
|
|
||||||
LogInfo foundLogInfo = null;
|
|
||||||
if (logResult.size() > i)
|
|
||||||
foundLogInfo = logResult.get(i);
|
|
||||||
|
|
||||||
if (foundLogInfo == null) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected log [ %s ]", expectedLogInfo.toString());
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
} else {
|
|
||||||
if (!Arrays.equals(expectedLogInfo.getAddress(), foundLogInfo.getAddress())) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected address [ %s ], found [ %s ]", Hex.toHexString(expectedLogInfo.getAddress()), Hex.toHexString(foundLogInfo.getAddress()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(expectedLogInfo.getData(), foundLogInfo.getData())) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected data [ %s ], found [ %s ]", Hex.toHexString(expectedLogInfo.getData()), Hex.toHexString(foundLogInfo.getData()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expectedLogInfo.getBloom().equals(foundLogInfo.getBloom())) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected bloom [ %s ], found [ %s ]",
|
|
||||||
Hex.toHexString(expectedLogInfo.getBloom().getData()),
|
|
||||||
Hex.toHexString(foundLogInfo.getBloom().getData()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedLogInfo.getTopics().size() != foundLogInfo.getTopics().size()) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected number of topics [ %d ], found [ %d ]",
|
|
||||||
expectedLogInfo.getTopics().size(), foundLogInfo.getTopics().size());
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
} else {
|
|
||||||
int j = 0;
|
|
||||||
for (DataWord topic : expectedLogInfo.getTopics()) {
|
|
||||||
byte[] foundTopic = foundLogInfo.getTopics().get(j).getData();
|
|
||||||
|
|
||||||
if (!Arrays.equals(topic.getData(), foundTopic)) {
|
|
||||||
String output =
|
|
||||||
String.format("Expected topic [ %s ], found [ %s ]", Hex.toHexString(topic.getData()), Hex.toHexString(foundTopic));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: assert that you have no extra accounts in the repository
|
|
||||||
// TODO: -> basically the deleted by suicide should be deleted
|
|
||||||
// TODO: -> and no unexpected created
|
|
||||||
|
|
||||||
List<org.ethereum.vm.CallCreate> resultCallCreates =
|
|
||||||
program.getResult().getCallCreateList();
|
|
||||||
|
|
||||||
// assert call creates
|
|
||||||
for (int i = 0; i < testCase.getCallCreateList().size(); ++i) {
|
|
||||||
|
|
||||||
org.ethereum.vm.CallCreate resultCallCreate = null;
|
|
||||||
if (resultCallCreates != null && resultCallCreates.size() > i) {
|
|
||||||
resultCallCreate = resultCallCreates.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
CallCreate expectedCallCreate = testCase.getCallCreateList().get(i);
|
|
||||||
|
|
||||||
if (resultCallCreate == null && expectedCallCreate != null) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Missing call/create invoke: to: [ %s ], data: [ %s ], gas: [ %s ], value: [ %s ]",
|
|
||||||
Hex.toHexString(expectedCallCreate.getDestination()),
|
|
||||||
Hex.toHexString(expectedCallCreate.getData()),
|
|
||||||
Hex.toHexString(expectedCallCreate.getGasLimit()),
|
|
||||||
Hex.toHexString(expectedCallCreate.getValue()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean assertDestination = Arrays.equals(
|
|
||||||
expectedCallCreate.getDestination(),
|
|
||||||
resultCallCreate.getDestination());
|
|
||||||
if (!assertDestination) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Call/Create destination is different. Expected: [ %s ], result: [ %s ]",
|
|
||||||
Hex.toHexString(expectedCallCreate.getDestination()),
|
|
||||||
Hex.toHexString(resultCallCreate.getDestination()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean assertData = Arrays.equals(
|
|
||||||
expectedCallCreate.getData(),
|
|
||||||
resultCallCreate.getData());
|
|
||||||
if (!assertData) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Call/Create data is different. Expected: [ %s ], result: [ %s ]",
|
|
||||||
Hex.toHexString(expectedCallCreate.getData()),
|
|
||||||
Hex.toHexString(resultCallCreate.getData()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean assertGasLimit = Arrays.equals(
|
|
||||||
expectedCallCreate.getGasLimit(),
|
|
||||||
resultCallCreate.getGasLimit());
|
|
||||||
if (!assertGasLimit) {
|
|
||||||
String output =
|
|
||||||
String.format("Call/Create gasLimit is different. Expected: [ %s ], result: [ %s ]",
|
|
||||||
Hex.toHexString(expectedCallCreate.getGasLimit()),
|
|
||||||
Hex.toHexString(resultCallCreate.getGasLimit()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean assertValue = Arrays.equals(
|
|
||||||
expectedCallCreate.getValue(),
|
|
||||||
resultCallCreate.getValue());
|
|
||||||
if (!assertValue) {
|
|
||||||
String output =
|
|
||||||
String.format("Call/Create value is different. Expected: [ %s ], result: [ %s ]",
|
|
||||||
Hex.toHexString(expectedCallCreate.getValue()),
|
|
||||||
Hex.toHexString(resultCallCreate.getValue()));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert out
|
|
||||||
byte[] expectedHReturn = testCase.getOut();
|
|
||||||
byte[] actualHReturn = EMPTY_BYTE_ARRAY;
|
|
||||||
if (program.getResult().getHReturn() != null) {
|
|
||||||
actualHReturn = program.getResult().getHReturn().array();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(expectedHReturn, actualHReturn)) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("HReturn is different. Expected hReturn: [ %s ], actual hReturn: [ %s ]",
|
|
||||||
Hex.toHexString(expectedHReturn),
|
|
||||||
Hex.toHexString(actualHReturn));
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert gas
|
|
||||||
BigInteger expectedGas = new BigInteger(1, testCase.getGas());
|
|
||||||
BigInteger actualGas = new BigInteger(1, gas).subtract(BigInteger.valueOf(program.getResult().getGasUsed()));
|
|
||||||
|
|
||||||
if (!expectedGas.equals(actualGas)) {
|
|
||||||
|
|
||||||
String output =
|
|
||||||
String.format("Gas remaining is different. Expected gas remaining: [ %s ], actual gas remaining: [ %s ]",
|
|
||||||
expectedGas.toString(),
|
|
||||||
actualGas.toString());
|
|
||||||
logger.info(output);
|
|
||||||
results.add(output);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* end of if(testCase.getPost().size() == 0)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
} finally {
|
|
||||||
// repository.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public org.ethereum.core.Transaction createTransaction(Transaction tx) {
|
|
||||||
|
|
||||||
byte[] nonceBytes = ByteUtil.longToBytes(tx.nonce);
|
|
||||||
byte[] gasPriceBytes = ByteUtil.longToBytes(tx.gasPrice);
|
|
||||||
byte[] gasBytes = tx.gasLimit;
|
|
||||||
byte[] valueBytes = ByteUtil.longToBytes(tx.value);
|
|
||||||
byte[] toAddr = tx.getTo();
|
|
||||||
byte[] data = tx.getData();
|
|
||||||
|
|
||||||
org.ethereum.core.Transaction transaction = new org.ethereum.core.Transaction(
|
|
||||||
nonceBytes, gasPriceBytes, gasBytes,
|
|
||||||
toAddr, valueBytes, data);
|
|
||||||
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RepositoryImpl loadRepository(RepositoryImpl track, Map<ByteArrayWrapper, AccountState> pre) {
|
|
||||||
|
|
||||||
|
|
||||||
/* 1. Store pre-exist accounts - Pre */
|
|
||||||
for (ByteArrayWrapper key : pre.keySet()) {
|
|
||||||
|
|
||||||
AccountState accountState = pre.get(key);
|
|
||||||
byte[] addr = key.getData();
|
|
||||||
|
|
||||||
track.addBalance(addr, new BigInteger(1, accountState.getBalance()));
|
|
||||||
track.setNonce(key.getData(), new BigInteger(1, accountState.getNonce()));
|
|
||||||
|
|
||||||
track.saveCode(addr, accountState.getCode());
|
|
||||||
|
|
||||||
for (DataWord storageKey : accountState.getStorage().keySet()) {
|
|
||||||
track.addStorageRow(addr, storageKey, accountState.getStorage().get(storageKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return track;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ProgramTrace getTrace() {
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 10.07.2014
|
|
||||||
*/
|
|
||||||
public class TestSuite {
|
|
||||||
|
|
||||||
List<TestCase> testList = new ArrayList<>();
|
|
||||||
|
|
||||||
public TestSuite(JSONObject testCaseJSONObj) throws ParseException {
|
|
||||||
|
|
||||||
for (Object key : testCaseJSONObj.keySet()) {
|
|
||||||
|
|
||||||
Object testCaseJSON = testCaseJSONObj.get(key);
|
|
||||||
TestCase testCase = new TestCase(key.toString(), (JSONObject) testCaseJSON);
|
|
||||||
testList.add(testCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TestCase> getAllTests(){
|
|
||||||
return testList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<TestCase> iterator() {
|
|
||||||
return testList.iterator();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import static org.ethereum.util.ByteUtil.toHexString;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 28.06.2014
|
|
||||||
*/
|
|
||||||
public class Transaction {
|
|
||||||
|
|
||||||
byte[] data;
|
|
||||||
byte[] gasLimit;
|
|
||||||
long gasPrice;
|
|
||||||
long nonce;
|
|
||||||
byte[] secretKey;
|
|
||||||
byte[] to;
|
|
||||||
long value;
|
|
||||||
|
|
||||||
/* e.g.
|
|
||||||
"transaction" : {
|
|
||||||
"data" : "",
|
|
||||||
"gasLimit" : "10000",
|
|
||||||
"gasPrice" : "1",
|
|
||||||
"nonce" : "0",
|
|
||||||
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
|
||||||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
|
||||||
"value" : "100000"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public Transaction(JSONObject callCreateJSON) {
|
|
||||||
|
|
||||||
String dataStr = callCreateJSON.get("data").toString();
|
|
||||||
String gasLimitStr = callCreateJSON.get("gasLimit").toString();
|
|
||||||
String gasPriceStr = callCreateJSON.get("gasPrice").toString();
|
|
||||||
String nonceStr = callCreateJSON.get("nonce").toString();
|
|
||||||
String secretKeyStr = callCreateJSON.get("secretKey").toString();
|
|
||||||
String toStr = callCreateJSON.get("to").toString();
|
|
||||||
String valueStr = callCreateJSON.get("value").toString();
|
|
||||||
|
|
||||||
this.data = Utils.parseData(dataStr);
|
|
||||||
this.gasLimit = !gasLimitStr.isEmpty() ? new BigInteger(gasLimitStr).toByteArray() : new byte[]{0};
|
|
||||||
this.gasPrice = Utils.parseLong(gasPriceStr);
|
|
||||||
this.nonce = Utils.parseLong(nonceStr);
|
|
||||||
this.secretKey = Utils.parseData(secretKeyStr);
|
|
||||||
this.to = Utils.parseData(toStr);
|
|
||||||
this.value = Utils.parseLong(valueStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getGasLimit() {
|
|
||||||
return gasLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getGasPrice() {
|
|
||||||
return gasPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getNonce() {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getSecretKey() {
|
|
||||||
return secretKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getTo() {
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Transaction{" +
|
|
||||||
"data=" + toHexString(data) +
|
|
||||||
", gasLimit=" + gasLimit +
|
|
||||||
", gasPrice=" + gasPrice +
|
|
||||||
", nonce=" + nonce +
|
|
||||||
", secretKey=" + toHexString(secretKey) +
|
|
||||||
", to=" + toHexString(to) +
|
|
||||||
", value=" + value +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package org.ethereum.jsontestsuite;
|
|
||||||
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 15.12.2014
|
|
||||||
*/
|
|
||||||
public class Utils {
|
|
||||||
|
|
||||||
public static byte[] parseData(String data) {
|
|
||||||
if (data == null) return ByteUtil.EMPTY_BYTE_ARRAY;
|
|
||||||
if (data.startsWith("0x")) data = data.substring(2);
|
|
||||||
return Hex.decode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long parseLong(String data) {
|
|
||||||
return data.equals("") ? 0 : Long.parseLong(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package org.ethereum.serpent;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
|
||||||
import org.antlr.v4.runtime.BaseErrorListener;
|
|
||||||
import org.antlr.v4.runtime.CharStream;
|
|
||||||
import org.antlr.v4.runtime.CommonTokenStream;
|
|
||||||
import org.antlr.v4.runtime.DiagnosticErrorListener;
|
|
||||||
import org.antlr.v4.runtime.Lexer;
|
|
||||||
import org.antlr.v4.runtime.Parser;
|
|
||||||
import org.antlr.v4.runtime.RecognitionException;
|
|
||||||
import org.antlr.v4.runtime.Recognizer;
|
|
||||||
import org.antlr.v4.runtime.TokenStream;
|
|
||||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
|
||||||
import org.antlr.v4.runtime.misc.Nullable;
|
|
||||||
|
|
||||||
public class ParserUtils {
|
|
||||||
private ParserUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <L extends Lexer> L getLexer(Class<L> lexerClass, String source) {
|
|
||||||
CharStream input = new ANTLRInputStream(source);
|
|
||||||
L lexer;
|
|
||||||
try {
|
|
||||||
lexer = lexerClass.getConstructor(CharStream.class).newInstance(input);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("couldn't invoke lexer constructor", e);
|
|
||||||
}
|
|
||||||
lexer.addErrorListener(new AntlrFailureListener());
|
|
||||||
return lexer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <P extends Parser> P getParser(Class<? extends Lexer> lexerClass, Class<P> parserClass, String
|
|
||||||
source) {
|
|
||||||
Lexer lexer = getLexer(lexerClass, source);
|
|
||||||
TokenStream tokens = new CommonTokenStream(lexer);
|
|
||||||
|
|
||||||
P parser;
|
|
||||||
try {
|
|
||||||
parser = parserClass.getConstructor(TokenStream.class).newInstance(tokens);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("couldn't invoke parser constructor", e);
|
|
||||||
}
|
|
||||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION);
|
|
||||||
parser.removeErrorListeners(); // don't spit to stderr
|
|
||||||
parser.addErrorListener(new DiagnosticErrorListener());
|
|
||||||
parser.addErrorListener(new AntlrFailureListener());
|
|
||||||
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AntlrFailureListener extends BaseErrorListener {
|
|
||||||
@Override
|
|
||||||
public void syntaxError(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line,
|
|
||||||
int charPositionInLine, String msg, @Nullable RecognitionException e) {
|
|
||||||
throw new AntlrParseException(line, charPositionInLine, msg, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AntlrParseException extends RuntimeException {
|
|
||||||
public AntlrParseException(int line, int posInLine, String msg, Throwable cause) {
|
|
||||||
// posInLine comes in 0-indexed, but we want to 1-index it so it lines up with what editors say (they
|
|
||||||
// tend to 1-index)
|
|
||||||
super(String.format("at line %d column %d: %s", line, posInLine + 1, msg), cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
package org.ethereum.serpent;
|
|
||||||
|
|
||||||
import org.ethereum.util.ByteUtil;
|
|
||||||
import org.ethereum.vm.OpCode;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
|
|
||||||
import org.spongycastle.util.Arrays;
|
|
||||||
import org.spongycastle.util.BigIntegers;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 13.05.14
|
|
||||||
*/
|
|
||||||
public class SerpentCompiler {
|
|
||||||
|
|
||||||
public static String compile(String code) {
|
|
||||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class, SerpentParser.class,
|
|
||||||
code);
|
|
||||||
ParseTree tree = parser.parse();
|
|
||||||
|
|
||||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
|
||||||
result = result.replaceAll("\\s+", " ");
|
|
||||||
result = result.trim();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String compileFullNotion(String code) {
|
|
||||||
SerpentParser parser = ParserUtils.getParser(SerpentLexer.class,
|
|
||||||
SerpentParser.class, code);
|
|
||||||
ParseTree tree = parser.parse_init_code_block();
|
|
||||||
|
|
||||||
String result = new SerpentToAssemblyCompiler().visit(tree);
|
|
||||||
result = result.replaceAll("\\s+", " ");
|
|
||||||
result = result.trim();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] compileFullNotionAssemblyToMachine(String code) {
|
|
||||||
byte[] initCode = compileAssemblyToMachine(extractInitBlock(code));
|
|
||||||
byte[] codeCode = compileAssemblyToMachine(extractCodeBlock(code));
|
|
||||||
return encodeMachineCodeForVMRun(codeCode, initCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String extractInitBlock(String code) {
|
|
||||||
String result = "";
|
|
||||||
Pattern pattern = Pattern.compile("\\[init (.*?) init\\]");
|
|
||||||
Matcher matcher = pattern.matcher(code);
|
|
||||||
if (matcher.find()) {
|
|
||||||
result = matcher.group(1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String extractCodeBlock(String code) {
|
|
||||||
String result = "";
|
|
||||||
Pattern pattern = Pattern.compile("\\[code (.*?) code\\]");
|
|
||||||
Matcher matcher = pattern.matcher(code);
|
|
||||||
if (matcher.find()) {
|
|
||||||
result = matcher.group(1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] compileAssemblyToMachine(String code) {
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
String[] lexaArr = code.split("\\s+");
|
|
||||||
|
|
||||||
List<String> lexaList = new ArrayList<>();
|
|
||||||
Collections.addAll(lexaList, lexaArr);
|
|
||||||
|
|
||||||
// Encode push_n numbers
|
|
||||||
boolean skipping = false;
|
|
||||||
for (int i = 0; i < lexaList.size(); ++i) {
|
|
||||||
|
|
||||||
String lexa = lexaList.get(i);
|
|
||||||
|
|
||||||
{ // skipping the [asm asm] block
|
|
||||||
if (lexa.equals("asm]")) {
|
|
||||||
skipping = false;
|
|
||||||
lexaList.remove(i);
|
|
||||||
--i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (lexa.equals("[asm")) {
|
|
||||||
skipping = true;
|
|
||||||
lexaList.remove(i);
|
|
||||||
--i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (skipping)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OpCode.contains(lexa) ||
|
|
||||||
lexa.contains("REF_") ||
|
|
||||||
lexa.contains("LABEL_")) continue;
|
|
||||||
|
|
||||||
int bytesNum = ByteUtil.numBytes(lexa);
|
|
||||||
|
|
||||||
String num = lexaList.remove(i);
|
|
||||||
BigInteger bNum = new BigInteger(num);
|
|
||||||
byte[] bytes = BigIntegers.asUnsignedByteArray(bNum);
|
|
||||||
if (bytes.length == 0) bytes = new byte[]{0};
|
|
||||||
|
|
||||||
for (int j = bytes.length; j > 0; --j) {
|
|
||||||
lexaList.add(i, (bytes[j - 1] & 0xFF) + "");
|
|
||||||
}
|
|
||||||
lexaList.add(i, "PUSH" + bytesNum);
|
|
||||||
i = i + bytesNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode ref for 5 bytes
|
|
||||||
for (int i = 0; i < lexaList.size(); ++i) {
|
|
||||||
|
|
||||||
String lexa = lexaList.get(i);
|
|
||||||
if (!lexa.contains("REF_")) continue;
|
|
||||||
lexaList.add(i + 1, lexa);
|
|
||||||
lexaList.add(i + 2, lexa);
|
|
||||||
lexaList.add(i + 3, lexa);
|
|
||||||
lexaList.add(i + 4, lexa);
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc label pos & remove labels
|
|
||||||
Map<String, Integer> labels = new HashMap<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < lexaList.size(); ++i) {
|
|
||||||
|
|
||||||
String lexa = lexaList.get(i);
|
|
||||||
if (!lexa.contains("LABEL_")) continue;
|
|
||||||
|
|
||||||
String label = lexaList.remove(i);
|
|
||||||
String labelNum = label.split("LABEL_")[1];
|
|
||||||
|
|
||||||
int labelPos = i;
|
|
||||||
|
|
||||||
labels.put(labelNum, labelPos);
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode all ref occurrence
|
|
||||||
for (int i = 0; i < lexaList.size(); ++i) {
|
|
||||||
|
|
||||||
String lexa = lexaList.get(i);
|
|
||||||
if (!lexa.contains("REF_")) continue;
|
|
||||||
|
|
||||||
String ref = lexaList.remove(i);
|
|
||||||
lexaList.remove(i);
|
|
||||||
lexaList.remove(i);
|
|
||||||
lexaList.remove(i);
|
|
||||||
lexaList.remove(i);
|
|
||||||
String labelNum = ref.split("REF_")[1];
|
|
||||||
|
|
||||||
Integer pos = labels.get(labelNum);
|
|
||||||
int bytesNum = ByteUtil.numBytes(pos.toString());
|
|
||||||
|
|
||||||
lexaList.add(i, pos.toString());
|
|
||||||
|
|
||||||
for (int j = 0; j < (4 - bytesNum); ++j)
|
|
||||||
lexaList.add(i, "0");
|
|
||||||
|
|
||||||
lexaList.add(i, "PUSH4");
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String lexa : lexaList) {
|
|
||||||
|
|
||||||
if (OpCode.contains(lexa))
|
|
||||||
baos.write(OpCode.byteVal(lexa));
|
|
||||||
else {
|
|
||||||
// TODO: support for number more than one byte
|
|
||||||
baos.write(Integer.parseInt(lexa) & 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap plan
|
|
||||||
// 1) that is the code
|
|
||||||
// 2) need to wrap it with PUSH1 (codesize) PUSH1 (codestart) 000 CODECOPY 000 PUSH1 (codesize) _fURN
|
|
||||||
|
|
||||||
return baos.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return encoded bytes.
|
|
||||||
*/
|
|
||||||
public static byte[] encodeMachineCodeForVMRun(byte[] code, byte[] init) {
|
|
||||||
|
|
||||||
if (code == null || code.length == 0) throw new RuntimeException("code can't be empty code: " + code);
|
|
||||||
|
|
||||||
int numBytes = ByteUtil.numBytes(code.length + "");
|
|
||||||
byte[] lenBytes = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(code.length));
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte lenByte : lenBytes) {
|
|
||||||
sb.append(lenByte).append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc real code start position (after the init header)
|
|
||||||
int pos = 10 + numBytes * 2;
|
|
||||||
if (init != null) pos += init.length;
|
|
||||||
|
|
||||||
// @push_len @len PUSH1 @src_start PUSH1 0 CODECOPY @push_len @len 0 PUSH1 0 RETURN
|
|
||||||
String header = String.format("[asm %s %s PUSH1 %d PUSH1 0 CODECOPY %s %s PUSH1 0 RETURN asm]",
|
|
||||||
"PUSH" + numBytes, sb.toString(), pos, "PUSH" + numBytes, sb.toString());
|
|
||||||
|
|
||||||
byte[] headerMachine = compileAssemblyToMachine(header);
|
|
||||||
|
|
||||||
return init != null ? Arrays.concatenate(init, headerMachine, code) :
|
|
||||||
Arrays.concatenate(headerMachine, code);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,778 +0,0 @@
|
||||||
package org.ethereum.serpent;
|
|
||||||
|
|
||||||
import org.ethereum.crypto.HashUtil;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.misc.NotNull;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Roman Mandeleil
|
|
||||||
* @since 05.05.14
|
|
||||||
*/
|
|
||||||
public class SerpentToAssemblyCompiler extends SerpentBaseVisitor<String> {
|
|
||||||
|
|
||||||
private int labelIndex = 0;
|
|
||||||
private List<String> vars = new ArrayList<>();
|
|
||||||
|
|
||||||
private Map<String, Integer> arraysSize = new HashMap<>();
|
|
||||||
private List<String> arraysIndex = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitParse(@NotNull SerpentParser.ParseContext ctx) {
|
|
||||||
|
|
||||||
String codeBlock = visit(ctx.block());
|
|
||||||
int memSize = vars.size() * 32 - (vars.size() > 0 ? 1 : 0);
|
|
||||||
|
|
||||||
String initMemCodeBlock = "";
|
|
||||||
if (!arraysSize.isEmpty() && vars.size() > 0)
|
|
||||||
initMemCodeBlock = String.format(" 0 %d MSTORE8 ", memSize);
|
|
||||||
|
|
||||||
if (memSize == 0)
|
|
||||||
codeBlock = codeBlock.replace("@vars_table@", "0");
|
|
||||||
else
|
|
||||||
codeBlock = codeBlock.replace("@vars_table@", memSize + 1 + "");
|
|
||||||
|
|
||||||
return initMemCodeBlock + codeBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitParse_init_code_block(@NotNull SerpentParser.Parse_init_code_blockContext ctx) {
|
|
||||||
|
|
||||||
String initBlock = visit(ctx.block(0));
|
|
||||||
int memSize = vars.size() * 32 - (vars.size() > 0 ? 1 : 0);
|
|
||||||
|
|
||||||
String initMemInitBlock = "";
|
|
||||||
if (!arraysSize.isEmpty() && vars.size() > 0)
|
|
||||||
initMemInitBlock = String.format(" 0 %d MSTORE8 ", memSize);
|
|
||||||
|
|
||||||
if (memSize == 0)
|
|
||||||
initBlock = initBlock.replace("@vars_table@", "0");
|
|
||||||
else
|
|
||||||
initBlock = initBlock.replace("@vars_table@", memSize + 1 + "");
|
|
||||||
|
|
||||||
vars.clear();
|
|
||||||
String codeBlock = visit(ctx.block(1));
|
|
||||||
memSize = vars.size() * 32 - (vars.size() > 0 ? 1 : 0);
|
|
||||||
|
|
||||||
if (memSize == 0)
|
|
||||||
codeBlock = codeBlock.replace("@vars_table@", "0");
|
|
||||||
else
|
|
||||||
codeBlock = codeBlock.replace("@vars_table@", memSize + 1 + "");
|
|
||||||
|
|
||||||
|
|
||||||
String initMemCodeBlock = "";
|
|
||||||
if (!arraysSize.isEmpty() && vars.size() > 0)
|
|
||||||
initMemCodeBlock = String.format(" 0 %d MSTORE8 ", memSize);
|
|
||||||
|
|
||||||
return String.format(" [init %s %s init] [code %s %s code] ", initMemInitBlock, initBlock,
|
|
||||||
initMemCodeBlock, codeBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitIf_elif_else_stmt(@NotNull SerpentParser.If_elif_else_stmtContext ctx) {
|
|
||||||
|
|
||||||
StringBuilder retCode = new StringBuilder();
|
|
||||||
|
|
||||||
int endOfStmtLabel = labelIndex++;
|
|
||||||
|
|
||||||
// if body
|
|
||||||
SerpentParser.BlockContext ifBlock = (SerpentParser.BlockContext) ctx.getChild(4);
|
|
||||||
String blockCode = visitBlock(ifBlock);
|
|
||||||
String ifCond = visitCondition(ctx.condition(0));
|
|
||||||
|
|
||||||
int nextLabel = labelIndex++;
|
|
||||||
|
|
||||||
// if_cond [NOT REF_X JUMPI] if_body [REF_Y JUMP LABEL_X] Y=total_end
|
|
||||||
retCode.append(ifCond).
|
|
||||||
append(" NOT REF_").append(nextLabel).append(" JUMPI ").
|
|
||||||
append(blockCode).
|
|
||||||
append("REF_").append(endOfStmtLabel).
|
|
||||||
append(" JUMP LABEL_").append(nextLabel).append(" ");
|
|
||||||
|
|
||||||
// traverse the children and find out all [elif] and [else] that exist
|
|
||||||
int count = ctx.condition().size();
|
|
||||||
|
|
||||||
// traverse all 'elif' statements
|
|
||||||
int i = 1; // define i here for 'else' option
|
|
||||||
for (; i < count; ++i) {
|
|
||||||
|
|
||||||
// if the condition much at the end jump out of the main if
|
|
||||||
// append to general retCode
|
|
||||||
// TODO: [NOT REF_X JUMPI] elif_body [REF_Y JUMP LABEL_X] Y=total_end
|
|
||||||
// TODO extract the condition and the body
|
|
||||||
nextLabel = labelIndex++;
|
|
||||||
|
|
||||||
// elif condition
|
|
||||||
String elifCond = visitCondition(ctx.condition(i));
|
|
||||||
|
|
||||||
// elif body
|
|
||||||
String elifBlockCode = visitBlock(ctx.block(i));
|
|
||||||
|
|
||||||
retCode.append(elifCond).
|
|
||||||
append(" NOT REF_").append(nextLabel).append(" JUMPI ").
|
|
||||||
append(elifBlockCode).
|
|
||||||
append("REF_").append(endOfStmtLabel).append(" JUMP LABEL_").
|
|
||||||
append(nextLabel).append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if there is 'else'
|
|
||||||
if (ctx.block(i) != null) {
|
|
||||||
|
|
||||||
// body
|
|
||||||
String elseBlockCode = visitBlock(ctx.block(i));
|
|
||||||
|
|
||||||
// append to general retCode
|
|
||||||
retCode.append(elseBlockCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [LABEL_Y] Y = end of statement
|
|
||||||
retCode.append("LABEL_").append(endOfStmtLabel).append(" ");
|
|
||||||
|
|
||||||
return retCode.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitWhile_stmt(@NotNull SerpentParser.While_stmtContext ctx) {
|
|
||||||
|
|
||||||
int whileStartRef = labelIndex++;
|
|
||||||
int whileEndRef = labelIndex++;
|
|
||||||
|
|
||||||
// elif condition
|
|
||||||
SerpentParser.ConditionContext whileCondition = (SerpentParser.ConditionContext)
|
|
||||||
ctx.getChild(1);
|
|
||||||
|
|
||||||
// elif body
|
|
||||||
SerpentParser.BlockContext whileBlock = (SerpentParser.BlockContext)
|
|
||||||
ctx.getChild(4);
|
|
||||||
|
|
||||||
return String.format(" LABEL_%d %s NOT REF_%d JUMPI %s REF_%d JUMP LABEL_%d ",
|
|
||||||
whileStartRef, visitCondition(whileCondition), whileEndRef, visitBlock(whileBlock),
|
|
||||||
whileStartRef, whileEndRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock(@NotNull SerpentParser.BlockContext ctx) {
|
|
||||||
|
|
||||||
return super.visitBlock(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitCondition(@NotNull SerpentParser.ConditionContext ctx) {
|
|
||||||
return super.visitCondition(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitGet_var(@NotNull SerpentParser.Get_varContext ctx) {
|
|
||||||
|
|
||||||
String varName = ctx.VAR().toString();
|
|
||||||
int addr;
|
|
||||||
|
|
||||||
addr = vars.indexOf(varName);
|
|
||||||
if (addr == -1) {
|
|
||||||
throw new UnassignVarException(varName);
|
|
||||||
}
|
|
||||||
return String.format(" %d MLOAD ", addr * 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitAssign(@NotNull SerpentParser.AssignContext ctx) {
|
|
||||||
|
|
||||||
String varName = ctx.VAR().toString();
|
|
||||||
|
|
||||||
// msg assigned has two arrays to calc
|
|
||||||
if (ctx.msg_func() != null) {
|
|
||||||
|
|
||||||
return visitMsg_func(ctx.msg_func(), varName);
|
|
||||||
} else if (ctx.arr_def() != null) {
|
|
||||||
// if it's an array the all management is different
|
|
||||||
String arrayCode = visitArr_def(ctx.arr_def());
|
|
||||||
|
|
||||||
// calc the pointer addr
|
|
||||||
int pos = getArraySize(arrayCode);
|
|
||||||
arraysSize.put(varName, pos);
|
|
||||||
arraysIndex.add(varName);
|
|
||||||
|
|
||||||
return arrayCode;
|
|
||||||
} else {
|
|
||||||
String expression = visitExpression(ctx.expression());
|
|
||||||
int addr = vars.indexOf(varName);
|
|
||||||
if (addr == -1) {
|
|
||||||
addr = vars.size();
|
|
||||||
vars.add(varName);
|
|
||||||
}
|
|
||||||
return String.format(" %s %d MSTORE ", expression, addr * 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitContract_storage_load(@NotNull SerpentParser.Contract_storage_loadContext ctx) {
|
|
||||||
|
|
||||||
String operand0 = visitExpression(ctx.expression());
|
|
||||||
|
|
||||||
return String.format(" %s SLOAD ", operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitContract_storage_assign(@NotNull SerpentParser.Contract_storage_assignContext ctx) {
|
|
||||||
|
|
||||||
String operand0 = visitExpression(ctx.expression(0));
|
|
||||||
String operand1 = visitExpression(ctx.expression(1));
|
|
||||||
|
|
||||||
return String.format(" %s %s SSTORE ", operand1, operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitInt_val(@NotNull SerpentParser.Int_valContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.OP_NOT() != null)
|
|
||||||
return visitExpression(ctx.expression()) + " NOT";
|
|
||||||
|
|
||||||
if (ctx.expression() != null)
|
|
||||||
return visitExpression(ctx.expression());
|
|
||||||
|
|
||||||
if (ctx.get_var() != null)
|
|
||||||
return visitGet_var(ctx.get_var());
|
|
||||||
|
|
||||||
if (ctx.hex_num() != null)
|
|
||||||
return hexStringToDecimalString(ctx.hex_num().getText());
|
|
||||||
|
|
||||||
if (ctx.special_func() != null)
|
|
||||||
return visitSpecial_func(ctx.special_func());
|
|
||||||
|
|
||||||
if (ctx.msg_data() != null)
|
|
||||||
return visitMsg_data(ctx.msg_data());
|
|
||||||
|
|
||||||
if (ctx.contract_storage_load() != null)
|
|
||||||
return visitContract_storage_load(ctx.contract_storage_load());
|
|
||||||
|
|
||||||
if (ctx.send_func() != null)
|
|
||||||
return visitSend_func(ctx.send_func());
|
|
||||||
|
|
||||||
if (ctx.array_retreive() != null)
|
|
||||||
return visitArray_retreive(ctx.array_retreive());
|
|
||||||
|
|
||||||
return ctx.INT().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitArr_def(@NotNull SerpentParser.Arr_defContext ctx) {
|
|
||||||
|
|
||||||
List<SerpentParser.Int_valContext> numElements = ctx.int_val();
|
|
||||||
int arraySize = numElements.size() * 32 + 32;
|
|
||||||
|
|
||||||
StringBuffer arrayInit = new StringBuffer();
|
|
||||||
int i = 32;
|
|
||||||
for (SerpentParser.Int_valContext int_val : ctx.int_val()) {
|
|
||||||
|
|
||||||
arrayInit.append(String.format(" DUP %d ADD %s SWAP MSTORE ", i, visit(int_val)));
|
|
||||||
i += 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.format(" MSIZE 32 ADD MSIZE %s %d SWAP MSTORE ", arrayInit, arraySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitArray_assign(@NotNull SerpentParser.Array_assignContext ctx) {
|
|
||||||
|
|
||||||
int order = this.arraysIndex.indexOf(ctx.VAR().toString());
|
|
||||||
if (order == -1) {
|
|
||||||
throw new Error("array with that name was not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
//calcAllocatedBefore();
|
|
||||||
int allocSize = 0;
|
|
||||||
for (int i = 0; i < order; ++i) {
|
|
||||||
String var = arraysIndex.get(i);
|
|
||||||
allocSize += arraysSize.get(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
String index = visit(ctx.int_val());
|
|
||||||
String assignValue = visit(ctx.expression());
|
|
||||||
|
|
||||||
return String.format(" %s 32 %s MUL 32 ADD %d ADD @vars_table@ ADD MSTORE ", assignValue, index, allocSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitArray_retreive(@NotNull SerpentParser.Array_retreiveContext ctx) {
|
|
||||||
|
|
||||||
int order = this.arraysIndex.indexOf(ctx.VAR().toString());
|
|
||||||
if (order == -1) {
|
|
||||||
throw new Error("array with that name was not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
int allocSize = 32;
|
|
||||||
for (int i = 0; i < order; ++i) {
|
|
||||||
String var = arraysIndex.get(i);
|
|
||||||
allocSize += arraysSize.get(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
String index = visit(ctx.int_val());
|
|
||||||
|
|
||||||
return String.format(" 32 %s MUL %d ADD @vars_table@ ADD MLOAD ", index, allocSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitMul_expr(@NotNull SerpentParser.Mul_exprContext ctx) {
|
|
||||||
if (ctx.mul_expr() == null) return visit(ctx.int_val());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.int_val());
|
|
||||||
String operand1 = visit(ctx.mul_expr());
|
|
||||||
|
|
||||||
switch (ctx.OP_MUL().getText().toLowerCase()) {
|
|
||||||
case "*":
|
|
||||||
return operand0 + " " + operand1 + " MUL";
|
|
||||||
case "/":
|
|
||||||
return operand0 + " " + operand1 + " DIV";
|
|
||||||
case "^":
|
|
||||||
return operand0 + " " + operand1 + " EXP";
|
|
||||||
case "%":
|
|
||||||
return operand0 + " " + operand1 + " MOD";
|
|
||||||
case "#/":
|
|
||||||
return operand0 + " " + operand1 + " SDIV";
|
|
||||||
case "#%":
|
|
||||||
return operand0 + " " + operand1 + " SMOD";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_MUL().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitAdd_expr(@NotNull SerpentParser.Add_exprContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.add_expr() == null) return visit(ctx.mul_expr());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.mul_expr());
|
|
||||||
String operand1 = visit(ctx.add_expr());
|
|
||||||
|
|
||||||
switch (ctx.OP_ADD().getText().toLowerCase()) {
|
|
||||||
case "+":
|
|
||||||
return operand0 + " " + operand1 + " ADD";
|
|
||||||
case "-":
|
|
||||||
return operand0 + " " + operand1 + " SUB";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_ADD().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitRel_exp(@NotNull SerpentParser.Rel_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.rel_exp() == null) return visit(ctx.add_expr());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.rel_exp());
|
|
||||||
String operand1 = visit(ctx.add_expr());
|
|
||||||
|
|
||||||
switch (ctx.OP_REL().getText().toLowerCase()) {
|
|
||||||
case "<":
|
|
||||||
return operand1 + " " + operand0 + " LT";
|
|
||||||
case ">":
|
|
||||||
return operand1 + " " + operand0 + " GT";
|
|
||||||
case ">=":
|
|
||||||
return operand1 + " " + operand0 + " LT NOT";
|
|
||||||
case "<=":
|
|
||||||
return operand1 + " " + operand0 + " GT NOT";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_REL().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitEq_exp(@NotNull SerpentParser.Eq_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.eq_exp() == null) return visit(ctx.rel_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.rel_exp());
|
|
||||||
String operand1 = visit(ctx.eq_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_EQ().getText().toLowerCase()) {
|
|
||||||
case "==":
|
|
||||||
return operand0 + " " + operand1 + " EQ";
|
|
||||||
case "!=":
|
|
||||||
return operand0 + " " + operand1 + " EQ NOT";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_EQ().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitAnd_exp(@NotNull SerpentParser.And_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.and_exp() == null) return visit(ctx.eq_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.eq_exp());
|
|
||||||
String operand1 = visit(ctx.and_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_AND().getText().toLowerCase()) {
|
|
||||||
case "&":
|
|
||||||
return operand0 + " " + operand1 + " AND";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_AND().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitEx_or_exp(@NotNull SerpentParser.Ex_or_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.ex_or_exp() == null) return visit(ctx.and_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.and_exp());
|
|
||||||
String operand1 = visit(ctx.ex_or_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_EX_OR().getText().toLowerCase()) {
|
|
||||||
case "xor":
|
|
||||||
return operand0 + " " + operand1 + " XOR";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_EX_OR().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitIn_or_exp(@NotNull SerpentParser.In_or_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.in_or_exp() == null) return visit(ctx.ex_or_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.ex_or_exp());
|
|
||||||
String operand1 = visit(ctx.in_or_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_IN_OR().getText().toLowerCase()) {
|
|
||||||
case "|":
|
|
||||||
return operand0 + " " + operand1 + " OR";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_IN_OR().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitLog_and_exp(@NotNull SerpentParser.Log_and_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.log_and_exp() == null) return visit(ctx.in_or_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.in_or_exp());
|
|
||||||
String operand1 = visit(ctx.log_and_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_LOG_AND().getText().toLowerCase()) {
|
|
||||||
case "and":
|
|
||||||
return operand0 + " " + operand1 + " NOT NOT MUL";
|
|
||||||
case "&&":
|
|
||||||
return operand0 + " " + operand1 + " NOT NOT MUL";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_LOG_AND().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitLog_or_exp(@NotNull SerpentParser.Log_or_expContext ctx) {
|
|
||||||
|
|
||||||
if (ctx.log_or_exp() == null) return visit(ctx.log_and_exp());
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.log_and_exp());
|
|
||||||
String operand1 = visit(ctx.log_or_exp());
|
|
||||||
|
|
||||||
switch (ctx.OP_LOG_OR().getText().toLowerCase()) {
|
|
||||||
case "||":
|
|
||||||
return operand0 + " " + operand1 + " DUP 4 PC ADD JUMPI POP SWAP POP";
|
|
||||||
case "or":
|
|
||||||
return operand0 + " " + operand1 + " DUP 4 PC ADD JUMPI POP SWAP POP";
|
|
||||||
default:
|
|
||||||
throw new UnknownOperandException(ctx.OP_LOG_OR().getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitMsg_sender(@NotNull SerpentParser.Msg_senderContext ctx) {
|
|
||||||
return "CALLER";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitMsg_datasize(@NotNull SerpentParser.Msg_datasizeContext ctx) {
|
|
||||||
return "32 CALLDATASIZE DIV ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitMsg_value(@NotNull SerpentParser.Msg_valueContext ctx) {
|
|
||||||
return " CALLVALUE ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitContract_balance(@NotNull SerpentParser.Contract_balanceContext ctx) {
|
|
||||||
return " BALANCE ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitContract_address(@NotNull SerpentParser.Contract_addressContext ctx) {
|
|
||||||
return " ADDRESS ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitTx_origin(@NotNull SerpentParser.Tx_originContext ctx) {
|
|
||||||
return " ORIGIN ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_timestamp(@NotNull SerpentParser.Block_timestampContext ctx) {
|
|
||||||
return " TIMESTAMP ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_number(@NotNull SerpentParser.Block_numberContext ctx) {
|
|
||||||
return " NUMBER ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitTx_gas(@NotNull SerpentParser.Tx_gasContext ctx) {
|
|
||||||
return " GAS ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_difficulty(@NotNull SerpentParser.Block_difficultyContext ctx) {
|
|
||||||
return " DIFFICULTY ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_coinbase(@NotNull SerpentParser.Block_coinbaseContext ctx) {
|
|
||||||
return " COINBASE ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitTx_gasprice(@NotNull SerpentParser.Tx_gaspriceContext ctx) {
|
|
||||||
return " GASPRICE ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_prevhash(@NotNull SerpentParser.Block_prevhashContext ctx) {
|
|
||||||
return " PREVHASH ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitBlock_gaslimit(@NotNull SerpentParser.Block_gaslimitContext ctx) {
|
|
||||||
return " GASLIMIT ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitRet_func_1(@NotNull SerpentParser.Ret_func_1Context ctx) {
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.expression());
|
|
||||||
|
|
||||||
return String.format(" %s MSIZE SWAP MSIZE MSTORE 32 SWAP RETURN ", operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitRet_func_2(@NotNull SerpentParser.Ret_func_2Context ctx) {
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.expression(0));
|
|
||||||
String operand1 = visit(ctx.expression(1));
|
|
||||||
|
|
||||||
return String.format(" %s 32 MUL %s RETURN ", operand1, operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitSuicide_func(@NotNull SerpentParser.Suicide_funcContext ctx) {
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.expression());
|
|
||||||
|
|
||||||
return String.format(" %s SUICIDE ", operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitStop_func(@NotNull SerpentParser.Stop_funcContext ctx) {
|
|
||||||
return " STOP ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitMsg_data(@NotNull SerpentParser.Msg_dataContext ctx) {
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.expression());
|
|
||||||
|
|
||||||
return String.format("%s 32 MUL CALLDATALOAD ", operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String visitMsg_func(@NotNull SerpentParser.Msg_funcContext ctx, String varName) {
|
|
||||||
|
|
||||||
// msg_func: 'msg' '(' int_val ',' int_val ',' int_val ',' arr_def ',' int_val ',' int_val')' ;
|
|
||||||
// msg_func: 'msg' '(' [gas] ',' [to] ',' [value] ',' arr_def ',' [in_len] ',' [out_len]')' ;
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.int_val(0));
|
|
||||||
String operand1 = visit(ctx.int_val(1));
|
|
||||||
String operand2 = visit(ctx.int_val(2));
|
|
||||||
|
|
||||||
String loadInData = visit(ctx.arr_def());
|
|
||||||
|
|
||||||
String inSizeCallParam = visit(ctx.int_val(3));
|
|
||||||
String outSizeCallParam = visit(ctx.int_val(4));
|
|
||||||
|
|
||||||
// TODO: 1. allocate out_memory and push ptr
|
|
||||||
// TODO: 2. push ptr for in_memory allocated
|
|
||||||
|
|
||||||
String randomArrayName = new String(HashUtil.randomPeerId());
|
|
||||||
|
|
||||||
int inSize = Integer.parseInt(inSizeCallParam);
|
|
||||||
int outSize = Integer.parseInt(outSizeCallParam);
|
|
||||||
|
|
||||||
arraysSize.put(randomArrayName, inSize * 32 + 32);
|
|
||||||
arraysIndex.add(randomArrayName);
|
|
||||||
|
|
||||||
int outSizeVal = outSize * 32 + 32;
|
|
||||||
arraysSize.put(varName, outSize * 32 + 32);
|
|
||||||
arraysIndex.add(varName);
|
|
||||||
|
|
||||||
// [OUTDATASIZE] [OUTDATASTART] [INDATASIZE] [INDATASTART] [VALUE] [TO] [GAS] CALL
|
|
||||||
// [OUTDATASIZE] [OUTDATASTART] [INDATASIZE] [INDATASTART] ***ARR_IN_SET*** [VALUE] [TO] [GAS] CALL
|
|
||||||
//X_X = [ 32 + 128 + 6 * 32 ] = [ var_table_size + in_arr_size + out_arr_size ]
|
|
||||||
|
|
||||||
// this code allocates the memory block for the out data,
|
|
||||||
// and saves the size in typical array format [size, element_1, element_2, ...]
|
|
||||||
String outArrSet = String.format(" %d MSIZE MSTORE 0 %d MSIZE ADD MSTORE8 ", outSizeVal, outSizeVal - 32);
|
|
||||||
|
|
||||||
return String.format("%d MSIZE %s %d %s %s %s %s CALL ",
|
|
||||||
outSizeVal, outArrSet, inSize * 32, loadInData, operand2, operand1, operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitSend_func(@NotNull SerpentParser.Send_funcContext ctx) {
|
|
||||||
|
|
||||||
String operand0 = visit(ctx.int_val(0));
|
|
||||||
String operand1 = visit(ctx.int_val(1));
|
|
||||||
String operand2 = visit(ctx.int_val(2));
|
|
||||||
|
|
||||||
// OUTDATASIZE OUTDATASTART INDATASIZE INDATASTART VALUE TO GAS CALL
|
|
||||||
return String.format("0 0 0 0 %s %s %s CALL ", operand2, operand1, operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitCreate_func(@NotNull SerpentParser.Create_funcContext ctx) {
|
|
||||||
String operand0 = visit(ctx.int_val(0));
|
|
||||||
String operand1 = visit(ctx.int_val(1));
|
|
||||||
String operand2 = visit(ctx.int_val(2));
|
|
||||||
|
|
||||||
// MEM_SIZE MEM_START GAS CREATE
|
|
||||||
return String.format(" %s %s %s CREATE ", operand2, operand1, operand0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitAsm(@NotNull SerpentParser.AsmContext ctx) {
|
|
||||||
|
|
||||||
int size = ctx.asm_symbol().getChildCount();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
|
|
||||||
String symbol = ctx.asm_symbol().children.get(i).toString();
|
|
||||||
symbol = symbol.trim();
|
|
||||||
|
|
||||||
// exclude all that is not an assembly code
|
|
||||||
if (symbol.equals("[asm") || symbol.equals("asm]") || symbol.equals("\n")) continue;
|
|
||||||
|
|
||||||
boolean match = Pattern.matches("[0-9]+", symbol);
|
|
||||||
if (match) {
|
|
||||||
|
|
||||||
int byteVal = Integer.parseInt(symbol);
|
|
||||||
if (byteVal > 255 || byteVal < 0)
|
|
||||||
throw new Error("In the [asm asm] block should be placed only byte range numbers");
|
|
||||||
}
|
|
||||||
|
|
||||||
match = Pattern.matches("0[xX][0-9a-fA-F]+", symbol);
|
|
||||||
if (match) {
|
|
||||||
|
|
||||||
int byteVal = Integer.parseInt(symbol.substring(2), 16);
|
|
||||||
if (byteVal > 255 || byteVal < 0)
|
|
||||||
throw new Error("In the [asm asm] block should be placed only byte range numbers");
|
|
||||||
symbol = byteVal + "";
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(symbol).append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return "[asm " + sb.toString() + " asm]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String aggregateResult(String aggregate, String nextResult) {
|
|
||||||
return aggregate + nextResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String defaultResult() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param hexNum should be in form '0x34fabd34....'
|
|
||||||
*/
|
|
||||||
private String hexStringToDecimalString(String hexNum) {
|
|
||||||
String digits = hexNum.substring(2);
|
|
||||||
if (digits.length() % 2 != 0) digits = "0" + digits;
|
|
||||||
byte[] numberBytes = Hex.decode(digits);
|
|
||||||
return (new BigInteger(1, numberBytes)).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UnknownOperandException extends RuntimeException {
|
|
||||||
public UnknownOperandException(String name) {
|
|
||||||
super("unknown operand: " + name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UnassignVarException extends RuntimeException {
|
|
||||||
public UnassignVarException(String name) {
|
|
||||||
super("attempt to access not assigned variable: " + name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer getMsgOutputArraySize(String code) {
|
|
||||||
|
|
||||||
String result = "0";
|
|
||||||
Pattern pattern = Pattern.compile("<out_size ([0-9])* out_size>");
|
|
||||||
Matcher matcher = pattern.matcher(code.trim());
|
|
||||||
if (matcher.find()) {
|
|
||||||
|
|
||||||
String group = matcher.group(0);
|
|
||||||
result = group.replaceAll("<out_size ([0-9]*) out_size>", "$1").trim();
|
|
||||||
}
|
|
||||||
return Integer.parseInt(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer getMsgInputArraySize(String code) {
|
|
||||||
|
|
||||||
String result = "0";
|
|
||||||
Pattern pattern = Pattern.compile("<in_size ([0-9])* in_size>");
|
|
||||||
Matcher matcher = pattern.matcher(code.trim());
|
|
||||||
if (matcher.find()) {
|
|
||||||
|
|
||||||
String group = matcher.group(0);
|
|
||||||
result = group.replaceAll("<in_size ([0-9]*) in_size>", "$1").trim();
|
|
||||||
}
|
|
||||||
return Integer.parseInt(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String cleanMsgString(String code) {
|
|
||||||
return code.replaceAll("<(.*?)>", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* After the array deff code is set
|
|
||||||
* extract the size out of code string
|
|
||||||
*/
|
|
||||||
private static Integer getArraySize(String code) {
|
|
||||||
|
|
||||||
String result = "0";
|
|
||||||
Pattern pattern = Pattern.compile(" [0-9]* SWAP MSTORE$");
|
|
||||||
Matcher matcher = pattern.matcher(code.trim());
|
|
||||||
if (matcher.find()) {
|
|
||||||
|
|
||||||
String group = matcher.group(0);
|
|
||||||
result = group.replace("SWAP MSTORE", "").trim();
|
|
||||||
}
|
|
||||||
return Integer.parseInt(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue