Repository redesign:
+ clean and structured design of the tracker + batch write to the db at end of the process + record blocks for runnable test scenario
This commit is contained in:
parent
187fcef1c4
commit
8bf61332ae
|
@ -18,7 +18,7 @@ import org.springframework.stereotype.Component;
|
|||
*/
|
||||
public class SystemProperties {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SystemProperties.class);
|
||||
private static Logger logger = LoggerFactory.getLogger("general");
|
||||
|
||||
private static int DEFAULT_TX_APPROVE_TIMEOUT = 10;
|
||||
private static String DEFAULT_DISCOVERY_PEER_LIST = "poc-7.ethdev.com:30303";
|
||||
|
@ -29,6 +29,7 @@ public class SystemProperties {
|
|||
private static int DEFAULT_ACTIVE_PEER_CHANNEL_TIMEOUT = 5;
|
||||
private static Boolean DEFAULT_DB_RESET = false;
|
||||
private static Boolean DEFAULT_DUMP_FULL = false;
|
||||
private static Boolean DEFAULT_RECORD_BLOCKS = false;
|
||||
private static String DEFAULT_DUMP_DIR = "dmp";
|
||||
private static String DEFAULT_DUMP_STYLE = "standard+";
|
||||
private static Integer DEFAULT_VMTRACE_BLOCK = 0;
|
||||
|
@ -61,6 +62,7 @@ public class SystemProperties {
|
|||
File file = new File(fileName);
|
||||
|
||||
if (file.exists()) {
|
||||
logger.info("config loaded from {}", fileName);
|
||||
input = new FileInputStream(file);
|
||||
} else {
|
||||
fileName = "system.properties";
|
||||
|
@ -159,6 +161,12 @@ public class SystemProperties {
|
|||
return Integer.parseInt(prop.getProperty("trace.startblock"));
|
||||
}
|
||||
|
||||
public Boolean recordBlocks() {
|
||||
if (prop.isEmpty()) return DEFAULT_RECORD_BLOCKS;
|
||||
return Boolean.parseBoolean(prop.getProperty("record.blocks"));
|
||||
}
|
||||
|
||||
|
||||
public Boolean dumpFull() {
|
||||
if (prop.isEmpty()) return DEFAULT_DUMP_FULL;
|
||||
return Boolean.parseBoolean(prop.getProperty("dump.full"));
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.ethereum.core;
|
||||
|
||||
import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;
|
||||
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
|
||||
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;
|
||||
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
|
||||
|
||||
public class AccountState {
|
||||
|
||||
private byte[] rlpEncoded;
|
||||
|
@ -41,6 +41,10 @@ public class AccountState {
|
|||
* retrieval */
|
||||
private byte[] codeHash = EMPTY_DATA_HASH;
|
||||
|
||||
private boolean dirty = false;
|
||||
private boolean deleted = false;
|
||||
|
||||
|
||||
public AccountState() {
|
||||
this(BigInteger.ZERO, BigInteger.ZERO);
|
||||
}
|
||||
|
@ -66,6 +70,10 @@ public class AccountState {
|
|||
return nonce;
|
||||
}
|
||||
|
||||
public void setNonce(BigInteger nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
public byte[] getStateRoot() {
|
||||
return stateRoot;
|
||||
}
|
||||
|
@ -73,11 +81,13 @@ public class AccountState {
|
|||
public void setStateRoot(byte[] stateRoot) {
|
||||
rlpEncoded = null;
|
||||
this.stateRoot = stateRoot;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
public void incrementNonce() {
|
||||
rlpEncoded = null;
|
||||
this.nonce = nonce.add(BigInteger.ONE);
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
public byte[] getCodeHash() {
|
||||
|
@ -96,12 +106,14 @@ public class AccountState {
|
|||
public BigInteger addToBalance(BigInteger value) {
|
||||
if (value.signum() != 0) rlpEncoded = null;
|
||||
this.balance = balance.add(value);
|
||||
setDirty(true);
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
public void subFromBalance(BigInteger value) {
|
||||
if (value.signum() != 0) rlpEncoded = null;
|
||||
this.balance = balance.subtract(value);
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
public byte[] getEncoded() {
|
||||
|
@ -115,6 +127,33 @@ public class AccountState {
|
|||
return rlpEncoded;
|
||||
}
|
||||
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public AccountState clone(){
|
||||
AccountState accountState = new AccountState();
|
||||
|
||||
accountState.addToBalance(this.getBalance());
|
||||
accountState.setNonce(this.getNonce());
|
||||
accountState.setCodeHash(this.getCodeHash());
|
||||
accountState.setStateRoot(this.getStateRoot());
|
||||
|
||||
return accountState;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String ret = "Nonce: " + this.getNonce().toString() + "\n" +
|
||||
"Balance: " + Denomination.toFriendlyString(getBalance()) + "\n" +
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package org.ethereum.core;
|
||||
|
||||
import org.apache.commons.collections4.BidiMap;
|
||||
import org.apache.commons.collections4.bidimap.DualTreeBidiMap;
|
||||
import org.codehaus.plexus.util.FileUtils;
|
||||
import org.ethereum.db.BlockStore;
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.db.RepositoryImpl;
|
||||
import org.ethereum.facade.Blockchain;
|
||||
import org.ethereum.facade.Repository;
|
||||
import org.ethereum.listener.EthereumListener;
|
||||
|
@ -12,7 +9,7 @@ import org.ethereum.manager.WorldManager;
|
|||
import org.ethereum.net.BlockQueue;
|
||||
import org.ethereum.net.server.ChannelManager;
|
||||
import org.ethereum.util.AdvancedDeviceUtils;
|
||||
import org.ethereum.util.FastByteComparisons;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.vm.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -20,11 +17,19 @@ import org.spongycastle.util.encoders.Hex;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
import static org.ethereum.core.Denomination.SZABO;
|
||||
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||
|
||||
/**
|
||||
* The Ethereum blockchain is in many ways similar to the Bitcoin blockchain,
|
||||
|
@ -70,6 +75,7 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
@Autowired
|
||||
private Repository repository;
|
||||
private Repository track;
|
||||
|
||||
@Autowired
|
||||
private BlockStore blockStore;
|
||||
|
@ -94,11 +100,6 @@ public class BlockchainImpl implements Blockchain {
|
|||
private List<Chain> altChains = new ArrayList<>();
|
||||
private List<Block> garbage = new ArrayList<>();
|
||||
|
||||
public BlockchainImpl(){}
|
||||
public BlockchainImpl(Repository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGasPrice() {
|
||||
// In case of the genesis block we don't want to rely on the min gas price
|
||||
|
@ -133,12 +134,17 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
public void tryToConnect(Block block){
|
||||
|
||||
recordBlock(block);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Try connect block: {}",
|
||||
Hex.toHexString(block.getEncoded()));
|
||||
|
||||
if (blockStore.getBlockByHash(block.getHash()) != null){
|
||||
// retry of well known block
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// The simple case got the block
|
||||
// to connect to the main chain
|
||||
if (bestBlock.isParentOf(block)){
|
||||
|
@ -189,13 +195,7 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
// if there is too much garbage ask for re-sync
|
||||
if (garbage.size() > 20){
|
||||
blockQueue.clear();
|
||||
totalDifficulty = BigInteger.ZERO;
|
||||
bestBlock = Genesis.getInstance();
|
||||
this.repository.close();
|
||||
this.repository = new RepositoryImpl();
|
||||
garbage.clear();
|
||||
altChains.clear();
|
||||
worldManager.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,7 @@ public class BlockchainImpl implements Blockchain {
|
|||
@Override
|
||||
public void add(Block block) {
|
||||
|
||||
track = repository.startTracking();
|
||||
if (block == null)
|
||||
return;
|
||||
|
||||
|
@ -215,6 +216,7 @@ public class BlockchainImpl implements Blockchain {
|
|||
}
|
||||
|
||||
this.processBlock(block);
|
||||
track.commit();
|
||||
|
||||
// Remove all wallet transactions as they already approved by the net
|
||||
worldManager.getWallet().removeTransactions(block.getTransactionsList());
|
||||
|
@ -332,6 +334,8 @@ public class BlockchainImpl implements Blockchain {
|
|||
this.addReward(block);
|
||||
this.updateTotalDifficulty(block);
|
||||
|
||||
track.commit();
|
||||
|
||||
if(block.getNumber() >= CONFIG.traceStartBlock())
|
||||
repository.dumpState(block, totalGasUsed, 0, null);
|
||||
}
|
||||
|
@ -343,9 +347,6 @@ public class BlockchainImpl implements Blockchain {
|
|||
* @param block object containing the header and uncles
|
||||
*/
|
||||
private void addReward(Block block) {
|
||||
// Create coinbase if doesn't exist yet
|
||||
if (repository.getAccountState(block.getCoinbase()) == null)
|
||||
repository.createAccount(block.getCoinbase());
|
||||
|
||||
// Add standard block reward
|
||||
BigInteger totalBlockReward = Block.BLOCK_REWARD;
|
||||
|
@ -353,12 +354,12 @@ public class BlockchainImpl implements Blockchain {
|
|||
// Add extra rewards based on number of uncles
|
||||
if(block.getUncleList().size() > 0) {
|
||||
for (BlockHeader uncle : block.getUncleList()) {
|
||||
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
|
||||
track.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
|
||||
}
|
||||
totalBlockReward = totalBlockReward.add(Block.INCLUSION_REWARD
|
||||
.multiply(BigInteger.valueOf(block.getUncleList().size())));
|
||||
}
|
||||
repository.addBalance(block.getCoinbase(), totalBlockReward);
|
||||
track.addBalance(block.getCoinbase(), totalBlockReward);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -367,18 +368,20 @@ public class BlockchainImpl implements Blockchain {
|
|||
/* Debug check to see if the state is still as expected */
|
||||
if(logger.isWarnEnabled()) {
|
||||
String blockStateRootHash = Hex.toHexString(block.getStateRoot());
|
||||
String worldStateRootHash = Hex.toHexString(worldManager.getRepository().getWorldState().getRootHash());
|
||||
String worldStateRootHash = Hex.toHexString(repository.getRoot());
|
||||
if(!blockStateRootHash.equals(worldStateRootHash)){
|
||||
|
||||
stateLogger.warn("BLOCK: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
|
||||
// repository.close();
|
||||
// System.exit(-1); // Don't add block
|
||||
|
||||
// in case of rollback hard move the root
|
||||
// Block parentBlock = blockStore.getBlockByHash(block.getParentHash());
|
||||
// repository.syncToRoot(parentBlock.getStateRoot());
|
||||
// todo: after the rollback happens other block should be requested
|
||||
}
|
||||
}
|
||||
|
||||
blockStore.saveBlock(block);
|
||||
this.setBestBlock(block);
|
||||
repository.getWorldState().sync();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("block added to the blockChain: index: [{}]", block.getNumber());
|
||||
|
@ -397,20 +400,17 @@ public class BlockchainImpl implements Blockchain {
|
|||
*/
|
||||
public long applyTransaction(Block block, Transaction tx) {
|
||||
|
||||
logger.info("applyTransaction: [{}]", Hex.toHexString(tx.getHash()));
|
||||
|
||||
byte[] coinbase = block.getCoinbase();
|
||||
|
||||
// VALIDATE THE SENDER
|
||||
byte[] senderAddress = tx.getSender();
|
||||
AccountState senderAccount = repository.getAccountState(senderAddress);
|
||||
if (senderAccount == null) {
|
||||
if (stateLogger.isWarnEnabled())
|
||||
stateLogger.warn("No such address: {}",
|
||||
Hex.toHexString(senderAddress));
|
||||
return 0;
|
||||
}
|
||||
// AccountState senderAccount = repository.getAccountState(senderAddress);
|
||||
logger.info("tx.sender: [{}]", Hex.toHexString(tx.getSender()));
|
||||
|
||||
// VALIDATE THE NONCE
|
||||
BigInteger nonce = senderAccount.getNonce();
|
||||
BigInteger nonce = track.getNonce(senderAddress);
|
||||
BigInteger txNonce = new BigInteger(1, tx.getNonce());
|
||||
if (nonce.compareTo(txNonce) != 0) {
|
||||
if (stateLogger.isWarnEnabled())
|
||||
|
@ -420,7 +420,8 @@ public class BlockchainImpl implements Blockchain {
|
|||
}
|
||||
|
||||
// UPDATE THE NONCE
|
||||
repository.increaseNonce(senderAddress);
|
||||
track.increaseNonce(senderAddress);
|
||||
logger.info("increased tx.nonce to: [{}]", track.getNonce(senderAddress));
|
||||
|
||||
// FIND OUT THE TRANSACTION TYPE
|
||||
byte[] receiverAddress, code = null;
|
||||
|
@ -430,31 +431,25 @@ public class BlockchainImpl implements Blockchain {
|
|||
code = tx.getData(); // init code
|
||||
} else {
|
||||
receiverAddress = tx.getReceiveAddress();
|
||||
if (repository.getAccountState(receiverAddress) == null) {
|
||||
repository.createAccount(receiverAddress);
|
||||
if (stateLogger.isDebugEnabled())
|
||||
stateLogger.debug("new receiver account created address={}",
|
||||
Hex.toHexString(receiverAddress));
|
||||
} else {
|
||||
code = repository.getCode(receiverAddress);
|
||||
if (code != null) {
|
||||
code = track.getCode(receiverAddress);
|
||||
if (code != EMPTY_BYTE_ARRAY) {
|
||||
if (stateLogger.isDebugEnabled())
|
||||
stateLogger.debug("calling for existing contract: address={}",
|
||||
Hex.toHexString(receiverAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// THE SIMPLE VALUE/BALANCE CHANGE
|
||||
boolean isValueTx = tx.getValue() != null;
|
||||
if (isValueTx) {
|
||||
BigInteger txValue = new BigInteger(1, tx.getValue());
|
||||
if (senderAccount.getBalance().compareTo(txValue) >= 0) {
|
||||
senderAccount.subFromBalance(txValue); // balance will be read again below
|
||||
repository.addBalance(senderAddress, txValue.negate());
|
||||
if (track.getBalance(senderAddress).compareTo(txValue) >= 0) {
|
||||
|
||||
if(!isContractCreation) // adding to new contract could be reverted
|
||||
repository.addBalance(receiverAddress, txValue);
|
||||
track.addBalance(receiverAddress, txValue); // balance will be read again below
|
||||
track.addBalance(senderAddress, txValue.negate());
|
||||
|
||||
// if(!isContractCreation) // adding to new contract could be reverted
|
||||
// track.addBalance(receiverAddress, txValue); todo: find out what is that ?
|
||||
|
||||
if (stateLogger.isDebugEnabled())
|
||||
stateLogger.debug("Update value balance \n "
|
||||
|
@ -469,22 +464,23 @@ public class BlockchainImpl implements Blockchain {
|
|||
// TODO: performance improve multiply without BigInteger
|
||||
BigInteger gasPrice = new BigInteger(1, tx.getGasPrice());
|
||||
BigInteger gasDebit = new BigInteger(1, tx.getGasLimit()).multiply(gasPrice);
|
||||
logger.info("Gas price limited to [{} wei]", gasDebit.toString());
|
||||
|
||||
// Debit the actual total gas value from the sender
|
||||
// the purchased gas will be available for
|
||||
// the contract in the execution state,
|
||||
// it can be retrieved using GAS op
|
||||
if (gasDebit.signum() == 1) {
|
||||
if (senderAccount.getBalance().compareTo(gasDebit) == -1) {
|
||||
if (track.getBalance(senderAddress).compareTo(gasDebit) == -1) {
|
||||
logger.debug("No gas to start the execution: sender={}",
|
||||
Hex.toHexString(senderAddress));
|
||||
return 0;
|
||||
}
|
||||
repository.addBalance(senderAddress, gasDebit.negate());
|
||||
track.addBalance(senderAddress, gasDebit.negate());
|
||||
|
||||
// The coinbase get the gas cost
|
||||
if (coinbase != null)
|
||||
repository.addBalance(coinbase, gasDebit);
|
||||
track.addBalance(coinbase, gasDebit);
|
||||
|
||||
if (stateLogger.isDebugEnabled())
|
||||
stateLogger.debug(
|
||||
|
@ -495,19 +491,19 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
// CREATE AND/OR EXECUTE CONTRACT
|
||||
long gasUsed = 0;
|
||||
if (isContractCreation || code != null) {
|
||||
if (isContractCreation || code != EMPTY_BYTE_ARRAY) {
|
||||
|
||||
// START TRACKING FOR REVERT CHANGES OPTION
|
||||
Repository trackRepository = repository.getTrack();
|
||||
trackRepository.startTracking();
|
||||
Repository trackTx = track.startTracking();
|
||||
logger.info("Start tracking VM run");
|
||||
try {
|
||||
|
||||
// CREATE NEW CONTRACT ADDRESS AND ADD TX VALUE
|
||||
if(isContractCreation) {
|
||||
if (isValueTx) // adding to balance also creates the account
|
||||
trackRepository.addBalance(receiverAddress, new BigInteger(1, tx.getValue()));
|
||||
trackTx.addBalance(receiverAddress, new BigInteger(1, tx.getValue()));
|
||||
else
|
||||
trackRepository.createAccount(receiverAddress);
|
||||
trackTx.createAccount(receiverAddress);
|
||||
|
||||
if(stateLogger.isDebugEnabled())
|
||||
stateLogger.debug("new contract created address={}",
|
||||
|
@ -517,7 +513,7 @@ public class BlockchainImpl implements Blockchain {
|
|||
Block currBlock = (block == null) ? this.getBestBlock() : block;
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
programInvokeFactory.createProgramInvoke(tx, currBlock, trackRepository);
|
||||
programInvokeFactory.createProgramInvoke(tx, currBlock, trackTx);
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(code, programInvoke);
|
||||
|
@ -527,15 +523,15 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
program.saveProgramTraceToFile(Hex.toHexString(tx.getHash()));
|
||||
ProgramResult result = program.getResult();
|
||||
applyProgramResult(result, gasDebit, gasPrice, trackRepository,
|
||||
applyProgramResult(result, gasDebit, gasPrice, trackTx,
|
||||
senderAddress, receiverAddress, coinbase, isContractCreation);
|
||||
gasUsed = result.getGasUsed();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
trackRepository.rollback();
|
||||
trackTx.rollback();
|
||||
return new BigInteger(1, tx.getGasLimit()).longValue();
|
||||
}
|
||||
trackRepository.commit();
|
||||
trackTx.commit();
|
||||
} else {
|
||||
// REFUND GASDEBIT EXCEPT FOR FEE (500 + 5*TXDATA)
|
||||
long dataCost = tx.getData() == null ? 0: tx.getData().length * GasCost.TXDATA;
|
||||
|
@ -543,8 +539,8 @@ public class BlockchainImpl implements Blockchain {
|
|||
|
||||
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(gasUsed).multiply(gasPrice));
|
||||
if (refund.signum() > 0) {
|
||||
repository.addBalance(senderAddress, refund);
|
||||
repository.addBalance(coinbase, refund.negate());
|
||||
track.addBalance(senderAddress, refund);
|
||||
track.addBalance(coinbase, refund.negate());
|
||||
}
|
||||
}
|
||||
return gasUsed;
|
||||
|
@ -665,4 +661,53 @@ public class BlockchainImpl implements Blockchain {
|
|||
public void setTotalDifficulty(BigInteger totalDifficulty) {
|
||||
this.totalDifficulty = totalDifficulty;
|
||||
}
|
||||
|
||||
private void recordBlock(Block block){
|
||||
|
||||
if (!CONFIG.recordBlocks()) return;
|
||||
|
||||
if (bestBlock.isGenesis()){
|
||||
try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
|
||||
}
|
||||
|
||||
String dir = CONFIG.dumpDir() + "/";
|
||||
|
||||
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + "_blocks_rec.txt");
|
||||
FileWriter fw = null;
|
||||
BufferedWriter bw = null;
|
||||
|
||||
try {
|
||||
|
||||
dumpFile.getParentFile().mkdirs();
|
||||
if (!dumpFile.exists()) dumpFile.createNewFile();
|
||||
|
||||
fw = new FileWriter(dumpFile.getAbsoluteFile(), true);
|
||||
bw = new BufferedWriter(fw);
|
||||
|
||||
if (bestBlock.isGenesis()){
|
||||
bw.write(Hex.toHexString(bestBlock.getEncoded()));
|
||||
bw.write("\n");
|
||||
}
|
||||
|
||||
bw.write(Hex.toHexString(block.getEncoded()));
|
||||
bw.write("\n");
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (bw != null) bw.close();
|
||||
if (fw != null) fw.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ public class Transaction {
|
|||
* (including public key recovery bits) */
|
||||
private ECDSASignature signature;
|
||||
|
||||
private byte[] sendAddress;
|
||||
|
||||
/* Tx in encoded form */
|
||||
private byte[] rlpEncoded;
|
||||
private byte[] rlpRaw;
|
||||
|
@ -107,6 +109,9 @@ public class Transaction {
|
|||
byte[] r = ((RLPItem) transaction.get(7)).getRLPData();
|
||||
byte[] s = ((RLPItem) transaction.get(8)).getRLPData();
|
||||
this.signature = ECDSASignature.fromComponents(r, s, v);
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
logger.debug("RLP encoded tx is not signed!");
|
||||
}
|
||||
|
@ -182,8 +187,11 @@ public class Transaction {
|
|||
|
||||
public byte[] getSender() {
|
||||
try {
|
||||
if (sendAddress == null) {
|
||||
ECKey key = ECKey.signatureToKey(getHash(), getSignature().toBase64());
|
||||
return key.getAddress();
|
||||
sendAddress = key.getAddress();
|
||||
}
|
||||
return sendAddress;
|
||||
} catch (SignatureException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ import java.util.Map;
|
|||
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPElement;
|
||||
import org.ethereum.util.RLPItem;
|
||||
import org.ethereum.util.RLPList;
|
||||
import org.ethereum.util.*;
|
||||
import org.ethereum.vm.DataWord;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +22,10 @@ public class ContractDetails {
|
|||
private List<DataWord> storageKeys = new ArrayList<>();
|
||||
private List<DataWord> storageValues = new ArrayList<>();
|
||||
|
||||
private byte[] code;
|
||||
private byte[] code = ByteUtil.EMPTY_BYTE_ARRAY;
|
||||
|
||||
private boolean dirty = true;
|
||||
private boolean deleted = false;
|
||||
|
||||
private Trie storageTrie = new TrieImpl(null);
|
||||
|
||||
|
@ -61,6 +61,7 @@ public class ContractDetails {
|
|||
storageValues.add(value);
|
||||
}
|
||||
|
||||
this.setDirty(true);
|
||||
this.rlpEncoded = null;
|
||||
}
|
||||
|
||||
|
@ -82,6 +83,7 @@ public class ContractDetails {
|
|||
|
||||
public void setCode(byte[] code) {
|
||||
this.code = code;
|
||||
this.setDirty(true);
|
||||
this.rlpEncoded = null;
|
||||
}
|
||||
|
||||
|
@ -125,10 +127,27 @@ public class ContractDetails {
|
|||
storageTrie.update(key.getData(), RLP.encodeElement(value.getNoLeadZeroesData()));
|
||||
}
|
||||
|
||||
this.code = code.getRLPData();
|
||||
this.code = (code.getRLPData() == null) ? ByteUtil.EMPTY_BYTE_ARRAY : code.getRLPData();
|
||||
this.rlpEncoded = rlpCode;
|
||||
}
|
||||
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
|
||||
public byte[] getEncoded() {
|
||||
|
||||
if (rlpEncoded == null) {
|
||||
|
@ -164,5 +183,21 @@ public class ContractDetails {
|
|||
return Collections.unmodifiableMap(storage);
|
||||
}
|
||||
|
||||
public void setStorage(List<DataWord> storageKeys, List<DataWord> storageValues) {
|
||||
this.storageKeys = storageKeys;
|
||||
this.storageValues = storageValues;
|
||||
}
|
||||
|
||||
|
||||
public ContractDetails clone(){
|
||||
|
||||
ContractDetails contractDetails = new ContractDetails();
|
||||
|
||||
contractDetails.setCode(this.getCode());
|
||||
contractDetails.setStorage(new ArrayList<DataWord>(this.storageKeys) ,
|
||||
new ArrayList<DataWord>(this.storageValues));
|
||||
return contractDetails;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -89,11 +89,19 @@ public class DatabaseImpl implements Database {
|
|||
|
||||
@Override
|
||||
public void put(byte[] key, byte[] value) {
|
||||
|
||||
if(logger.isDebugEnabled())
|
||||
logger.debug("put: key: [{}], value: [{}]",
|
||||
Hex.toHexString(key),
|
||||
Hex.toHexString(value));
|
||||
db.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(byte[] key) {
|
||||
if(logger.isDebugEnabled())
|
||||
logger.debug("delete: key: [{}]");
|
||||
|
||||
db.delete(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,16 +5,14 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
|||
import org.codehaus.plexus.util.FileUtils;
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.facade.Repository;
|
||||
import org.ethereum.json.EtherObjectMapper;
|
||||
import org.ethereum.json.JSONHelper;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
import org.iq80.leveldb.WriteBatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
@ -25,90 +23,60 @@ import java.io.File;
|
|||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
import static org.ethereum.crypto.SHA3Helper.*;
|
||||
|
||||
/**
|
||||
*
|
||||
***********************************************************************************
|
||||
Repository<br>
|
||||
|<br>
|
||||
--> AccountState ---> Trie ---> leveldb (state) /key=address<br>
|
||||
--> nonce<br>
|
||||
--> balance<br>
|
||||
--> stateRoot<br>
|
||||
--> codeHash<br>
|
||||
|<br>
|
||||
--> ContractDetails ---> leveldb(details) /key=address<br>
|
||||
--> code ---> sha3(code) // saved into AccountInfo.codeHash<br>
|
||||
--> storage ---> Trie // to calculate the AccountInfo.stateRoot<br>
|
||||
***********************************************************************************
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
* www.etherj.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 23/06/2014 23:01
|
||||
* Created on: 17/11/2014 21:15
|
||||
*/
|
||||
@Component
|
||||
public class RepositoryImpl implements Repository {
|
||||
|
||||
final static String DETAILS_DB = "details";
|
||||
final static String STATE_DB = "state";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger("repository");
|
||||
|
||||
private Trie worldState;
|
||||
private TrackTrie accountStateDB;
|
||||
private TrackDatabase contractDetailsDB;
|
||||
|
||||
// TODO: Listeners listeners
|
||||
// TODO: cash impl
|
||||
|
||||
private DatabaseImpl detailsDB = null;
|
||||
private DatabaseImpl stateDB = null;
|
||||
|
||||
/**
|
||||
* Create a new Repository DAO
|
||||
* assuming empty db and thus no stateRoot
|
||||
*
|
||||
*/
|
||||
public RepositoryImpl() {
|
||||
this("details", "state");
|
||||
this(DETAILS_DB, STATE_DB);
|
||||
}
|
||||
|
||||
public RepositoryImpl(String detailsDbName, String stateDbName) {
|
||||
detailsDB = new DatabaseImpl(detailsDbName);
|
||||
contractDetailsDB = new TrackDatabase(detailsDB);
|
||||
stateDB = new DatabaseImpl(stateDbName);
|
||||
worldState = new TrieImpl(stateDB.getDb());
|
||||
accountStateDB = new TrackTrie(worldState);
|
||||
}
|
||||
|
||||
private RepositoryImpl(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
|
||||
this.accountStateDB = accountStateDB;
|
||||
this.contractDetailsDB = contractDetailsDB;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
close();
|
||||
detailsDB = new DatabaseImpl(DETAILS_DB);
|
||||
stateDB = new DatabaseImpl(STATE_DB);
|
||||
worldState = new TrieImpl(stateDB.getDb());
|
||||
}
|
||||
|
||||
public Repository getTrack() {
|
||||
TrackTrie trackState = new TrackTrie(accountStateDB);
|
||||
TrackDatabase trackDetails = new TrackDatabase(contractDetailsDB);
|
||||
return new RepositoryImpl(trackState, trackDetails);
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.detailsDB != null){
|
||||
detailsDB.close();
|
||||
detailsDB = null;
|
||||
}
|
||||
|
||||
public void startTracking() {
|
||||
logger.debug("start tracking");
|
||||
accountStateDB.startTrack();
|
||||
contractDetailsDB.startTrack();
|
||||
if (this.stateDB != null){
|
||||
stateDB.close();
|
||||
stateDB = null;
|
||||
}
|
||||
|
||||
public void commit() {
|
||||
logger.debug("commit changes");
|
||||
accountStateDB.commitTrack();
|
||||
contractDetailsDB.commitTrack();
|
||||
}
|
||||
|
||||
public void rollback() {
|
||||
logger.debug("rollback changes");
|
||||
accountStateDB.rollbackTrack();
|
||||
contractDetailsDB.rollbackTrack();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,189 +84,73 @@ public class RepositoryImpl implements Repository {
|
|||
return stateDB == null;
|
||||
}
|
||||
|
||||
public AccountState createAccount(byte[] addr) {
|
||||
@Override
|
||||
public void updateBatch(HashMap<ByteArrayWrapper, AccountState> stateCache,
|
||||
HashMap<ByteArrayWrapper, ContractDetails> detailsCache) {
|
||||
|
||||
logger.trace("createAccount: [{}]", Hex.toHexString(addr)) ;
|
||||
this.validateAddress(addr);
|
||||
WriteBatch writeBatch = detailsDB.getDb().createWriteBatch();
|
||||
for (ByteArrayWrapper hash : detailsCache.keySet()) {
|
||||
|
||||
// 1. Save AccountState
|
||||
AccountState state = new AccountState();
|
||||
accountStateDB.update(addr, state.getEncoded());
|
||||
ContractDetails contractDetails = detailsCache.get(hash);
|
||||
|
||||
ContractDetails details = new ContractDetails();
|
||||
contractDetailsDB.put(addr, details.getEncoded());
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("New account created: [{}]", Hex.toHexString(addr));
|
||||
|
||||
return state;
|
||||
if (contractDetails.isDeleted())
|
||||
writeBatch.delete(hash.getData());
|
||||
else{
|
||||
if (contractDetails.isDirty())
|
||||
writeBatch.put(hash.getData(), contractDetails.getEncoded());
|
||||
}
|
||||
|
||||
public Trie getWorldState() {
|
||||
return worldState;
|
||||
if (contractDetails.isDirty() || contractDetails.isDeleted()){
|
||||
|
||||
AccountState accountState = stateCache.get(hash);
|
||||
accountState.setStateRoot(contractDetails.getStorageHash());
|
||||
accountState.setCodeHash(sha3(contractDetails.getCode()));
|
||||
}
|
||||
|
||||
public AccountState getAccountState(byte[] addr) {
|
||||
contractDetails.setDeleted(false);
|
||||
contractDetails.setDirty(false);
|
||||
}
|
||||
detailsDB.getDb().write(writeBatch);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Get account state for: [{}]", Hex.toHexString(addr));
|
||||
for (ByteArrayWrapper hash : detailsCache.keySet()) {
|
||||
|
||||
this.validateAddress(addr);
|
||||
AccountState accountState = stateCache.get(hash);
|
||||
|
||||
byte[] accountStateRLP = accountStateDB.get(addr);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Found account state RLP: [{}]", Hex.toHexString(accountStateRLP));
|
||||
|
||||
if (accountStateRLP == null || accountStateRLP.length == 0)
|
||||
return null;
|
||||
|
||||
AccountState state = new AccountState(accountStateRLP);
|
||||
return state;
|
||||
if (accountState.isDeleted())
|
||||
worldState.delete(hash.getData());
|
||||
else{
|
||||
if (accountState.isDirty())
|
||||
worldState.update(hash.getData(), accountState.getEncoded());
|
||||
}
|
||||
|
||||
public ContractDetails getContractDetails(byte[] addr) {
|
||||
|
||||
this.validateAddress(addr);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Get contract details for: [{}]", Hex.toHexString(addr));
|
||||
|
||||
byte[] accountDetailsRLP = contractDetailsDB.get(addr);
|
||||
|
||||
if (accountDetailsRLP == null)
|
||||
return null;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Found contract details RLP: [{}]", Hex.toHexString(accountDetailsRLP));
|
||||
|
||||
ContractDetails details = new ContractDetails(accountDetailsRLP);
|
||||
return details;
|
||||
accountState.setDeleted(false);
|
||||
accountState.setDirty(false);
|
||||
}
|
||||
worldState.sync();
|
||||
}
|
||||
|
||||
public BigInteger addBalance(byte[] addr, BigInteger value) {
|
||||
|
||||
AccountState state = getAccountState(addr);
|
||||
|
||||
if (state == null)
|
||||
state = createAccount(addr);
|
||||
|
||||
BigInteger newBalance = state.addToBalance(value);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Changing balance: \n account:\t [{}]\n new balance:\t [{}]\n delta:\t\t [{}]",
|
||||
Hex.toHexString(addr), newBalance.toString(), value);
|
||||
|
||||
accountStateDB.update(addr, state.getEncoded());
|
||||
return newBalance;
|
||||
@Override
|
||||
public void rollback() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public BigInteger getBalance(byte[] addr) {
|
||||
AccountState state = getAccountState(addr);
|
||||
if (state == null) return BigInteger.ZERO;
|
||||
return state.getBalance();
|
||||
@Override
|
||||
public void commit() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public BigInteger getNonce(byte[] addr) {
|
||||
AccountState state = getAccountState(addr);
|
||||
if (state == null) return BigInteger.ZERO;
|
||||
return state.getNonce();
|
||||
@Override
|
||||
public void syncToRoot(byte[] root) {
|
||||
worldState.setRoot(root);
|
||||
}
|
||||
|
||||
public BigInteger increaseNonce(byte[] addr) {
|
||||
AccountState state = getAccountState(addr);
|
||||
if (state == null) return BigInteger.ZERO;
|
||||
state.incrementNonce();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Increment nonce:\n account:\t [{}]\n new nonce:\t [{}]",
|
||||
Hex.toHexString(addr), state.getNonce().longValue());
|
||||
|
||||
accountStateDB.update(addr, state.getEncoded());
|
||||
return state.getNonce();
|
||||
}
|
||||
|
||||
public void addStorageRow(byte[] addr, DataWord key, DataWord value) {
|
||||
|
||||
if (key == null) return;
|
||||
AccountState state = getAccountState(addr);
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
|
||||
if (state == null || details == null) return;
|
||||
details.put(key, value);
|
||||
|
||||
byte[] storageHash = details.getStorageHash();
|
||||
state.setStateRoot(storageHash);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Storage key/value saved:\n account:\t [{}]\n key:\t\t [{}]\n value:\t\t [{}]\n new hash:\t [{}]",
|
||||
Hex.toHexString(addr),
|
||||
Hex.toHexString(key.getNoLeadZeroesData()),
|
||||
Hex.toHexString(value.getNoLeadZeroesData()),
|
||||
Hex.toHexString(storageHash));
|
||||
|
||||
accountStateDB.update(addr, state.getEncoded());
|
||||
contractDetailsDB.put(addr, details.getEncoded());
|
||||
}
|
||||
|
||||
public DataWord getStorageValue(byte[] addr, DataWord key) {
|
||||
|
||||
if (key == null) return null;
|
||||
|
||||
AccountState state = getAccountState(addr);
|
||||
if (state == null) return null;
|
||||
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
DataWord value = details.get(key);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte[] getCode(byte[] addr) {
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
if (details == null) return null;
|
||||
return details.getCode();
|
||||
}
|
||||
|
||||
public void saveCode(byte[] addr, byte[] code) {
|
||||
|
||||
if (code == null) return;
|
||||
AccountState state = getAccountState(addr);
|
||||
if (state == null) return;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Saving code: \n address:\t [{}], \n code:\t\t [{}]",
|
||||
Hex.toHexString(addr),
|
||||
Hex.toHexString(code));
|
||||
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
details.setCode(code);
|
||||
|
||||
byte[] codeHash = HashUtil.sha3(code);
|
||||
state.setCodeHash(codeHash);
|
||||
|
||||
accountStateDB.update(addr, state.getEncoded());
|
||||
contractDetailsDB.put(addr, details.getEncoded());
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Code saved: \n accountstate:\t [{}]\n codeHash:\t [{}]\n details RLP:\t [{}]",
|
||||
Hex.toHexString(state.getEncoded()),
|
||||
Hex.toHexString(codeHash),
|
||||
Hex.toHexString(details.getEncoded()));
|
||||
}
|
||||
|
||||
public void delete(byte[] addr) {
|
||||
this.validateAddress(addr);
|
||||
accountStateDB.delete(addr);
|
||||
contractDetailsDB.delete(addr);
|
||||
}
|
||||
|
||||
public List<ByteArrayWrapper> dumpKeys() {
|
||||
return stateDB.dumpKeys();
|
||||
@Override
|
||||
public Repository startTracking() {
|
||||
return new RepositoryTrack(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) {
|
||||
|
||||
dumpTrie(block);
|
||||
|
||||
if (!(CONFIG.dumpFull() || CONFIG.dumpBlock() == block.getNumber()))
|
||||
|
@ -308,7 +160,8 @@ public class RepositoryImpl implements Repository {
|
|||
|
||||
if (block.getNumber() == 0 && txNumber == 0)
|
||||
if (CONFIG.dumpCleanOnRestart()) {
|
||||
try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
|
||||
try {
|
||||
FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
|
||||
}
|
||||
|
||||
String dir = CONFIG.dumpDir() + "/";
|
||||
|
@ -336,8 +189,9 @@ public class RepositoryImpl implements Repository {
|
|||
|
||||
JsonNodeFactory jsonFactory = new JsonNodeFactory(false);
|
||||
ObjectNode blockNode = jsonFactory.objectNode();
|
||||
|
||||
JSONHelper.dumpBlock(blockNode, block, gasUsed,
|
||||
this.getWorldState().getRootHash(),
|
||||
this.getRoot(),
|
||||
keys, this);
|
||||
|
||||
EtherObjectMapper mapper = new EtherObjectMapper();
|
||||
|
@ -355,7 +209,6 @@ public class RepositoryImpl implements Repository {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void dumpTrie(Block block){
|
||||
|
||||
if (!(CONFIG.dumpFull() || CONFIG.dumpBlock() == block.getNumber()))
|
||||
|
@ -367,7 +220,7 @@ public class RepositoryImpl implements Repository {
|
|||
FileWriter fw = null;
|
||||
BufferedWriter bw = null;
|
||||
|
||||
String dump = this.getWorldState().getTrieDump();
|
||||
String dump = this.worldState.getTrieDump();
|
||||
|
||||
try {
|
||||
|
||||
|
@ -389,35 +242,156 @@ public class RepositoryImpl implements Repository {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DBIterator getAccountsIterator() {
|
||||
return detailsDB.iterator();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (this.detailsDB != null){
|
||||
detailsDB.close();
|
||||
detailsDB = null;
|
||||
}
|
||||
if (this.stateDB != null){
|
||||
stateDB.close();
|
||||
stateDB = null;
|
||||
}
|
||||
@Override
|
||||
public BigInteger addBalance(byte[] addr, BigInteger value) {
|
||||
|
||||
AccountState account = getAccountState(addr);
|
||||
|
||||
if (account == null)
|
||||
account = createAccount(addr);
|
||||
|
||||
BigInteger result = account.addToBalance(value);
|
||||
worldState.update(addr, account.getEncoded());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateAddress(byte[] addr) {
|
||||
if (addr == null || addr.length < 20) {
|
||||
logger.error("Can't create address {} because is null or length != 20", ByteUtil.toHexString(addr));
|
||||
throw new IllegalArgumentException("Address must be a byte-array of length 20");
|
||||
}
|
||||
@Override
|
||||
public BigInteger getBalance(byte[] addr) {
|
||||
|
||||
AccountState account = getAccountState(addr);
|
||||
|
||||
if (account == null)
|
||||
return BigInteger.ZERO;
|
||||
|
||||
return account.getBalance();
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
close();
|
||||
detailsDB = new DatabaseImpl("details");
|
||||
contractDetailsDB = new TrackDatabase(detailsDB);
|
||||
stateDB = new DatabaseImpl("state");
|
||||
worldState = new TrieImpl(stateDB.getDb());
|
||||
accountStateDB = new TrackTrie(worldState);
|
||||
@Override
|
||||
public DataWord getStorageValue(byte[] addr, DataWord key) {
|
||||
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
|
||||
if (details == null)
|
||||
return null;
|
||||
|
||||
return details.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStorageRow(byte[] addr, DataWord key, DataWord value) {
|
||||
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
|
||||
if (details == null){
|
||||
createAccount(addr);
|
||||
details = getContractDetails(addr);
|
||||
}
|
||||
|
||||
details.put(key, value);
|
||||
detailsDB.put(addr, details.getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getCode(byte[] addr) {
|
||||
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
|
||||
if (details == null)
|
||||
return null;
|
||||
|
||||
return details.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveCode(byte[] addr, byte[] code) {
|
||||
ContractDetails details = getContractDetails(addr);
|
||||
|
||||
if (details == null){
|
||||
createAccount(addr);
|
||||
details = getContractDetails(addr);
|
||||
}
|
||||
|
||||
details.setCode(code);
|
||||
detailsDB.put(addr, details.getEncoded());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BigInteger getNonce(byte[] addr) {
|
||||
|
||||
AccountState account = getAccountState(addr);
|
||||
|
||||
if (account == null)
|
||||
account = createAccount(addr);
|
||||
|
||||
return account.getNonce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger increaseNonce(byte[] addr) {
|
||||
|
||||
AccountState account = getAccountState(addr);
|
||||
|
||||
if (account == null)
|
||||
account = createAccount(addr);
|
||||
|
||||
account.incrementNonce();
|
||||
worldState.update(addr, account.getEncoded());
|
||||
|
||||
return account.getNonce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(byte[] addr) {
|
||||
worldState.delete(addr);
|
||||
detailsDB.delete(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContractDetails getContractDetails(byte[] addr) {
|
||||
|
||||
ContractDetails result = null;
|
||||
byte[] detailsData = detailsDB.get(addr);
|
||||
|
||||
if (detailsData != null)
|
||||
result = new ContractDetails(detailsData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState getAccountState(byte[] addr) {
|
||||
|
||||
AccountState result = null;
|
||||
byte[] accountData = worldState.get(addr);
|
||||
|
||||
if (accountData.length != 0)
|
||||
result = new AccountState(accountData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState createAccount(byte[] addr) {
|
||||
|
||||
AccountState accountState = new AccountState();
|
||||
worldState.update(addr, accountState.getEncoded());
|
||||
|
||||
ContractDetails contractDetails = new ContractDetails();
|
||||
detailsDB.put(addr, contractDetails.getEncoded());
|
||||
|
||||
return accountState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRoot() {
|
||||
return worldState.getRootHash();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
package org.ethereum.db;
|
||||
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.facade.Repository;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.ethereum.crypto.SHA3Helper.sha3;
|
||||
import static org.ethereum.util.ByteUtil.wrap;
|
||||
|
||||
/**
|
||||
* www.etherj.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 17/11/2014 21:15
|
||||
*/
|
||||
|
||||
public class RepositoryTrack implements Repository {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger("repository");
|
||||
|
||||
|
||||
HashMap<ByteArrayWrapper, AccountState> cacheAccounts = new HashMap<>();
|
||||
HashMap<ByteArrayWrapper, ContractDetails> cacheDetails = new HashMap<>();
|
||||
|
||||
Repository repository;
|
||||
|
||||
public RepositoryTrack(Repository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState createAccount(byte[] addr) {
|
||||
|
||||
logger.trace("createAccount: [{}]", Hex.toHexString(addr)) ;
|
||||
|
||||
AccountState accountState = new AccountState();
|
||||
cacheAccounts.put(wrap(addr), accountState);
|
||||
|
||||
ContractDetails contractDetails = new ContractDetails();
|
||||
cacheDetails.put(wrap(addr), contractDetails);
|
||||
|
||||
return accountState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState getAccountState(byte[] addr) {
|
||||
|
||||
AccountState accountState = cacheAccounts.get(wrap(addr));
|
||||
|
||||
if (accountState == null){
|
||||
accountState = repository.getAccountState(addr);
|
||||
|
||||
if (accountState == null){
|
||||
|
||||
accountState = createAccount(addr);
|
||||
} else {
|
||||
|
||||
accountState = accountState.clone();
|
||||
cacheAccounts.put(wrap(addr), accountState);
|
||||
|
||||
ContractDetails contractDetails = repository.getContractDetails(addr);
|
||||
cacheDetails.put(wrap(addr), contractDetails.clone());
|
||||
}
|
||||
}
|
||||
return accountState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContractDetails getContractDetails(byte[] addr) {
|
||||
getAccountState(addr);
|
||||
ContractDetails contractDetails = cacheDetails.get(wrap(addr));
|
||||
return contractDetails;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void delete(byte[] addr) {
|
||||
|
||||
logger.trace("delete account: [{}]", Hex.toHexString(addr)) ;
|
||||
getAccountState(addr).setDeleted(true);
|
||||
getContractDetails(addr).setDeleted(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger increaseNonce(byte[] addr) {
|
||||
|
||||
AccountState accountState = getAccountState(addr);
|
||||
|
||||
if (accountState == null)
|
||||
accountState = createAccount(addr);
|
||||
|
||||
BigInteger saveNonce = accountState.getNonce();
|
||||
accountState.incrementNonce();
|
||||
|
||||
logger.trace("increase nonce addr: [{}], from: [{}], to: [{}]", Hex.toHexString(addr),
|
||||
saveNonce, accountState.getNonce());
|
||||
|
||||
return accountState.getNonce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getNonce(byte[] addr) {
|
||||
AccountState accountState = getAccountState(addr);
|
||||
return accountState == null ? BigInteger.ZERO : accountState.getNonce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getBalance(byte[] addr) {
|
||||
AccountState accountState = getAccountState(addr);
|
||||
return accountState == null ? BigInteger.ZERO : accountState.getBalance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger addBalance(byte[] addr, BigInteger value) {
|
||||
|
||||
AccountState accountState = getAccountState(addr);
|
||||
if (accountState == null){
|
||||
accountState = createAccount(addr);
|
||||
}
|
||||
|
||||
logger.trace("adding to balance addr: [{}], balance: [{}], delta: [{}]", Hex.toHexString(addr),
|
||||
accountState.getBalance(), value);
|
||||
|
||||
return accountState.addToBalance(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveCode(byte[] addr, byte[] code) {
|
||||
logger.trace("saving code addr: [{}], code: [{}]", Hex.toHexString(addr),
|
||||
Hex.toHexString(code));
|
||||
getContractDetails(addr).setCode(code);
|
||||
getAccountState(addr).setCodeHash(sha3(code));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getCode(byte[] addr) {
|
||||
return getContractDetails(addr).getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStorageRow(byte[] addr, DataWord key, DataWord value) {
|
||||
|
||||
logger.trace("add storage row, addr: [{}], key: [{}] val: [{}]", Hex.toHexString(addr),
|
||||
key.toString(), value.toString());
|
||||
|
||||
getContractDetails(addr).put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataWord getStorageValue(byte[] addr, DataWord key) {
|
||||
return getContractDetails(addr).get(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DBIterator getAccountsIterator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository startTracking() {
|
||||
logger.debug("start tracking");
|
||||
return new RepositoryTrack(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
logger.debug("commit changes");
|
||||
repository.updateBatch(cacheAccounts, cacheDetails);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void syncToRoot(byte[] root) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
logger.debug("rollback changes");
|
||||
|
||||
cacheAccounts.clear();
|
||||
cacheDetails.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBatch(HashMap<ByteArrayWrapper, AccountState> accountStates,
|
||||
HashMap<ByteArrayWrapper, ContractDetails> contractDetailes){
|
||||
|
||||
for (ByteArrayWrapper hash : accountStates.keySet()){
|
||||
cacheAccounts.put(hash, accountStates.get(hash));
|
||||
}
|
||||
|
||||
for (ByteArrayWrapper hash : contractDetailes.keySet()){
|
||||
cacheDetails.put(hash, contractDetailes.get(hash));
|
||||
}
|
||||
}
|
||||
|
||||
@Override // that's the idea track is here not for root calculations
|
||||
public byte[] getRoot() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -73,7 +73,6 @@ public class EthereumFactory {
|
|||
|
||||
System.setProperty("hsqldb.reconfig_logging", "false");
|
||||
|
||||
|
||||
String url =
|
||||
String.format("jdbc:hsqldb:file:./%s/blockchain/blockchain.db;" +
|
||||
"create=%s;hsqldb.default_table_type=cached",
|
||||
|
@ -86,6 +85,7 @@ public class EthereumFactory {
|
|||
ds.setUrl(url);
|
||||
ds.setUsername("sa");
|
||||
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
package org.ethereum.facade;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.db.ContractDetails;
|
||||
import org.ethereum.db.DatabaseImpl;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* www.etherj.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 08/09/2014 10:25
|
||||
|
@ -94,6 +91,7 @@ public interface Repository {
|
|||
*/
|
||||
public void addStorageRow(byte[] addr, DataWord key, DataWord value);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve storage value from an account for a given key
|
||||
*
|
||||
|
@ -128,13 +126,6 @@ public interface Repository {
|
|||
*/
|
||||
public DBIterator getAccountsIterator();
|
||||
|
||||
/**
|
||||
* Return the current state as the Trie data structure
|
||||
*
|
||||
* @return the <code>Trie</code> representing the entire current state
|
||||
*/
|
||||
public Trie getWorldState();
|
||||
|
||||
|
||||
/**
|
||||
* Dump the full state of the current repository into a file with JSON format
|
||||
|
@ -149,16 +140,11 @@ public interface Repository {
|
|||
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash);
|
||||
|
||||
/**
|
||||
* Start tracking the database changes
|
||||
*/
|
||||
public void startTracking();
|
||||
|
||||
/**
|
||||
* Return a repository snapshot of the current state
|
||||
* Save a snapshot and start tracking future changes
|
||||
*
|
||||
* @return the repository in its current state
|
||||
* @return the tracker repository
|
||||
*/
|
||||
public Repository getTrack();
|
||||
public Repository startTracking();
|
||||
|
||||
/**
|
||||
* Store all the temporary changes made
|
||||
|
@ -172,6 +158,13 @@ public interface Repository {
|
|||
*/
|
||||
public void rollback();
|
||||
|
||||
/**
|
||||
* Return to one of the previous snapshots
|
||||
* by moving the root.
|
||||
* @param root - new root
|
||||
*/
|
||||
public void syncToRoot(byte[] root);
|
||||
|
||||
/**
|
||||
* Check to see if the current repository has an open connection to the database
|
||||
* @return <tt>true</tt> if connection to database is open
|
||||
|
@ -188,4 +181,10 @@ public interface Repository {
|
|||
*/
|
||||
public void reset();
|
||||
|
||||
public void updateBatch(HashMap<ByteArrayWrapper, AccountState> accountStates,
|
||||
HashMap<ByteArrayWrapper, ContractDetails> contractDetailes);
|
||||
|
||||
|
||||
public byte[] getRoot();
|
||||
|
||||
}
|
||||
|
|
|
@ -157,11 +157,15 @@ public class WorldManager {
|
|||
repository.createAccount(Hex.decode(address));
|
||||
repository.addBalance(Hex.decode(address), Genesis.PREMINE_AMOUNT);
|
||||
}
|
||||
blockchain.storeBlock(Genesis.getInstance());
|
||||
|
||||
blockStore.saveBlock(Genesis.getInstance());
|
||||
|
||||
blockchain.setBestBlock(Genesis.getInstance());
|
||||
blockchain.setTotalDifficulty(BigInteger.ZERO);
|
||||
|
||||
repository.dumpState(Genesis.getInstance(), 0, 0, null);
|
||||
|
||||
logger.info("Genesis block loaded");
|
||||
} else {
|
||||
|
||||
blockchain.setBestBlock(bestBlock);
|
||||
|
@ -181,16 +185,25 @@ public class WorldManager {
|
|||
// update world state by dummy hash
|
||||
byte[] rootHash = Hex.decode(CONFIG.rootHashStart());
|
||||
logger.info("Loading root hash from property file: [{}]", CONFIG.rootHashStart());
|
||||
this.repository.getWorldState().setRoot(rootHash);
|
||||
this.repository.syncToRoot(rootHash);
|
||||
|
||||
} else{
|
||||
|
||||
// Update world state to latest loaded block from db
|
||||
this.repository.getWorldState().setRoot(blockchain.getBestBlock().getStateRoot());
|
||||
this.repository.syncToRoot(blockchain.getBestBlock().getStateRoot());
|
||||
}
|
||||
|
||||
/* todo: return it when there is no state conflicts on the chain
|
||||
boolean dbValid = this.repository.getWorldState().validate() || bestBlock.isGenesis();
|
||||
if (!dbValid){
|
||||
logger.error("The DB is not valid for that blockchain");
|
||||
System.exit(-1); // todo: reset the repository and blockchain
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
logger.info("Resetting blockchain ");
|
||||
repository.reset();
|
||||
blockchain.reset();
|
||||
loadBlockchain();
|
||||
|
|
|
@ -65,7 +65,7 @@ public class BlockQueue {
|
|||
Block block = blockReceivedQueue.poll();
|
||||
|
||||
logger.info("Processing block index: {}", block.getNumber());
|
||||
blockchain.add(block);
|
||||
blockchain.tryToConnect(block);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -425,7 +425,7 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
|
|||
}
|
||||
sendGetBlocks();
|
||||
}
|
||||
}, 1000, 300);
|
||||
}, 300, 10);
|
||||
}
|
||||
|
||||
private void stopGetBlocksTimer() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.ethereum.crypto.HashUtil;
|
|||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.util.Value;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.iq80.leveldb.WriteBatch;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
|
@ -74,17 +75,18 @@ public class Cache {
|
|||
return;
|
||||
}
|
||||
|
||||
WriteBatch batch = db.createWriteBatch();
|
||||
|
||||
for (ByteArrayWrapper key : this.nodes.keySet()) {
|
||||
Node node = this.nodes.get(key);
|
||||
if (node.isDirty()) {
|
||||
this.db.put(key.getData(), node.getValue().encode());
|
||||
batch.put(key.getData(), node.getValue().encode());
|
||||
node.setDirty(false);
|
||||
}
|
||||
}
|
||||
this.isDirty = false;
|
||||
|
||||
// TODO come up with a way to clean up this.nodes
|
||||
// from memory without breaking consensus
|
||||
db.write(batch);
|
||||
this.isDirty = false;
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The TrackTrie is a wrapper around and actual Modified Merkle Patricia Trie
|
||||
* to keep track of changes which can be rolled back or committed down into
|
||||
* the original trie after successful execution of a transaction.
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 19:47
|
||||
*/
|
||||
public class TrackTrie implements Trie {
|
||||
|
||||
private Trie trie;
|
||||
|
||||
private boolean trackingChanges = false;
|
||||
private Map<ByteArrayWrapper, byte[]> changes;
|
||||
private List<ByteArrayWrapper> deletes;
|
||||
|
||||
public TrackTrie(Trie trie) {
|
||||
this.trie = trie;
|
||||
}
|
||||
|
||||
public void startTrack() {
|
||||
changes = new HashMap<>();
|
||||
deletes = new ArrayList<>();
|
||||
trackingChanges = true;
|
||||
}
|
||||
|
||||
public void commitTrack() {
|
||||
for (ByteArrayWrapper key : changes.keySet())
|
||||
trie.update(key.getData(), changes.get(key));
|
||||
for (ByteArrayWrapper key : deletes)
|
||||
trie.update(key.getData(), ByteUtil.EMPTY_BYTE_ARRAY);
|
||||
changes = null;
|
||||
trackingChanges = false;
|
||||
}
|
||||
|
||||
public void rollbackTrack() {
|
||||
changes = new HashMap<>();
|
||||
deletes = new ArrayList<>();
|
||||
changes = null;
|
||||
trackingChanges = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] key, byte[] value) {
|
||||
if (trackingChanges)
|
||||
changes.put(new ByteArrayWrapper(key), value);
|
||||
else
|
||||
trie.update(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] get(byte[] key) {
|
||||
if (trackingChanges) {
|
||||
ByteArrayWrapper wKey = new ByteArrayWrapper(key);
|
||||
if (deletes.contains(wKey))
|
||||
return null;
|
||||
if (changes.get(wKey) != null)
|
||||
return changes.get(wKey);
|
||||
return trie.get(key);
|
||||
}
|
||||
return trie.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(byte[] key) {
|
||||
if (trackingChanges) {
|
||||
ByteArrayWrapper wKey = new ByteArrayWrapper(key);
|
||||
deletes.add(wKey);
|
||||
} else
|
||||
trie.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRootHash() {
|
||||
return trie.getRootHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrieDump() {
|
||||
return trie.getTrieDump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoot(byte[] root) {
|
||||
trie.setRoot(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync() {
|
||||
trie.sync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
trie.undo();
|
||||
}
|
||||
}
|
|
@ -54,4 +54,7 @@ public interface Trie {
|
|||
public void undo();
|
||||
|
||||
public String getTrieDump();
|
||||
|
||||
public boolean validate();
|
||||
|
||||
}
|
|
@ -510,4 +510,12 @@ public class TrieImpl implements Trie {
|
|||
public interface ScanAction {
|
||||
public void doOnNode(byte[] hash, Value node);
|
||||
}
|
||||
|
||||
public boolean validate(){
|
||||
|
||||
if (cache.get(getRootHash()) != null)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.math.BigInteger;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
public class ByteUtil {
|
||||
|
@ -287,4 +288,9 @@ public class ByteUtil {
|
|||
System.arraycopy(src, 0, dest, dest.length - src.length, src.length);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
public static ByteArrayWrapper wrap(byte[] data){
|
||||
return new ByteArrayWrapper(data);
|
||||
}
|
||||
}
|
|
@ -297,17 +297,16 @@ public class Program {
|
|||
result.getRepository().addBalance(senderAddress, endowment.negate());
|
||||
BigInteger newBalance = result.getRepository().addBalance(newAddress, endowment);
|
||||
|
||||
Repository trackRepository = result.getRepository().getTrack();
|
||||
trackRepository.startTracking();
|
||||
Repository track = result.getRepository().startTracking();
|
||||
|
||||
// [3] UPDATE THE NONCE
|
||||
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
|
||||
trackRepository.increaseNonce(senderAddress);
|
||||
track.increaseNonce(senderAddress);
|
||||
|
||||
// [5] COOK THE INVOKE AND EXECUTE
|
||||
ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke(
|
||||
this, new DataWord(newAddress), DataWord.ZERO, gasLimit,
|
||||
newBalance, null, trackRepository);
|
||||
newBalance, null, track);
|
||||
|
||||
ProgramResult result = null;
|
||||
|
||||
|
@ -324,15 +323,15 @@ public class Program {
|
|||
result.getException() instanceof Program.OutOfGasException) {
|
||||
logger.info("contract run halted by OutOfGas: new contract init ={}" , Hex.toHexString(newAddress));
|
||||
|
||||
trackRepository.rollback();
|
||||
track.rollback();
|
||||
stackPushZero();
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. CREATE THE CONTRACT OUT OF RETURN
|
||||
byte[] code = result.getHReturn().array();
|
||||
trackRepository.saveCode(newAddress, code);
|
||||
trackRepository.commit();
|
||||
track.saveCode(newAddress, code);
|
||||
track.commit();
|
||||
|
||||
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
|
||||
stackPush(new DataWord(newAddress));
|
||||
|
@ -402,9 +401,7 @@ public class Program {
|
|||
// actual gas subtract
|
||||
this.spendGas(msg.getGas().longValue(), "internal call");
|
||||
|
||||
Repository trackRepository = result.getRepository().getTrack();
|
||||
trackRepository.startTracking();
|
||||
|
||||
Repository trackRepository = result.getRepository().startTracking();
|
||||
ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke(
|
||||
this, new DataWord(contextAddress), msg.getEndowment(),
|
||||
msg.getGas(), contextBalance, data, trackRepository);
|
||||
|
|
|
@ -6,7 +6,7 @@ log4j.logger.dump=TRACE, DUMP
|
|||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} [%c{1}] %m%n
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%c{1}] %m%n
|
||||
log4j.appender.stdout.Threshold=TRACE
|
||||
|
||||
# Direct log messages to stdout
|
||||
|
@ -31,19 +31,18 @@ log4j.logger.peerdiscovery = TRACE
|
|||
log4j.logger.peermonitor = TRACE
|
||||
log4j.logger.java.nio = ERROR
|
||||
log4j.logger.io.netty = ERROR
|
||||
log4j.logger.wire = TRACE
|
||||
log4j.logger.wire = ERROR
|
||||
log4j.logger.VM = ERROR
|
||||
log4j.logger.main = ERROR
|
||||
log4j.logger.trie = ERROR
|
||||
log4j.logger.state = INFO
|
||||
log4j.logger.repository = INFO
|
||||
log4j.logger.blockchain = TRACE
|
||||
log4j.logger.repository = TRACE
|
||||
log4j.logger.blockchain = DEBUG
|
||||
log4j.logger.txs = ERROR
|
||||
log4j.logger.ui = ERROR
|
||||
log4j.logger.gas = ERROR
|
||||
log4j.logger.cli = INFO
|
||||
|
||||
|
||||
log4j.logger.org.springframework = ERROR
|
||||
log4j.logger.org.hibernate = ERROR
|
||||
log4j.logger.hsqldb.db = ERROR
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
# if the system will work as a server also
|
||||
# accept for incoming connections [true/false]
|
||||
server.acceptConnections = false
|
||||
|
||||
# List of the peers to start
|
||||
# the search of the online peers
|
||||
# values: [ip:port, ip:port, ip:port ...]
|
||||
|
|
|
@ -70,7 +70,6 @@ public class TestContext {
|
|||
|
||||
System.setProperty("hsqldb.reconfig_logging", "false");
|
||||
|
||||
|
||||
String url =
|
||||
String.format("jdbc:hsqldb:file:./%s/blockchain/blockchain.db;" +
|
||||
"create=%s;hsqldb.default_table_type=cached",
|
||||
|
@ -83,6 +82,7 @@ public class TestContext {
|
|||
ds.setUrl(url);
|
||||
ds.setUsername("sa");
|
||||
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,16 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import test.ethereum.TestContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -183,6 +191,84 @@ public class BlockTest {
|
|||
assertEquals(actualGasLimit, calcGasLimit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScenario1() throws URISyntaxException, IOException {
|
||||
|
||||
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
||||
|
||||
URL scenario1 = ClassLoader
|
||||
.getSystemResource("blockload/scenario1.dmp");
|
||||
|
||||
File file = new File(scenario1.toURI());
|
||||
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
|
||||
byte[] root = Genesis.getInstance().getStateRoot();
|
||||
for(String blockRLP : strData){
|
||||
Block block = new Block(
|
||||
Hex.decode(blockRLP));
|
||||
logger.info("sending block.hash: {}", Hex.toHexString( block.getHash() ));
|
||||
blockchain.tryToConnect(block);
|
||||
root = block.getStateRoot();
|
||||
}
|
||||
|
||||
logger.info("asserting root state is: {}", Hex.toHexString( root ));
|
||||
|
||||
//expected root: 13a5e615365c86438d98df5a2ca5bf1173ab4ea33be808fde7b94e47e9534549
|
||||
assertArrayEquals(root, worldManager.getRepository().getRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScenario2() throws URISyntaxException, IOException {
|
||||
|
||||
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
||||
|
||||
URL scenario1 = ClassLoader
|
||||
.getSystemResource("blockload/scenario2.dmp");
|
||||
|
||||
File file = new File(scenario1.toURI());
|
||||
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
|
||||
byte[] root = Genesis.getInstance().getStateRoot();
|
||||
for(String blockRLP : strData){
|
||||
Block block = new Block(
|
||||
Hex.decode(blockRLP));
|
||||
logger.info("sending block.hash: {}", Hex.toHexString( block.getHash() ));
|
||||
blockchain.tryToConnect(block);
|
||||
root = block.getStateRoot();
|
||||
}
|
||||
|
||||
logger.info("asserting root state is: {}", Hex.toHexString( root ));
|
||||
|
||||
//expected root: 8bbff862199ccf5411c9505598eeba3d76b51e4d391ac1189903b0fcbdd3733b
|
||||
assertArrayEquals(root, worldManager.getRepository().getRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScenario3() throws URISyntaxException, IOException {
|
||||
|
||||
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
||||
|
||||
URL scenario1 = ClassLoader
|
||||
.getSystemResource("blockload/scenario3.dmp");
|
||||
|
||||
File file = new File(scenario1.toURI());
|
||||
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
|
||||
byte[] root = Genesis.getInstance().getStateRoot();
|
||||
for(String blockRLP : strData){
|
||||
Block block = new Block(
|
||||
Hex.decode(blockRLP));
|
||||
logger.info("sending block.hash: {}", Hex.toHexString( block.getHash() ));
|
||||
blockchain.tryToConnect(block);
|
||||
root = block.getStateRoot();
|
||||
}
|
||||
|
||||
logger.info("asserting root state is: {}", Hex.toHexString( root ));
|
||||
|
||||
//expected root: 8bbff862199ccf5411c9505598eeba3d76b51e4d391ac1189903b0fcbdd3733b
|
||||
assertArrayEquals(root, worldManager.getRepository().getRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testUncleValidGenerationGap() {
|
||||
|
|
|
@ -68,10 +68,10 @@ public class ForkTest {
|
|||
|
||||
BlockchainImpl blockchain = (BlockchainImpl)worldManager.getBlockchain();
|
||||
|
||||
URL massiveUpload_1 = ClassLoader
|
||||
URL scenario1 = ClassLoader
|
||||
.getSystemResource("fork/scenario1.dmp");
|
||||
|
||||
File file = new File(massiveUpload_1.toURI());
|
||||
File file = new File(scenario1.toURI());
|
||||
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
|
||||
for(String blockRLP : strData){
|
||||
|
@ -96,10 +96,10 @@ public class ForkTest {
|
|||
BlockchainImpl blockchain = (BlockchainImpl) worldManager.getBlockchain();
|
||||
|
||||
|
||||
URL massiveUpload_1 = ClassLoader
|
||||
URL scenario2 = ClassLoader
|
||||
.getSystemResource("fork/scenario2.dmp");
|
||||
|
||||
File file = new File(massiveUpload_1.toURI());
|
||||
File file = new File(scenario2.toURI());
|
||||
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
|
||||
for(String blockRLP : strData){
|
||||
|
|
|
@ -1,350 +1,559 @@
|
|||
package test.ethereum.db;
|
||||
|
||||
import org.ethereum.config.SystemProperties;
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.db.RepositoryImpl;
|
||||
import org.ethereum.core.Genesis;
|
||||
import org.ethereum.facade.Repository;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.db.RepositoryImpl;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.junit.After;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import test.ethereum.TestContext;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* www.etherj.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 23/06/2014 23:52
|
||||
* Created on: 17/11/2014 23:08
|
||||
*/
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
|
||||
public class RepositoryTest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger("test");
|
||||
|
||||
@Test
|
||||
public void test1(){
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "org.ethereum")
|
||||
static class ContextConfiguration extends TestContext {
|
||||
static {
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
}
|
||||
}
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
@Autowired
|
||||
WorldManager worldManager;
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
@After
|
||||
public void doReset(){
|
||||
worldManager.reset();
|
||||
repository.increaseNonce(cow);
|
||||
repository.increaseNonce(horse);
|
||||
|
||||
assertEquals(BigInteger.ONE, repository.getNonce(cow));
|
||||
assertEquals(BigInteger.ONE, repository.getNonce(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test // create account, get account
|
||||
public void test1() {
|
||||
@Test
|
||||
public void test2(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
AccountState createdState = repository.createAccount(Hex.decode(addr));
|
||||
AccountState fetchedState = repository.getAccountState(Hex.decode(addr));
|
||||
assertEquals(createdState.getEncoded(), fetchedState.getEncoded());
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
repository.addBalance(cow, BigInteger.TEN);
|
||||
repository.addBalance(horse, BigInteger.ONE);
|
||||
|
||||
assertEquals(BigInteger.TEN, repository.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, repository.getBalance(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test // increase nonce
|
||||
public void test2() {
|
||||
@Test
|
||||
public void test3(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
BigInteger nonce0 = repository.getNonce(Hex.decode(addr));
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
BigInteger nonce1 = repository.getNonce(Hex.decode(addr));
|
||||
byte[] cowCode = Hex.decode("A1A2A3");
|
||||
byte[] horseCode = Hex.decode("B1B2B3");
|
||||
|
||||
repository.increaseNonce(Hex.decode(addr));
|
||||
BigInteger nonce2 = repository.getNonce(Hex.decode(addr));
|
||||
repository.saveCode(cow, cowCode);
|
||||
repository.saveCode(horse, horseCode);
|
||||
|
||||
assertEquals(0, nonce0.intValue());
|
||||
assertEquals(0, nonce1.intValue());
|
||||
assertEquals(1, nonce2.intValue());
|
||||
assertArrayEquals(cowCode, repository.getCode(cow));
|
||||
assertArrayEquals(horseCode, repository.getCode(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // increase nonce
|
||||
public void test3() {
|
||||
@Test
|
||||
public void test4(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
BigInteger nonce0 = repository.getNonce(Hex.decode(addr));
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
BigInteger nonce1 = repository.getNonce(Hex.decode(addr));
|
||||
byte[] cowKey = Hex.decode("A1A2A3");
|
||||
byte[] cowValue = Hex.decode("A4A5A6");
|
||||
|
||||
repository.increaseNonce(Hex.decode(addr));
|
||||
repository.increaseNonce(Hex.decode(addr));
|
||||
repository.increaseNonce(Hex.decode(addr));
|
||||
BigInteger nonce2 = repository.getNonce(Hex.decode(addr));
|
||||
byte[] horseKey = Hex.decode("B1B2B3");
|
||||
byte[] horseValue = Hex.decode("B4B5B6");
|
||||
|
||||
assertEquals(0, nonce0.intValue());
|
||||
assertEquals(0, nonce1.intValue());
|
||||
assertEquals(3, nonce2.intValue());
|
||||
repository.addStorageRow(cow, new DataWord(cowKey), new DataWord(cowValue));
|
||||
repository.addStorageRow(horse, new DataWord(horseKey), new DataWord(horseValue));
|
||||
|
||||
assertEquals(new DataWord(cowValue), repository.getStorageValue(cow, new DataWord(cowKey)));
|
||||
assertEquals(new DataWord(horseValue), repository.getStorageValue(horse, new DataWord(horseKey)));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // change balance
|
||||
public void test4() {
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
@Test
|
||||
public void test5(){
|
||||
|
||||
BigInteger balance0 = repository.getBalance(Hex.decode(addr));
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
BigInteger balance1 = repository.getBalance(Hex.decode(addr));
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
repository.addBalance(Hex.decode(addr), BigInteger.valueOf(300));
|
||||
BigInteger balance2 = repository.getBalance(Hex.decode(addr));
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow);
|
||||
|
||||
assertEquals(0, balance0.intValue());
|
||||
assertEquals(0, balance1.intValue());
|
||||
assertEquals(300, balance2.intValue());
|
||||
track.increaseNonce(horse);
|
||||
|
||||
track.commit();
|
||||
|
||||
assertEquals(BigInteger.TEN, repository.getNonce(cow));
|
||||
assertEquals(BigInteger.ONE, repository.getNonce(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // change balance
|
||||
public void test5() {
|
||||
@Test
|
||||
public void test6(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
BigInteger balance0 = repository.getBalance(Hex.decode(addr));
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
BigInteger balance1 = repository.getBalance(Hex.decode(addr));
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow); track.increaseNonce(cow); track.increaseNonce(cow);
|
||||
track.increaseNonce(cow);
|
||||
|
||||
repository.addBalance(Hex.decode(addr), BigInteger.valueOf(300));
|
||||
BigInteger balance2 = repository.getBalance(Hex.decode(addr));
|
||||
track.increaseNonce(horse);
|
||||
|
||||
repository.addBalance(Hex.decode(addr), BigInteger.valueOf(-150));
|
||||
BigInteger balance3 = repository.getBalance(Hex.decode(addr));
|
||||
assertEquals(BigInteger.TEN, track.getNonce(cow));
|
||||
assertEquals(BigInteger.ONE, track.getNonce(horse));
|
||||
|
||||
assertEquals(0, balance0.intValue());
|
||||
assertEquals(0, balance1.intValue());
|
||||
assertEquals(300, balance2.intValue());
|
||||
assertEquals(150, balance3.intValue());
|
||||
track.rollback();
|
||||
|
||||
assertEquals(BigInteger.ZERO, repository.getNonce(cow));
|
||||
assertEquals(BigInteger.ZERO, repository.getNonce(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // get/set code
|
||||
public void test6() {
|
||||
@Test
|
||||
public void test7(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
byte[] code = repository.getCode(Hex.decode(addr));
|
||||
assertTrue(code == null);
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
track.addBalance(cow, BigInteger.TEN);
|
||||
track.addBalance(horse, BigInteger.ONE);
|
||||
|
||||
assertEquals(BigInteger.TEN, track.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, track.getBalance(horse));
|
||||
|
||||
track.commit();
|
||||
|
||||
assertEquals(BigInteger.TEN, repository.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, repository.getBalance(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // get/set code
|
||||
public void test7() {
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
String codeString = "7f60c860005461012c602054000000000000000000000000000000000000000000600060206000f200";
|
||||
String codeHash = "8f0d7fc8cc6fdd688fa58ae9256310069f5659ed2a8a3af994d80350fbf1e798";
|
||||
@Test
|
||||
public void test8(){
|
||||
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
byte[] code0 = repository.getCode(Hex.decode(addr));
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
repository.saveCode(Hex.decode(addr), Hex.decode(codeString));
|
||||
byte[] code1 = repository.getCode(Hex.decode(addr));
|
||||
AccountState accountState = repository.getAccountState(Hex.decode(addr));
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
assertTrue(code0 == null);
|
||||
assertEquals(codeString, Hex.toHexString(code1));
|
||||
assertEquals(codeHash, Hex.toHexString(accountState.getCodeHash()));
|
||||
track.addBalance(cow, BigInteger.TEN);
|
||||
track.addBalance(horse, BigInteger.ONE);
|
||||
|
||||
assertEquals(BigInteger.TEN, track.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, track.getBalance(horse));
|
||||
|
||||
track.rollback();
|
||||
|
||||
assertEquals(BigInteger.ZERO, repository.getBalance(cow));
|
||||
assertEquals(BigInteger.ZERO, repository.getBalance(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // get/set code
|
||||
public void test8() {
|
||||
@Test
|
||||
public void test9(){
|
||||
|
||||
byte[] addr = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
byte[] code0 = repository.getCode(addr);
|
||||
repository.createAccount(addr);
|
||||
byte[] code1 = repository.getCode(addr);
|
||||
AccountState accountState = repository.getAccountState(addr);
|
||||
assertTrue(code0 == null);
|
||||
assertNull(code1);
|
||||
assertArrayEquals(EMPTY_DATA_HASH, accountState.getCodeHash());
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
DataWord cowKey = new DataWord(Hex.decode("A1A2A3"));
|
||||
DataWord cowValue = new DataWord(Hex.decode("A4A5A6"));
|
||||
|
||||
DataWord horseKey = new DataWord(Hex.decode("B1B2B3"));
|
||||
DataWord horseValue = new DataWord(Hex.decode("B4B5B6"));
|
||||
|
||||
track.addStorageRow(cow, cowKey, cowValue);
|
||||
track.addStorageRow(horse, horseKey, horseValue);
|
||||
|
||||
assertEquals(cowValue, track.getStorageValue(cow, cowKey));
|
||||
assertEquals(horseValue, track.getStorageValue(horse, horseKey));
|
||||
|
||||
track.commit();
|
||||
|
||||
assertEquals(cowValue, repository.getStorageValue(cow, cowKey));
|
||||
assertEquals(horseValue, repository.getStorageValue(horse, horseKey));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // storage set/get
|
||||
public void test9() {
|
||||
@Test
|
||||
public void test10(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
byte[] keyBytes = Hex.decode("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
|
||||
DataWord key = new DataWord(keyBytes);
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
Repository repository = worldManager.getRepository();
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
DataWord value = repository.getStorageValue(Hex.decode(addr), key);
|
||||
assertNull(value);
|
||||
DataWord cowKey = new DataWord(Hex.decode("A1A2A3"));
|
||||
DataWord cowValue = new DataWord(Hex.decode("A4A5A6"));
|
||||
|
||||
DataWord horseKey = new DataWord(Hex.decode("B1B2B3"));
|
||||
DataWord horseValue = new DataWord(Hex.decode("B4B5B6"));
|
||||
|
||||
track.addStorageRow(cow, cowKey, cowValue);
|
||||
track.addStorageRow(horse, horseKey, horseValue);
|
||||
|
||||
assertEquals(cowValue, track.getStorageValue(cow, cowKey));
|
||||
assertEquals(horseValue, track.getStorageValue(horse, horseKey));
|
||||
|
||||
track.rollback();
|
||||
|
||||
assertEquals(null, repository.getStorageValue(cow, cowKey));
|
||||
assertEquals(null, repository.getStorageValue(horse, horseKey));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // storage set/get
|
||||
public void test10() {
|
||||
|
||||
byte[] addr = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
|
||||
byte[] code = Hex.decode("00");
|
||||
Repository repository = worldManager.getRepository();
|
||||
@Test
|
||||
public void test11(){
|
||||
|
||||
repository.createAccount(addr);
|
||||
repository.saveCode(addr, code);
|
||||
byte[] keyBytes = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
|
||||
DataWord key = new DataWord(keyBytes);
|
||||
byte[] valueBytes = Hex.decode("0F4240");
|
||||
DataWord value = new DataWord(valueBytes);
|
||||
repository.addStorageRow(addr, key, value);
|
||||
DataWord fetchedValue = repository.getStorageValue(addr, key);
|
||||
assertEquals(value, fetchedValue);
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
byte[] cowCode = Hex.decode("A1A2A3");
|
||||
byte[] horseCode = Hex.decode("B1B2B3");
|
||||
|
||||
track.saveCode(cow, cowCode);
|
||||
track.saveCode(horse, horseCode);
|
||||
|
||||
assertArrayEquals(cowCode, track.getCode(cow));
|
||||
assertArrayEquals(horseCode, track.getCode(horse));
|
||||
|
||||
track.commit();
|
||||
|
||||
assertArrayEquals(cowCode, repository.getCode(cow));
|
||||
assertArrayEquals(horseCode, repository.getCode(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // storage set/get
|
||||
public void test11() {
|
||||
|
||||
byte[] addr = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
|
||||
byte[] code = Hex.decode("00");
|
||||
String expectedStorageHash = "a737c40a4aa895fb9eb464536c376ee7c2c08eb733c8fd2353fcc62dc734f075";
|
||||
@Test
|
||||
public void test12(){
|
||||
|
||||
Repository repository = worldManager.getRepository();
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
repository.createAccount(addr);
|
||||
repository.saveCode(addr, code);
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
byte[] keyBytes = Hex.decode("03E8");
|
||||
DataWord key1 = new DataWord(keyBytes);
|
||||
byte[] cowCode = Hex.decode("A1A2A3");
|
||||
byte[] horseCode = Hex.decode("B1B2B3");
|
||||
|
||||
keyBytes = Hex.decode("03E9");
|
||||
DataWord key2 = new DataWord(keyBytes);
|
||||
track.saveCode(cow, cowCode);
|
||||
track.saveCode(horse, horseCode);
|
||||
|
||||
keyBytes = Hex.decode("03F0");
|
||||
DataWord key3 = new DataWord(keyBytes);
|
||||
assertArrayEquals(cowCode, track.getCode(cow));
|
||||
assertArrayEquals(horseCode, track.getCode(horse));
|
||||
|
||||
byte[] valueBytes = Hex.decode("0F4240");
|
||||
DataWord value1 = new DataWord(valueBytes);
|
||||
track.rollback();
|
||||
|
||||
valueBytes = Hex.decode("0F4241");
|
||||
DataWord value2 = new DataWord(valueBytes);
|
||||
assertArrayEquals(null, repository.getCode(cow));
|
||||
assertArrayEquals(null, repository.getCode(horse));
|
||||
|
||||
valueBytes = Hex.decode("0F4242");
|
||||
DataWord value3 = new DataWord(valueBytes);
|
||||
|
||||
repository.addStorageRow(addr, key1, value1);
|
||||
repository.addStorageRow(addr, key2, value2);
|
||||
repository.addStorageRow(addr, key3, value3);
|
||||
|
||||
DataWord fetchedValue1 = repository.getStorageValue(addr, key1);
|
||||
DataWord fetchedValue2 = repository.getStorageValue(addr, key2);
|
||||
DataWord fetchedValue3 = repository.getStorageValue(addr, key3);
|
||||
|
||||
AccountState accountState = repository.getAccountState(addr);
|
||||
String stateRoot = Hex.toHexString(accountState.getStateRoot());
|
||||
|
||||
assertEquals(value1, fetchedValue1);
|
||||
assertEquals(value2, fetchedValue2);
|
||||
assertEquals(value3, fetchedValue3);
|
||||
assertEquals(expectedStorageHash, stateRoot);
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // commit/rollback
|
||||
public void test12() {
|
||||
@Test // Let's upload genesis pre-mine just like in the real world
|
||||
public void test13(){
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
long expectedBalance = 333;
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
Repository track = repository.startTracking();
|
||||
|
||||
Repository origRepository = worldManager.getRepository();
|
||||
|
||||
Repository repository = origRepository.getTrack();
|
||||
repository.startTracking();
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
repository.addBalance(Hex.decode(addr), BigInteger.valueOf(expectedBalance));
|
||||
|
||||
repository.commit();
|
||||
|
||||
BigInteger balance = repository.getBalance(Hex.decode(addr));
|
||||
|
||||
assertEquals(expectedBalance, balance.longValue());
|
||||
for (String address : Genesis.getPremine()) {
|
||||
track.addBalance(Hex.decode(address), Genesis.PREMINE_AMOUNT);
|
||||
}
|
||||
|
||||
@Test // commit/rollback
|
||||
public void test13() {
|
||||
track.commit();
|
||||
|
||||
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
long expectedBalance_1 = 55500;
|
||||
long expectedBalance_2 = 0;
|
||||
assertArrayEquals(Genesis.getInstance().getStateRoot(), repository.getRoot());
|
||||
|
||||
Repository origRepository = worldManager.getRepository();
|
||||
Repository repository = origRepository.getTrack();
|
||||
repository.startTracking();
|
||||
|
||||
repository.createAccount(Hex.decode(addr));
|
||||
repository.addBalance(Hex.decode(addr), BigInteger.valueOf(55500));
|
||||
|
||||
BigInteger balance = repository.getBalance(Hex.decode(addr));
|
||||
assertEquals(expectedBalance_1, balance.longValue());
|
||||
repository.rollback();
|
||||
|
||||
balance = repository.getBalance(Hex.decode(addr));
|
||||
assertEquals(expectedBalance_2, balance.longValue());
|
||||
repository.close();
|
||||
}
|
||||
|
||||
@Test // commit/rollback
|
||||
public void test14() {
|
||||
|
||||
String addr_1 = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
|
||||
String addr_2 = "77045e71a7a2c50903d88e564cd72fab11e82051";
|
||||
String codeString = "7f60c860005461012c602054000000000000000000000000000000000000000000600060206000f200";
|
||||
@Test
|
||||
public void test14(){
|
||||
|
||||
long expectedBalance = 55500;
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
Repository origRepository = worldManager.getRepository();
|
||||
Repository repository = origRepository.getTrack();
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
final BigInteger ELEVEN = BigInteger.TEN.add(BigInteger.ONE);
|
||||
|
||||
repository.createAccount(Hex.decode(addr_1));
|
||||
repository.addBalance(Hex.decode(addr_1), BigInteger.valueOf(expectedBalance));
|
||||
repository.startTracking();
|
||||
|
||||
repository.createAccount(Hex.decode(addr_2));
|
||||
repository.saveCode(Hex.decode(addr_2), Hex.decode(codeString));
|
||||
repository.addStorageRow(Hex.decode(addr_2), new DataWord(101), new DataWord(1000001));
|
||||
repository.addStorageRow(Hex.decode(addr_2), new DataWord(102), new DataWord(1000002));
|
||||
repository.addStorageRow(Hex.decode(addr_2), new DataWord(103), new DataWord(1000003));
|
||||
repository.rollback();
|
||||
// changes level_1
|
||||
Repository track1 = repository.startTracking();
|
||||
track1.addBalance(cow, BigInteger.TEN);
|
||||
track1.addBalance(horse, BigInteger.ONE);
|
||||
|
||||
BigInteger balance = repository.getBalance(Hex.decode(addr_1));
|
||||
assertEquals(expectedBalance, balance.longValue());
|
||||
assertEquals(BigInteger.TEN, track1.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, track1.getBalance(horse));
|
||||
|
||||
DataWord value = repository.getStorageValue(Hex.decode(addr_2), new DataWord(101));
|
||||
assertNull(value);
|
||||
|
||||
// changes level_2
|
||||
Repository track2 = track1.startTracking();
|
||||
track2.addBalance(cow, BigInteger.ONE);
|
||||
track2.addBalance(horse, BigInteger.TEN);
|
||||
|
||||
assertEquals(ELEVEN, track2.getBalance(cow));
|
||||
assertEquals(ELEVEN, track2.getBalance(horse));
|
||||
|
||||
track2.commit();
|
||||
track1.commit();
|
||||
|
||||
assertEquals(ELEVEN, repository.getBalance(cow));
|
||||
assertEquals(ELEVEN, repository.getBalance(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test15(){
|
||||
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
final BigInteger ELEVEN = BigInteger.TEN.add(BigInteger.ONE);
|
||||
|
||||
|
||||
// changes level_1
|
||||
Repository track1 = repository.startTracking();
|
||||
track1.addBalance(cow, BigInteger.TEN);
|
||||
track1.addBalance(horse, BigInteger.ONE);
|
||||
|
||||
assertEquals(BigInteger.TEN, track1.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, track1.getBalance(horse));
|
||||
|
||||
// changes level_2
|
||||
Repository track2 = track1.startTracking();
|
||||
track2.addBalance(cow, BigInteger.ONE);
|
||||
track2.addBalance(horse, BigInteger.TEN);
|
||||
|
||||
assertEquals(ELEVEN, track2.getBalance(cow));
|
||||
assertEquals(ELEVEN, track2.getBalance(horse));
|
||||
|
||||
track2.rollback();
|
||||
track1.commit();
|
||||
|
||||
assertEquals(BigInteger.TEN, repository.getBalance(cow));
|
||||
assertEquals(BigInteger.ONE, repository.getBalance(horse));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test16(){
|
||||
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
|
||||
byte[] cowKey1 = "key-c-1".getBytes();
|
||||
byte[] cowValue1 = "val-c-1".getBytes();
|
||||
|
||||
byte[] horseKey1 = "key-h-1".getBytes();
|
||||
byte[] horseValue1 = "val-h-1".getBytes();
|
||||
|
||||
byte[] cowKey2 = "key-c-2".getBytes();
|
||||
byte[] cowValue2 = "val-c-2".getBytes();
|
||||
|
||||
byte[] horseKey2 = "key-h-2".getBytes();
|
||||
byte[] horseValue2 = "val-h-2".getBytes();
|
||||
|
||||
// changes level_1
|
||||
Repository track1 = repository.startTracking();
|
||||
track1.addStorageRow(cow, new DataWord(cowKey1), new DataWord(cowValue1));
|
||||
track1.addStorageRow(horse, new DataWord(horseKey1), new DataWord(horseValue1));
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track1.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track1.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
// changes level_2
|
||||
Repository track2 = track1.startTracking();
|
||||
track2.addStorageRow(cow, new DataWord(cowKey2), new DataWord(cowValue2));
|
||||
track2.addStorageRow(horse, new DataWord(horseKey2), new DataWord(horseValue2));
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track2.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track2.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertEquals(new DataWord(cowValue2), track2.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertEquals(new DataWord(horseValue2), track2.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
track2.commit();
|
||||
// leaving level_2
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track1.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track1.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertEquals(new DataWord(cowValue2), track1.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertEquals(new DataWord(horseValue2), track1.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
|
||||
track1.commit();
|
||||
// leaving level_1
|
||||
|
||||
assertEquals(new DataWord(cowValue1), repository.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), repository.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertEquals(new DataWord(cowValue2), repository.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertEquals(new DataWord(horseValue2), repository.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test17(){
|
||||
|
||||
SystemProperties.CONFIG.setDataBaseDir("test_db/"+ RepositoryTest.class);
|
||||
Repository repository = new RepositoryImpl();
|
||||
|
||||
byte[] cow = Hex.decode("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826");
|
||||
byte[] horse = Hex.decode("13978AEE95F38490E9769C39B2773ED763D9CD5F");
|
||||
final BigInteger ELEVEN = BigInteger.TEN.add(BigInteger.ONE);
|
||||
|
||||
byte[] cowKey1 = "key-c-1".getBytes();
|
||||
byte[] cowValue1 = "val-c-1".getBytes();
|
||||
|
||||
byte[] horseKey1 = "key-h-1".getBytes();
|
||||
byte[] horseValue1 = "val-h-1".getBytes();
|
||||
|
||||
byte[] cowKey2 = "key-c-2".getBytes();
|
||||
byte[] cowValue2 = "val-c-2".getBytes();
|
||||
|
||||
byte[] horseKey2 = "key-h-2".getBytes();
|
||||
byte[] horseValue2 = "val-h-2".getBytes();
|
||||
|
||||
// changes level_1
|
||||
Repository track1 = repository.startTracking();
|
||||
track1.addStorageRow(cow, new DataWord(cowKey1), new DataWord(cowValue1));
|
||||
track1.addStorageRow(horse, new DataWord(horseKey1), new DataWord(horseValue1));
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track1.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track1.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
// changes level_2
|
||||
Repository track2 = track1.startTracking();
|
||||
track2.addStorageRow(cow, new DataWord(cowKey2), new DataWord(cowValue2));
|
||||
track2.addStorageRow(horse, new DataWord(horseKey2), new DataWord(horseValue2));
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track2.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track2.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertEquals(new DataWord(cowValue2), track2.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertEquals(new DataWord(horseValue2), track2.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
track2.rollback();
|
||||
// leaving level_2
|
||||
|
||||
assertEquals(new DataWord(cowValue1), track1.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), track1.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertNull(track1.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertNull(track1.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
|
||||
track1.commit();
|
||||
// leaving level_1
|
||||
|
||||
assertEquals(new DataWord(cowValue1), repository.getStorageValue(cow, new DataWord(cowKey1)));
|
||||
assertEquals(new DataWord(horseValue1), repository.getStorageValue(horse, new DataWord(horseKey1)));
|
||||
|
||||
assertNull(repository.getStorageValue(cow, new DataWord(cowKey2)));
|
||||
assertNull(repository.getStorageValue(horse, new DataWord(horseKey2)));
|
||||
|
||||
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test18(){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
f90139f90134a0955f36d073ccb026b78ab3424c15cf966a7563aa270413859f78702b9e8e22cba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a045c8c6675b11d66a33e351d2f104830727c909b58061cf992e85d622e33962e5a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ff80018609184e72a000830f3e6f80845466342180a0b8605c26e6d37aac4986bd952f36b6f595e96d76fec99eee832af740e454f42ac0c0
|
||||
f90139f90134a0dc24d5c43ad451400404f9255b7f63bdceeb11310a2b9ece302626cda9b41ff7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a05c2fcc83b43e09eee6801174a2db881187fbbdaf51e201350ec5009f8231e389a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ffff028609184e72a000830f3a9f80845466342180a090649c06424d4ba8462bcb50b4dcd8507931366e8a5f8cd61019ae590872523bc0c0
|
||||
f9020af90136a007bc19da9a5ecc987f579f40d1c450bd1d9938983929dce31d0a0734f76798a5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a013a5e615365c86438d98df5a2ca5bf1173ab4ea33be808fde7b94e47e9534549a00ddba1aad325f4f6d24e0b50ff6ceb3282650ccedc7afacbaa1256e18e014a55a072170d776433bd79dbbb1793f3cb1105ac369da0b142103639df7696e9718016b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ff80038609184e72a000830f36d08203e8845466342a80a086862224f20522063b12624e1c1158fc936bd549ca632e780d08471806bed945f8cef865808609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ca01377aa9363574f78d328844287690b4ba7b96dbc9d2a12c67fc60c821093b3a3a06385f6507e8314e45c6b79536ffd164bc8f87d24c63a832c41ce444492101887f865018609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ca062f64877aa71aa94bdddfa9c12be8aab35278f8856e1940aa2a55caf431152fea0101275c02e8b76c28f6cf90ed3a2dde0160eaba021a00cbfd02d1496b7d274fbc0
|
|
@ -0,0 +1,4 @@
|
|||
f90139f90134a0955f36d073ccb026b78ab3424c15cf966a7563aa270413859f78702b9e8e22cba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a045c8c6675b11d66a33e351d2f104830727c909b58061cf992e85d622e33962e5a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ff80018609184e72a000830f3e6f80845466342180a0b8605c26e6d37aac4986bd952f36b6f595e96d76fec99eee832af740e454f42ac0c0
|
||||
f90139f90134a0dc24d5c43ad451400404f9255b7f63bdceeb11310a2b9ece302626cda9b41ff7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a05c2fcc83b43e09eee6801174a2db881187fbbdaf51e201350ec5009f8231e389a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ffff028609184e72a000830f3a9f80845466342180a090649c06424d4ba8462bcb50b4dcd8507931366e8a5f8cd61019ae590872523bc0c0
|
||||
f9020af90136a007bc19da9a5ecc987f579f40d1c450bd1d9938983929dce31d0a0734f76798a5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a013a5e615365c86438d98df5a2ca5bf1173ab4ea33be808fde7b94e47e9534549a00ddba1aad325f4f6d24e0b50ff6ceb3282650ccedc7afacbaa1256e18e014a55a072170d776433bd79dbbb1793f3cb1105ac369da0b142103639df7696e9718016b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ff80038609184e72a000830f36d08203e8845466342a80a086862224f20522063b12624e1c1158fc936bd549ca632e780d08471806bed945f8cef865808609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ca01377aa9363574f78d328844287690b4ba7b96dbc9d2a12c67fc60c821093b3a3a06385f6507e8314e45c6b79536ffd164bc8f87d24c63a832c41ce444492101887f865018609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ca062f64877aa71aa94bdddfa9c12be8aab35278f8856e1940aa2a55caf431152fea0101275c02e8b76c28f6cf90ed3a2dde0160eaba021a00cbfd02d1496b7d274fbc0
|
||||
f90272f90136a0317604546eb502cf91b3d142301de66d46385fa3e89d6994bf8ed04e32a7ae55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949d6327e4f16d48a3e4b658503d9e3256293fce75a08bbff862199ccf5411c9505598eeba3d76b51e4d391ac1189903b0fcbdd3733ba0ae62cdf41908bd8593c9065b34653bdbebeeb5bd372e4f82b8afb9178dd0c843a0ee823bbd7016023e00804ae726cfe8c611e7ceeeb64644a96aaa77e4c868f3c4b840000000000000000000000000000080000000000000100000000000000000400000000000000000000000000000000000000000000000000000000000000000008301ff01048609184e72a000830f33038205dc845466346080a0a5f179dd5b8af29bd95c139af1a57815548669b6cd470485bea03088612c4c1cf90135f865028609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ba02891f3a500f4639906fdebb99cfad59c8f3fda9ed9d4ac13c40f01bedf38cfeea03ea2c3ce1b5346fe803c2e00309cc324979da38fac6893f182fc2a70d6d354d0f865038609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ba0e0b83b97cad39259c8d9c73125036b794025bf129b9718f16e2c95892c84f3b3a02cf5bfd58c73043eb6b8d23d0a8d762e5a33e68d964a0b078efb36c7fbb78ebff865048609184e72a0008203e894885f93eed577f2fc341ebb9a5c9b2ce4465d96c40c801ca0c2586f90246d16d1d19c5b2ecc73c64f7d2028367122ec4c4d38e37850bd22aca045021f7342e69f07e4d6f28ad0075105596e6f4b38e3c1d3905d7d16162fc9f8c0
|
File diff suppressed because one or more lines are too long
|
@ -5,7 +5,7 @@ log4j.rootLogger=DEBUG, stdout
|
|||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern= %d{HH:mm:ss} [%c{1}] %m%n
|
||||
log4j.appender.stdout.layout.ConversionPattern= %d{HH:mm:ss.SSS} [%c{1}] %m%n
|
||||
|
||||
# filter noisy classes
|
||||
log4j.logger.block = ERROR
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
<dependency>
|
||||
<groupId>org.ethereum</groupId>
|
||||
<artifactId>ethereumj</artifactId>
|
||||
<version>0.7.9.20141116.1409</version>
|
||||
<version>0.7.9.20141119.1233</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue