DUMP full state each tx - awesome testing hack
This commit is contained in:
parent
a86922178f
commit
4f3e8a841d
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue