DUMP full state each tx - awesome testing hack

This commit is contained in:
romanman 2014-06-26 13:31:33 +01:00
parent a86922178f
commit 4f3e8a841d
10 changed files with 238 additions and 20 deletions

View File

@ -144,6 +144,19 @@ mvn clean package -Dmaven.test.skip=true
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
</dependency>
</dependencies>
<build>

View File

@ -27,6 +27,9 @@ public class SystemProperties {
private static String DEFAULT_SAMPLES_DIR = "samples";
private static String DEFAULT_COINBASE_SECRET = "monkey";
private static int DEFAULT_ACTIVE_PEER_CHANNEL_TIMEOUT = 5;
private static Boolean DEFAULT_DUMP_FULL = false;
private static String DEFAULT_DUMP_DIR = "dmp";
private static Boolean DEFAULT_DUMP_CLEAN_ON_RESTART = true;
public static SystemProperties CONFIG = new SystemProperties();
private Properties prop = new Properties();
@ -129,6 +132,22 @@ public class SystemProperties {
return Integer.parseInt(prop.getProperty("active.peer.channel.timeout"));
}
public Boolean dumpFull(){
if(prop.isEmpty()) return DEFAULT_DUMP_FULL;
return Boolean.parseBoolean(prop.getProperty("dump.full"));
}
public String dumpDir(){
if(prop.isEmpty()) return DEFAULT_DUMP_DIR;
return prop.getProperty("dump.dir");
}
public Boolean dumpCleanOnRestart(){
if(prop.isEmpty()) return DEFAULT_DUMP_CLEAN_ON_RESTART;
return Boolean.parseBoolean( prop.getProperty("dump.clean.on.restart") );
}
public void print() {
Enumeration<?> e = prop.propertyNames();

View File

@ -2,6 +2,7 @@ package org.ethereum.core;
import java.math.BigInteger;
import com.sun.xml.internal.fastinfoset.algorithm.BuiltInEncodingAlgorithm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.manager.WorldManager;
import org.ethereum.util.RLP;
@ -74,6 +75,8 @@ public class Genesis extends Block {
this.setStateRoot( WorldManager.instance.repository.getRootHash() );
logger.info("Genesis-hash: " + Hex.toHexString(this.getHash()));
logger.info("Genesis-stateRoot: " + Hex.toHexString(this.getStateRoot()));
WorldManager.instance.repository.dumpState(0, 0, null);
}
public static Block getInstance() {

View File

@ -1,6 +1,9 @@
package org.ethereum.db;
import com.google.common.primitives.UnsignedBytes;
import java.util.Arrays;
import java.util.Comparator;
/**
* www.ethereumJ.com
@ -9,7 +12,7 @@ import java.util.Arrays;
* Created on: 11/06/2014 15:02
*/
public class ByteArrayWrapper {
public class ByteArrayWrapper implements Comparable{
private final byte[] data;
@ -42,4 +45,12 @@ public class ByteArrayWrapper {
public byte[] getData() {
return data;
}
@Override
public int compareTo(Object second) {
Comparator<byte[]> comparator = UnsignedBytes.lexicographicalComparator();
return comparator.compare(this.data, ((ByteArrayWrapper)second).getData());
}
}

View File

@ -4,6 +4,11 @@ import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.ethereum.config.SystemProperties;
import org.iq80.leveldb.DB;
@ -11,6 +16,7 @@ import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
/**
* Generic interface for Ethereum database
@ -92,4 +98,20 @@ public class DatabaseImpl implements Database{
}
}
public ArrayList<ByteArrayWrapper> dumpKeys(){
DBIterator iterator = getDb().iterator();
ArrayList<ByteArrayWrapper> keys = new ArrayList<>();
while(iterator.hasNext()){
ByteArrayWrapper key = new ByteArrayWrapper(iterator.next().getKey());
keys.add(key);
}
Collections.sort((List)keys);
return keys;
}
}

View File

@ -1,7 +1,9 @@
package org.ethereum.db;
import org.codehaus.plexus.util.FileUtils;
import org.ethereum.core.AccountState;
import org.ethereum.crypto.HashUtil;
import org.ethereum.json.JSONHelper;
import org.ethereum.trie.TrackTrie;
import org.ethereum.trie.Trie;
import org.ethereum.vm.DataWord;
@ -9,7 +11,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Map;
import static org.ethereum.config.SystemProperties.CONFIG;
/**
*
@ -267,4 +277,65 @@ public class Repository {
if (this.detailsDB != null)
detailsDB.close();
}
public void dumpState(long blockNumber, int txNumber, String txHash){
if (!CONFIG.dumpFull()) return;
if (txHash == null)
if (CONFIG.dumpCleanOnRestart()){
try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
}
String dir = CONFIG.dumpDir() + "/";
String fileName = "0.dmp";
if (txHash != null)
fileName =
String.format("%d_%d_%s.dmp",
blockNumber, txNumber, txHash.substring(0, 8));
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName);
try {
dumpFile.getParentFile().mkdirs();
dumpFile.createNewFile();
FileWriter fw = new FileWriter(dumpFile.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
ArrayList<ByteArrayWrapper> keys = this.detailsDB.dumpKeys();
// dump json file
for (ByteArrayWrapper key : keys){
byte[] keyBytes = key.getData();
AccountState state = getAccountState(keyBytes);
ContractDetails details = getContractDetails(keyBytes);
BigInteger nonce = state.getNonce();
BigInteger balance = state.getBalance();
byte[] stateRoot = state.getStateRoot();
byte[] codeHash = state.getCodeHash();
byte[] code = details.getCode();
Map<DataWord, DataWord> storage = details.getStorage();
String accountLine = JSONHelper.dumpLine(key.getData(), nonce.toByteArray(),
balance.toByteArray(), stateRoot, codeHash, code, storage);
bw.write(accountLine);
bw.write("\n");
// {address: x, nonce: n1, balance: b1, stateRoot: s1, codeHash: c1, code: c2, sotrage: [key: k1, value: v1, key:k2, value: v2 ] }
}
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,62 @@
package org.ethereum.json;
import org.ethereum.vm.DataWord;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.*;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 26/06/2014 10:08
*/
public class JSONHelper {
public static String dumpLine(byte[] address, byte[] nonce, byte[] balance, byte[] stateRoot,
byte[] codeHash, byte[] code, Map<DataWord, DataWord> storageMap){
// {address: x, nonce: n1, balance: b1, stateRoot: s1, codeHash: c1, code: c2, sotrage: [key: k1, value: v1, key:k2, value: v2 ] }
ArrayList<DataWord> storageKeys = new ArrayList(storageMap.keySet());
Collections.sort((List)storageKeys);
Map outMap = new LinkedHashMap();
for (DataWord key : storageKeys){
outMap.put(key.getNoLeadZeroesData(), storageMap.get(key).getNoLeadZeroesData());
}
String mapString = JSONValue.toJSONString(outMap);
mapString = mapString.replace("\"", "");
JSONArray orderFields = new JSONArray();
orderFields.add("address: " + Hex.toHexString(address));
orderFields.add(" nonce: " + Hex.toHexString(nonce));
orderFields.add(" balance: " + Hex.toHexString(balance));
orderFields.add(" stateRoot: " + (stateRoot == null ? "" : Hex.toHexString(stateRoot)));
orderFields.add(" codeHash: " + (codeHash == null ? "" : Hex.toHexString(codeHash)));
orderFields.add(" code: " + (code == null ? "" : Hex.toHexString(code)));
orderFields.add(" storage: " + mapString);
String out = orderFields.toString();
out = out.replace("\"", "");
return out;
}
}

View File

@ -1,5 +1,6 @@
package org.ethereum.manager;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.*;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
@ -294,14 +295,19 @@ public class WorldManager {
}
public void applyTransactionList(List<Transaction> txList) {
for (Transaction tx : txList){
applyTransaction(tx);
}
}
public void applyBlock(Block block) {
int i = 0;
List<Transaction> txList = block.getTransactionsList();
applyTransactionList(txList);
for (Transaction tx : txList){
applyTransaction(tx);
repository.dumpState(block.getNumber(), i, Hex.toHexString(tx.getHash()));
++i;
}
}
public void applyBlockList(List<Block> blocks) {

View File

@ -24,12 +24,12 @@ peer.discovery.port = 30303
#peer.active.port = 30303
# RomanJ general
peer.active.ip = 54.211.14.10
peer.active.port = 50505
#peer.active.ip = 54.211.14.10
#peer.active.port = 30303
#poc5.testnet.ethereum.org
#peer.active.ip = 54.72.69.180
#peer.active.port = 30303
peer.active.ip = 54.72.69.180
peer.active.port = 30303
#peer.active.ip = 151.64.223.120
#peer.active.port = 30304
@ -81,3 +81,14 @@ database.reset = true
# to be eventually the address
# that get the miner reward
coinbase.secret = "monkey"
# for testing purposes
# all the state will be dumped
# in JSON form to [dump.dir]
# if [dump.full] = true
# posible values true/false
dump.full = true
dump.dir = dmp
# clean the dump dir each start
dump.clean.on.restart = true

View File

@ -14,8 +14,8 @@ log4j.logger.java.nio = FATAL
log4j.logger.io.netty = FATAL
log4j.logger.org.ethereum.core = FATAL
log4j.logger.wire = FATAL
log4j.logger.VM = FATAL
log4j.logger.main = FATAL
log4j.logger.VM = DEBUG
log4j.logger.main = DEBUG
log4j.logger.state = FATAL
log4j.logger.blockchain = FATAL
log4j.logger.ui = FATAL