From af86249019e37eb64ddfe860b6f1c2610b386ee8 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Thu, 29 May 2014 11:30:38 +0200 Subject: [PATCH 1/4] Initial usage of LevelDB - still needs lots of work --- .../org/ethereum/config/SystemProperties.java | 5 + .../java/org/ethereum/core/AddressState.java | 13 +- .../main/java/org/ethereum/core/Block.java | 67 ++++--- .../java/org/ethereum/core/Blockchain.java | 142 ++++++++++++++ .../main/java/org/ethereum/core/Genesis.java | 32 ++-- .../main/java/org/ethereum/core/GoState.java | 160 ++++++++++++++++ .../main/java/org/ethereum/core/State.java | 12 ++ .../java/org/ethereum/core/StateObject.java | 180 ++++++++++++++++++ .../org/ethereum/core/StateObjectCache.java | 36 ++++ .../java/org/ethereum/core/Transaction.java | 7 +- .../main/java/org/ethereum/core/Wallet.java | 13 +- .../src/main/java/org/ethereum/db/Config.java | 8 + .../main/java/org/ethereum/db/Database.java | 116 +++++++++++ .../org/ethereum/{geodb => db}/IpGeoDB.java | 2 +- .../org/ethereum/gui/BlockChainTable.java | 10 +- .../org/ethereum/gui/BlockTableModel.java | 6 +- .../ethereum/gui/ConnectionConsoleWindow.java | 10 +- .../java/org/ethereum/gui/PayOutDialog.java | 4 +- .../org/ethereum/gui/PeersTableModel.java | 2 +- .../main/java/org/ethereum/gui/ToolBar.java | 133 +++++++------ .../java/org/ethereum/manager/MainData.java | 152 +++------------ .../net/client/EthereumProtocolHandler.java | 6 +- .../ethereum/net/submit/TransactionTask.java | 5 +- .../main/java/org/ethereum/trie/Cache.java | 16 -- .../java/org/ethereum/util/DecodeResult.java | 21 ++ .../src/main/java/org/ethereum/util/RLP.java | 2 +- .../src/main/resources/system.properties | 10 +- .../org/ethereum/core/TransactionTest.java | 9 +- .../java/org/ethereum/net/MessagesTest.java | 8 +- .../java/org/ethereum/util/UtilsTest.java | 10 +- 30 files changed, 879 insertions(+), 318 deletions(-) create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/GoState.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/State.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/StateObject.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/db/Config.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/db/Database.java rename ethereumj-core/src/main/java/org/ethereum/{geodb => db}/IpGeoDB.java (97%) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java index 9b7cb9a7..7431c47d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java @@ -91,6 +91,11 @@ public class SystemProperties { if(prop.isEmpty()) return 30303; return Integer.parseInt(prop.getProperty("peer.discovery.port")); } + + public boolean databaseReset(){ + if(prop.isEmpty()) return false; + return Boolean.parseBoolean(prop.getProperty("database.reset")); + } public String toString() { Enumeration e = prop.propertyNames(); diff --git a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java b/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java index e73b1f06..b953b455 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java @@ -17,14 +17,11 @@ public class AddressState { private BigInteger balance; public AddressState() { - ecKey = new ECKey(Utils.getRandom()); - nonce = BigInteger.ZERO; - balance = BigInteger.ZERO; + this(new ECKey(Utils.getRandom())); } public AddressState(ECKey ecKey) { - this(); - this.ecKey = ecKey; + this(ecKey, BigInteger.ZERO, BigInteger.ZERO); } public AddressState(ECKey ecKey, BigInteger nonce, BigInteger balance) { @@ -41,8 +38,8 @@ public class AddressState { return nonce; } - public void incrementTheNonce(){ - nonce = nonce.add(BigInteger.ONE); + public void incrementNonce(){ + this.nonce = nonce.add(BigInteger.ONE); } public BigInteger getBalance() { @@ -50,6 +47,6 @@ public class AddressState { } public void addToBalance(BigInteger value){ - balance = balance.add(value); + this.balance = balance.add(value); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index 392f5433..f754a732 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -7,6 +7,7 @@ import org.ethereum.util.RLPElement; import org.ethereum.util.RLPItem; import org.ethereum.util.RLPList; import org.ethereum.util.Utils; +import org.spongycastle.util.BigIntegers; import java.math.BigInteger; import java.util.ArrayList; @@ -21,10 +22,8 @@ import java.util.List; */ public class Block { - private static int LIMIT_FACTOR = (int) Math.pow(2, 16); - private static double EMA_FACTOR = 1.5; - /* A scalar value equal to the current limit of gas expenditure per block */ - private static int GAS_LIMIT = (int) Math.pow(10, 6); + /* A scalar value equal to the mininum limit of gas expenditure per block */ + private static long MIN_GAS_LIMIT = BigInteger.valueOf(10).pow(4).longValue(); private byte[] rlpEncoded; private boolean parsed = false; @@ -316,29 +315,49 @@ public class Block { } /** - * Because every transaction published into the blockchain imposes on the - * network the cost of needing to download and verify it, there is a need - * for some regulatory mechanism to prevent abuse. - * - * To solve this we simply institute a floating cap: - * - * No block can have more operations than BLK_LIMIT_FACTOR times - * the long-term exponential moving average. + * This mechanism enforces a homeostasis in terms of the time between blocks; + * a smaller period between the last two blocks results in an increase in the + * difficulty level and thus additional computation required, lengthening the + * likely next period. Conversely, if the period is too large, the difficulty, + * and expected time to the next block, is reduced. + */ + private boolean isValid() { + boolean isValid = false; + + // verify difficulty meets requirements + isValid = this.getDifficulty() == this.calcDifficulty(); + // verify gasLimit meets requirements + isValid = this.getGasLimit() == this.calcGasLimit(); + // verify timestamp meets requirements + isValid = this.getTimestamp() > this.getParent().getTimestamp(); + + return isValid; + } + + /** + * Calculate GasLimit + * max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024) * - * Specifically: - * - * blk.oplimit = floor((blk.parent.oplimit * (EMAFACTOR - 1) - * + floor(GAS_LIMIT * BLK_LIMIT_FACTOR)) / EMA_FACTOR) - * - * BLK_LIMIT_FACTOR and EMA_FACTOR are constants that will be set - * to 65536 and 1.5 for the time being, but will likely be changed - * after further analysis. - * * @return */ - public double getOplimit() { - return Math.floor((this.getParent().getOplimit() * (EMA_FACTOR - 1) - + Math.floor(GAS_LIMIT * LIMIT_FACTOR)) / EMA_FACTOR); + public long calcGasLimit() { + if (parentHash == null) + return 1000000L; + else { + Block parent = this.getParent(); + return Math.max(MIN_GAS_LIMIT, (parent.gasLimit * (1024 - 1) + (parent.gasUsed * 6 / 5)) / 1024); + } + } + + public byte[] calcDifficulty() { + if (parentHash == null) + return Genesis.DIFFICULTY; + else { + Block parent = this.getParent(); + long parentDifficulty = new BigInteger(1, parent.difficulty).longValue(); + long newDifficulty = timestamp >= parent.timestamp + 42 ? parentDifficulty - (parentDifficulty >> 10) : (parentDifficulty + (parentDifficulty >> 10)); + return BigIntegers.asUnsignedByteArray(BigInteger.valueOf(newDifficulty)); + } } public byte[] getEncoded() { diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java new file mode 100644 index 00000000..23907477 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java @@ -0,0 +1,142 @@ +package org.ethereum.core; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.ethereum.db.Config; +import org.ethereum.db.Database; +import org.ethereum.net.message.StaticMessages; +import org.ethereum.net.submit.PendingTransaction; +import org.iq80.leveldb.DBIterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongycastle.util.encoders.Hex; + +public class Blockchain extends ArrayList { + + private static Logger logger = LoggerFactory.getLogger(Blockchain.class); + + private Database db; + private Wallet wallet; + private long gasPrice = 1000; + private Block lastBlock = new Genesis(); + + private Map pendingTransactions = + Collections.synchronizedMap(new HashMap()); + + public Blockchain(Wallet wallet) { + this.db = Config.CHAIN_DB; + this.wallet = wallet; + + DBIterator iterator = db.iterator(); + try { + for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { + byte[] value = iterator.peekNext().getValue(); + Block block = new Block(value); + if(block.getNumber() > lastBlock.getNumber()) lastBlock = block; + this.add(new Block(value)); + } + } finally { + // Make sure you close the iterator to avoid resource leaks. + try { + iterator.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public Block getLastBlock() { + return lastBlock; + } + + public void addBlocks(List blocks) { + + // TODO: redesign this part when the state part and the genesis block is ready + + if (blocks.isEmpty()) return; + + Block firstBlockToAdd = blocks.get(blocks.size() - 1); + + // if it is the first block to add + // check that the parent is the genesis + if (this.isEmpty() && + !Arrays.equals(StaticMessages.GENESIS_HASH, firstBlockToAdd.getParentHash())){ + return; + } + // if there is some blocks already keep chain continuity + if (!this.isEmpty() ){ + Block lastBlock = this.get(this.size() - 1); + String hashLast = Hex.toHexString(lastBlock.getHash()); + String blockParentHash = Hex.toHexString(firstBlockToAdd.getParentHash()); + if (!hashLast.equals(blockParentHash)) return; + } + for (int i = blocks.size() - 1; i >= 0 ; --i){ + Block block = blocks.get(i); + this.add(block); + if(block.getNumber() > lastBlock.getNumber()) lastBlock = block; + db.put(block.getHash(), block.getEncoded()); + if (logger.isDebugEnabled()) + logger.debug("block added to the chain with hash: {}", Hex.toHexString(block.getHash())); + this.gasPrice = block.getMinGasPrice(); + + wallet.processBlock(block); + } + // Remove all pending transactions as they already approved by the net + for (Block block : blocks){ + for (Transaction tx : block.getTransactionsList()){ + if (logger.isDebugEnabled()) + logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash())); + removePendingTransaction(tx); + } + } + + logger.info("*** Block chain size: [ {} ]", this.size()); + } + + /* + * 1) the dialog put a pending transaction on the list + * 2) the dialog send the transaction to a net + * 3) wherever the transaction got for the wire in will change to approve state + * 4) only after the approve a) Wallet state changes + * 5) After the block is received with that tx the pending been clean up + */ + public PendingTransaction addPendingTransaction(Transaction transaction) { + + BigInteger hash = new BigInteger(transaction.getHash()); + logger.info("pending transaction placed hash: {} ", hash.toString(16) ); + + PendingTransaction pendingTransaction = pendingTransactions.get(hash); + if (pendingTransaction != null) + pendingTransaction.incApproved(); + else { + pendingTransaction = new PendingTransaction(transaction); + pendingTransactions.put(hash, pendingTransaction); + } + return pendingTransaction; + } + + public void removePendingTransaction(Transaction transaction){ + + BigInteger hash = new BigInteger(transaction.getHash()); + logger.info("pending transaction removed with hash: {} ", hash.toString(16) ); + pendingTransactions.remove(hash); + } + + public long getGasPrice() { + return gasPrice; + } + + public byte[] getLatestBlockHash(){ + if (this.isEmpty()) + return StaticMessages.GENESIS_HASH; + else + return lastBlock.getHash(); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java index 2ff006aa..2ad1dd7b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -16,25 +16,25 @@ public class Genesis extends Block { private static byte[] zeroHash160 = new byte[20]; private static byte[] sha3EmptyList = HashUtil.sha3(RLP.encodeList()); - private static byte[] parentHash = zeroHash256; - private static byte[] unclesHash = sha3EmptyList; - private static byte[] coinbase = zeroHash160; - private static byte[] stateRoot = // TODO: Get stateRoot from actual state + public static byte[] PARENT_HASH = zeroHash256; + public static byte[] UNCLES_HASH = sha3EmptyList; + public static byte[] COINBASE = zeroHash160; + public static byte[] STATE_ROOT = // TODO: Get stateRoot from actual state Hex.decode("12582945fc5ad12c3e7b67c4fc37a68fc0d52d995bb7f7291ff41a2739a7ca16"); - private static byte[] txTrieRoot = new byte[0]; - private static byte[] difficulty = BigInteger.valueOf(2).pow(22).toByteArray(); - private static long number = 0; - private static long minGasPrice = 0; - private static long gasLimit = 1000000; - private static long gasUsed = 0; - private static long timestamp = 0; - private static byte[] extraData = new byte[0]; - private static byte[] nonce = HashUtil.sha3(new byte[]{42}); + public static byte[] TX_TRIE_ROOT = new byte[0]; + public static byte[] DIFFICULTY = BigInteger.valueOf(2).pow(22).toByteArray(); + public static long NUMBER = 0; + public static long MIN_GAS_PRICE = 0; + public static long GAS_LIMIT = 1000000; + public static long GAS_USED = 0; + public static long TIMESTAMP = 0; + public static byte[] EXTRA_DATA = new byte[0]; + public static byte[] NONCE = HashUtil.sha3(new byte[]{42}); public Genesis() { - super(parentHash, unclesHash, coinbase, stateRoot, - txTrieRoot, difficulty, number, minGasPrice, gasLimit, gasUsed, - timestamp, extraData, nonce, null, null); + super(PARENT_HASH, UNCLES_HASH, COINBASE, STATE_ROOT, + TX_TRIE_ROOT, DIFFICULTY, NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, + TIMESTAMP, EXTRA_DATA, NONCE, null, null); logger.info("Genesis-hash: " + Hex.toHexString(this.getHash())); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/GoState.java b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java new file mode 100644 index 00000000..5cfc84f3 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java @@ -0,0 +1,160 @@ +package org.ethereum.core; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import org.ethereum.trie.Trie; + +public class GoState { + + // States within the ethereum protocol are used to store anything + // within the merkle trie. States take care of caching and storing + // nested states. It's the general query interface to retrieve: + // * Contracts + // * Accounts + + // The trie for this structure + private Trie trie; + // Nested states + private Map states; + + // Create a new state from a given trie + public GoState(Trie trie) { + this.trie = trie; + states = new HashMap(); + } + + public void add(String key, GoState state) { + this.states.put(key, state); + } + + // Resets the trie and all siblings + public void reset() { + this.trie.undo(); + + // Reset all nested states + for (GoState state : states.values()) { + state.reset(); + } + } + + // Syncs the trie and all siblings + public void sync() { + this.trie.sync(); + + // Sync all nested states + for (GoState state : states.values()) { + state.sync(); + } + } + + // Purges the current trie. + public int purge() { + return this.trie.getIterator().purge(); + } + + public StateObject getContract(byte[] address) { + String data = this.trie.get(new String(address)); + if (data == "") { + return null; + } + + // build contract + StateObject contract = new StateObject(address, data.getBytes()); + + // Check if there's a cached state for this contract + GoState cachedState = this.states.get(new String(address)); + if (cachedState != null) { + contract.setState( cachedState ); + } else { + // If it isn't cached, cache the state + this.states.put(new String(address), contract.getState()); + } + + return contract; + } + + public StateObject getAccount(byte[] address) { + String data = this.trie.get(new String(address)); + if (data == "") { + return StateObject.createAccount(address, BigInteger.ZERO); + } else { + return new StateObject(address, data.getBytes()); + } + } + + public boolean cmp(GoState other) { + return this.trie.cmp(other.getTrie()); + } + + public GoState copy() { + return new GoState(this.trie.copy()); + } + +// type ObjType byte +// +// enum ( +// NullTy ObjType = iota, +// AccountTy, +// ContractTy, +// UnknownTy +// ) + + // Returns the object stored at key and the type stored at key + // Returns null if nothing is stored +// public (*ethutil.Value, ObjType) getStateObject(byte[] key) { +// +// // Fetch data from the trie +// String data = this.trie.get(new String(key)); +// // Returns the null type, indicating nothing could be retrieved. +// // Anything using this function should check for this ret val +// if (data == "") { +// return (null, NullTy) +// } +// +// var enum ObjType +// Value val = new Value(data.getBytes()); +// // Check the length of the retrieved value. +// // Len 2 = Account +// // Len 3 = Contract +// // Other = invalid for now. If other types emerge, add them here +// if (val.length() == 2) { +// typ = AccountTy +// } else if (val.length == 3) { +// typ = ContractTy +// } else { +// typ = UnknownTy +// } +// +// return (val, typ); +// } + + // Updates any given state object + public void updateStateObject(StateObject stateObject) { + byte[] addr = stateObject.getAddress(); + + if (stateObject.getState() != null) { + this.states.put(new String(addr), stateObject.getState()); + } + + this.trie.update(new String(addr), new String(stateObject.rlpEncode())); + } + + public void put(byte[] key, byte[] object) { + this.trie.update(new String(key), new String(object)); + } + + /** + * Instead of calling this method, call state.getTrie().getRoot() + * @return + */ + @Deprecated() + public Object getRoot() { + return this.trie.getRoot(); + } + + public Trie getTrie() { + return this.trie; + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/State.java b/ethereumj-core/src/main/java/org/ethereum/core/State.java new file mode 100644 index 00000000..7aab74d6 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/State.java @@ -0,0 +1,12 @@ +package org.ethereum.core; + +import java.util.Map; + +import org.ethereum.trie.Trie; + +public class State { + + Trie trie; + Map states; + +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java new file mode 100644 index 00000000..751daf82 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java @@ -0,0 +1,180 @@ +package org.ethereum.core; + +import java.math.BigInteger; + +import org.ethereum.db.Config; +import org.ethereum.trie.Trie; +import org.ethereum.util.RLP; +import org.ethereum.util.Value; + +import static java.util.Arrays.copyOfRange; + +public class StateObject { + + // Address of the object + private byte[] address; + // Shared attributes + private BigInteger amount; + + private long nonce; + // Contract related attributes + private GoState state; + + private byte[] init; + private byte[] body; + + // Returns a newly created contract at root + public static StateObject createContract(byte[] address, BigInteger amount, byte[] root) { + StateObject contract = new StateObject(address, amount); + contract.setState(new GoState(new Trie(Config.STATE_DB.getDb(), new String(root)))); + return contract; + } + + // Returns a newly created account + public static StateObject createAccount(byte[] address, BigInteger amount) { + return new StateObject(address, amount); + } + + public StateObject(byte[] address, BigInteger amount) { + this.address = address; + this.amount = amount; + } + + public StateObject(byte[] address, byte[] data) { + this.address = address; + this.rlpDecode(data); + } + + public void setState(GoState state) { + this.state = state; + } + + public void setBody(byte[] body) { + this.body = body; + } + + public void setInit(byte[] init) { + this.init = init; + } + + public Value getAddress(byte[] address) { + return new Value(this.state.getTrie().get(new String(address)).getBytes()); + } + + public void setAddress(byte[] address, Object value) { + this.state.getTrie().update(new String(address), new String(new Value(value).encode())); + } + + public GoState getState() { + return this.state; + } + + public Value getMem(BigInteger num) { + byte[] nb = num.toByteArray(); + return this.getAddress(nb); + } + + /** + * Get the instruction + * + * @param pc + * @return byte wrapped in a Value object + */ + public Value getInstr(BigInteger pc) { + if (this.body.length-1 < pc.longValue()) { + return new Value(0); + } + return new Value( new byte[] { this.body[pc.intValue()] } ); + } + + public void setMem(BigInteger num, Value val) { + byte[] address = num.toByteArray(); + this.state.getTrie().update(new String(address), new String(val.encode())); + } + + // Return the gas back to the origin. Used by the Virtual machine or Closures + public void returnGas(BigInteger gas, BigInteger gasPrice, GoState state) { + BigInteger remainder = gas.multiply(gasPrice); + this.addAmount(remainder); + } + + public BigInteger getAmount() { + return this.amount; + } + + public void addAmount(BigInteger amount) { + this.amount = this.amount.add(amount); + } + + public void subAmount(BigInteger amount) { + this.amount = this.amount.subtract(amount); + } + + public void convertGas(BigInteger gas, BigInteger gasPrice) throws RuntimeException { + BigInteger total = gas.multiply(gasPrice); + if (total.compareTo(this.amount) > 0) { + throw new RuntimeException("insufficient amount: " + this.amount + ", " + total); + } + this.subAmount(total); + } + + // Returns the address of the contract/account + public byte[] getAddress() { + return this.address; + } + + public long getNonce() { + return this.nonce; + } + + // Returns the main script body + public byte[] getBody() { + return this.body; + } + + // Returns the initialization script + public byte[] getInit() { + return this.init; + } + + // State object encoding methods + public byte[] rlpEncode() { + Object root; + if (this.state != null) { + root = this.state.getTrie().getRoot(); + } else { + root = null; + } + return RLP.encode( new Object[] {this.amount, this.nonce, root, this.body}); + } + + public void rlpDecode(byte[] data) { + Value decoder = new Value(data); + + this.amount = decoder.get(0).asBigInt(); + this.nonce = decoder.get(1).asInt(); + this.state = new GoState(new Trie(Config.STATE_DB.getDb(), decoder.get(2).asObj())); + this.body = decoder.get(3).asBytes(); + } + + // Converts an transaction in to a state object + public static StateObject createContract(Transaction tx, GoState state) { + // Create contract if there's no recipient + if (tx.isContract()) { + // FIXME + byte[] txHash = tx.getHash(); + byte[] contractAddress = copyOfRange(txHash, 12, txHash.length); + + BigInteger value = new BigInteger(1, tx.getValue()); + StateObject contract = StateObject.createContract(contractAddress, value, "".getBytes()); + state.updateStateObject(contract); + + contract.setBody(tx.getData()); + + state.updateStateObject(contract); + + return contract; + } + return null; + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java b/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java new file mode 100644 index 00000000..95a9ab99 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java @@ -0,0 +1,36 @@ +package org.ethereum.core; + +import java.util.HashMap; +import java.util.Map; + +public class StateObjectCache { + + // The cached state and state object cache are helpers which will give you somewhat + // control over the nonce. When creating new transactions you're interested in the 'next' + // nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. + Map cachedObjects; + + public StateObjectCache() { + this.cachedObjects = new HashMap(); + } + + public CachedStateObject add(byte[] address, StateObject stateObject) { + CachedStateObject state = new CachedStateObject(stateObject.getNonce(), stateObject); + this.cachedObjects.put(new String(address), state); + return state; + } + + public CachedStateObject get(byte[] address) { + return this.cachedObjects.get(new String(address)); + } + + public class CachedStateObject { + private long nonce; + private StateObject stateObject; + + public CachedStateObject(long nonce, StateObject stateObject) { + this.nonce = nonce; + this.stateObject = stateObject; + } + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java index c2312076..65db0692 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java @@ -25,7 +25,7 @@ import java.util.Arrays; */ public class Transaction { - Logger logger = LoggerFactory.getLogger(this.getClass()); + private static Logger logger = LoggerFactory.getLogger(Transaction.class); public static final byte[] ZERO_ADDRESS = new byte[20]; @@ -157,11 +157,6 @@ public class Transaction { return data; } - public byte[] getInit() { - if (!parsed) rlpParse(); - return data; - } - public ECDSASignature getSignature() { if (!parsed) rlpParse(); return signature; diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java index 7a514384..f5b3605f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java @@ -32,7 +32,7 @@ public class Wallet { // todo: a) the values I need to keep for address state is balance & nonce & ECKey // todo: b) keep it to be easy accessed by the toAddress() // private HashMap rows = new HashMap<>(); - + // table for a wallet private HashMap rows = new HashMap(); private long high; @@ -42,7 +42,6 @@ public class Wallet { private HashMap transactionMap = new HashMap(); public void addNewKey(){ - AddressState addressState = new AddressState(); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); @@ -50,7 +49,6 @@ public class Wallet { } public void importKey(byte[] privKey){ - AddressState addressState = new AddressState(ECKey.fromPrivate(privKey)); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); @@ -66,7 +64,6 @@ public class Wallet { } public AddressState getAddressState(byte[] addressBytes){ - String address = Hex.toHexString(addressBytes); return rows.get(address); } @@ -94,7 +91,7 @@ public class Wallet { BigInteger value = new BigInteger(transaction.getValue()); senderState.addToBalance(value.negate()); - senderState.incrementTheNonce(); + senderState.incrementNonce(); } byte[] receiveAddress = transaction.getReceiveAddress(); @@ -116,12 +113,9 @@ public class Wallet { List transactions = block.getTransactionsList(); for (Transaction tx : transactions){ - boolean txExist = transactionMap.get(new BigInteger(tx.getHash())) != null; if (txExist) break; - else { - applyTransaction(tx); walletUpdated = true; } @@ -259,7 +253,8 @@ public class Wallet { } private void notifyListeners(){ - for (WalletListener listener : listeners) listener.valueChanged(); + for (WalletListener listener : listeners) + listener.valueChanged(); } public interface WalletListener{ diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Config.java b/ethereumj-core/src/main/java/org/ethereum/db/Config.java new file mode 100644 index 00000000..9266214e --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/db/Config.java @@ -0,0 +1,8 @@ +package org.ethereum.db; + +public class Config { + + public static Database CHAIN_DB = new Database("blockchain"); + public static Database STATE_DB = new Database("state"); + +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Database.java b/ethereumj-core/src/main/java/org/ethereum/db/Database.java new file mode 100644 index 00000000..3240e9a8 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/db/Database.java @@ -0,0 +1,116 @@ +package org.ethereum.db; + +import static org.iq80.leveldb.impl.Iq80DBFactory.factory; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.ethereum.config.SystemProperties; +import org.ethereum.core.Block; +import org.iq80.leveldb.DB; +import org.iq80.leveldb.DBIterator; +import org.iq80.leveldb.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongycastle.util.encoders.Hex; + +/** + * Generic interface for Ethereum database + * + * LevelDB key/value pair DB implementation will be used. + * Choice must be made between: + * Pure Java: https://github.com/dain/leveldb + * JNI binding: https://github.com/fusesource/leveldbjni + */ +public class Database { + + private static Logger logger = LoggerFactory.getLogger(Database.class); + private DB db; + + public Database(String name) { + // Initialize Database + Options options = new Options(); + options.createIfMissing(true); + try { + logger.debug("Opening database"); + if(SystemProperties.CONFIG.databaseReset()) { + logger.debug("Destroying '" + name + "' DB on startup ENABLED"); + destroyDB(name); + } + logger.debug("Initializing new or existing DB: '" + name + "'"); + options.createIfMissing(true); + db = factory.open(new File(name), options); + printDB(); +// logger.debug("Showing database stats"); +// String stats = DATABASE.getProperty("leveldb.stats"); +// logger.debug(stats); + } catch (IOException ioe) { + logger.error(ioe.getMessage(), ioe); + throw new RuntimeException("Can't initialize database"); + } + } + + public void destroyDB(String name) { + logger.debug("Destroying existing DB"); + Options options = new Options(); + try { + factory.destroy(new File(name), options); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + + public void printDB() { + DBIterator iterator = db.iterator(); + try { + Map blocks = new HashMap(); + int count = 0; + if (!iterator.hasNext()) { + logger.info("DB is empty"); + } else { + logger.info("Displaying blocks stored in DB sorted on key"); + } + for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { + byte[] key = iterator.peekNext().getKey(); + byte[] value = iterator.peekNext().getValue(); + Block block = new Block(value); + blocks.put(new Long(block.getNumber()), block); + logger.info("Block: " + count + " Key: " + Hex.toHexString(key) + " ---> " + block.toFlatString()); + count++; + } + } finally { + // Make sure you close the iterator to avoid resource leaks. + try { + iterator.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** Insert object(value) (key = sha3(value)) */ + public void put(byte[] key, byte[] value) { + db.put(key, value); + } + + /** Get object (key) -> value */ + public byte[] get(byte[] key) { + return db.get(key); + } + + /** Delete object (key) from db **/ + public void delete(byte[] key) { + delete(key); + } + + public DBIterator iterator() { + return db.iterator(); + } + + public DB getDb() { + return this.db; + } + +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java b/ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java similarity index 97% rename from ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java rename to ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java index bb718ea3..0fc1ac10 100644 --- a/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java @@ -1,4 +1,4 @@ -package org.ethereum.geodb; +package org.ethereum.db; import java.io.File; import java.net.InetAddress; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java b/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java index db318c4b..57df17d5 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java @@ -72,9 +72,9 @@ public class BlockChainTable extends JFrame { @Override public void actionPerformed(ActionEvent e) { - if (MainData.instance.getAllBlocks().size() - 1 < lastFindIndex) return; + if (MainData.instance.getBlockchain().size() - 1 < lastFindIndex) return; - Block block = MainData.instance.getAllBlocks().get(lastFindIndex); + Block block = MainData.instance.getBlockchain().get(lastFindIndex); StringSelection stsel = new StringSelection(block.toString()); Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard(); system.setContents(stsel,stsel); @@ -94,10 +94,10 @@ public class BlockChainTable extends JFrame { return; } - for (int i = lastFindIndex + 1; i < MainData.instance.getAllBlocks().size(); ++i) { + for (int i = lastFindIndex + 1; i < MainData.instance.getBlockchain().size(); ++i) { - if (MainData.instance.getAllBlocks().size() - 1 < i) return; - Block block = MainData.instance.getAllBlocks().get(i); + if (MainData.instance.getBlockchain().size() - 1 < i) return; + Block block = MainData.instance.getBlockchain().get(i); boolean found = block.toString().toLowerCase().contains(toFind.toLowerCase()); if (found) { // todo: now we find the first occur diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java b/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java index 12fa0f7d..cebcd1df 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java @@ -9,13 +9,13 @@ import javax.swing.table.AbstractTableModel; * User: Roman Mandeleil * Created on: 15/05/14 12:42 */ -public class BlockTableModel extends AbstractTableModel { +public class BlockTableModel extends AbstractTableModel { @Override public int getRowCount() { fireTableDataChanged(); - int rowCount = MainData.instance.getAllBlocks().size(); + int rowCount = MainData.instance.getBlockchain().size(); return rowCount; } @@ -30,6 +30,6 @@ public class BlockTableModel extends AbstractTableModel { // byte[] hash = MainData.instance.getAllBlocks().get(rowIndex).getHash(); // return Hex.toHexString(hash); - return MainData.instance.getAllBlocks().get(rowIndex).toString(); + return MainData.instance.getBlockchain().get(rowIndex).toString(); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java index e6c91b70..5fcecdbb 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java @@ -88,13 +88,10 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{ // new ClientPeer(thisConsole).connect("82.217.72.169", 30303); // c++: ZeroGox -// new ClientPeer(thisConsole).connect("54.204.10.41", 30303); + new ClientPeer(thisConsole).connect("54.204.10.41", 30303); // RomanJ - new ClientPeer(thisConsole).connect("54.211.14.10", 40404); - - - +// new ClientPeer(thisConsole).connect("54.211.14.10", 40404); } }; @@ -152,9 +149,6 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{ } } - - - public static void main(String[] args) { // Start all Swing applications on the EDT. SwingUtilities.invokeLater(new Runnable() { diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java index c0e3bd1b..1cb498e9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java @@ -115,7 +115,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes(); byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray(); - byte[] gasPrice = BigInteger.valueOf( MainData.instance.getGasPrice()).toByteArray(); + byte[] gasPrice = BigInteger.valueOf( MainData.instance.getBlockchain().getGasPrice()).toByteArray(); Transaction tx = new Transaction(nonce, gasPrice, BigIntegers .asUnsignedByteArray(fee), address, BigIntegers @@ -192,7 +192,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ // check if the tx is affordable BigInteger ammountValue = new BigInteger(amountText); BigInteger feeValue = new BigInteger(feeText); - BigInteger gasPrice = BigInteger.valueOf(MainData.instance.getGasPrice()); + BigInteger gasPrice = BigInteger.valueOf(MainData.instance.getBlockchain().getGasPrice()); BigInteger currentBalance = addressState.getBalance(); boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java index 55f1bdcf..73fab376 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java @@ -7,7 +7,7 @@ import java.util.*; import javax.swing.ImageIcon; import javax.swing.table.AbstractTableModel; -import org.ethereum.geodb.IpGeoDB; +import org.ethereum.db.IpGeoDB; import org.ethereum.manager.MainData; import org.ethereum.net.client.PeerData; import org.ethereum.util.Utils; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java index 87e74430..6ec06742 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java @@ -1,6 +1,5 @@ package org.ethereum.gui; -import org.apache.log4j.PropertyConfigurator; import org.ethereum.manager.MainData; import org.ethereum.util.Utils; import org.slf4j.Logger; @@ -29,7 +28,6 @@ public class ToolBar extends JFrame { WalletWindow walletWindow = null; SerpentEditor serpentEditor = null; - public ToolBar() throws HeadlessException { introLogger.info(""); @@ -90,22 +88,22 @@ public class ToolBar extends JFrame { editorToggle.setBorderPainted(false); editorToggle.setFocusPainted(false); editorToggle.setCursor(new Cursor(Cursor.HAND_CURSOR)); - editorToggle.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED){ - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (serpentEditor == null) - serpentEditor = new SerpentEditor(); - serpentEditor.setVisible(true); - } - }); - } else if (e.getStateChange() == ItemEvent.DESELECTED) { - serpentEditor.setVisible(false); - } - } - }); + editorToggle.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (serpentEditor == null) + serpentEditor = new SerpentEditor(); + serpentEditor.setVisible(true); + } + }); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + serpentEditor.setVisible(false); + } + } + }); JToggleButton logToggle = new JToggleButton(); logToggle.setIcon(image_2); @@ -115,23 +113,23 @@ public class ToolBar extends JFrame { logToggle.setBorderPainted(false); logToggle.setFocusPainted(false); logToggle.setCursor(new Cursor(Cursor.HAND_CURSOR)); - logToggle.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { + logToggle.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED){ - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (connectionConsoleWindow == null) - connectionConsoleWindow = new ConnectionConsoleWindow(); - connectionConsoleWindow.setVisible(true); - } - }); - } else if (e.getStateChange() == ItemEvent.DESELECTED) { - connectionConsoleWindow.setVisible(false); - } - } - }); + if (e.getStateChange() == ItemEvent.SELECTED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (connectionConsoleWindow == null) + connectionConsoleWindow = new ConnectionConsoleWindow(); + connectionConsoleWindow.setVisible(true); + } + }); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + connectionConsoleWindow.setVisible(false); + } + } + }); JToggleButton peersToggle = new JToggleButton(); peersToggle.setIcon(image_3); @@ -166,23 +164,23 @@ public class ToolBar extends JFrame { chainToggle.setBorderPainted(false); chainToggle.setFocusPainted(false); chainToggle.setCursor(new Cursor(Cursor.HAND_CURSOR)); - chainToggle.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED){ - SwingUtilities.invokeLater(new Runnable() { - public void run() { + chainToggle.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { - if (blockChainWindow == null) - blockChainWindow = new BlockChainTable(); - blockChainWindow.setVisible(true); - } - }); - } else if (e.getStateChange() == ItemEvent.DESELECTED) { - blockChainWindow.setVisible(false); - } - } - }); + if (blockChainWindow == null) + blockChainWindow = new BlockChainTable(); + blockChainWindow.setVisible(true); + } + }); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + blockChainWindow.setVisible(false); + } + } + }); JToggleButton walletToggle = new JToggleButton(); walletToggle.setIcon(image_5); @@ -192,26 +190,23 @@ public class ToolBar extends JFrame { walletToggle.setBorderPainted(false); walletToggle.setFocusPainted(false); walletToggle.setCursor(new Cursor(Cursor.HAND_CURSOR)); - walletToggle.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED){ - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (walletWindow == null) - walletWindow = new WalletWindow(); - walletWindow.setVisible(true); - } - }); - } else if (e.getStateChange() == ItemEvent.DESELECTED) { - walletWindow.setVisible(false); - } - - } - } - ); + walletToggle.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (walletWindow == null) + walletWindow = new WalletWindow(); + walletWindow.setVisible(true); + } + }); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + walletWindow.setVisible(false); + } + } + }); cp.add(editorToggle); cp.add(logToggle); cp.add(peersToggle); diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java index f135f509..8ded8051 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java @@ -1,29 +1,31 @@ package org.ethereum.manager; +import static org.ethereum.config.SystemProperties.CONFIG; + import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.*; - -import com.maxmind.geoip.Location; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.ethereum.core.AddressState; -import org.ethereum.core.Block; +import org.ethereum.core.Blockchain; import org.ethereum.core.Transaction; import org.ethereum.core.Wallet; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; -import org.ethereum.geodb.IpGeoDB; +import org.ethereum.db.Config; +import org.ethereum.db.IpGeoDB; import org.ethereum.net.client.ClientPeer; import org.ethereum.net.client.PeerData; -import org.ethereum.net.message.StaticMessages; import org.ethereum.net.peerdiscovery.PeerDiscovery; -import org.ethereum.net.submit.PendingTransaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; -import static org.ethereum.config.SystemProperties.CONFIG; +import com.maxmind.geoip.Location; /** * www.ethereumJ.com @@ -35,35 +37,16 @@ public class MainData { Logger logger = LoggerFactory.getLogger(getClass().getName()); private List peers = Collections.synchronizedList(new ArrayList()); - private List blockChainDB = new ArrayList(); + private Blockchain blockChain; private Wallet wallet = new Wallet(); private ClientPeer activePeer; - private long gasPrice = 1000; - - private Map pendingTransactions = - Collections.synchronizedMap(new HashMap()); - PeerDiscovery peerDiscovery; public static MainData instance = new MainData(); public MainData() { - - InetAddress ip = null; - int port = 0; - try { - ip = InetAddress.getByName(CONFIG.peerDiscoveryIP()); - port = CONFIG.peerDiscoveryPort(); - } catch (UnknownHostException e) { - e.printStackTrace(); - System.exit(-1); - } - - PeerData peer = new PeerData( - ip.getAddress(), port, new byte[]{00}); - peers.add(peer); - + // Initialize Wallet byte[] cowAddr = HashUtil.sha3("cow".getBytes()); ECKey key = ECKey.fromPrivate(cowAddr); @@ -72,66 +55,24 @@ public class MainData { state.addToBalance(BigInteger.valueOf(2).pow(200)); // 1606938044258990275541962092341162602522202993782792835301376 wallet.importKey(HashUtil.sha3("cat".getBytes())); - peerDiscovery = new PeerDiscovery(peers); + // Initialize Blockchain + blockChain = new Blockchain(wallet); + + // Initialize PeerData + try { + InetAddress ip = InetAddress.getByName(CONFIG.peerDiscoveryIP()); + int port = CONFIG.peerDiscoveryPort(); + PeerData peer = new PeerData(ip.getAddress(), port, new byte[]{00}); + peers.add(peer); + peerDiscovery = new PeerDiscovery(peers); + } catch (UnknownHostException e) { + e.printStackTrace(); + System.exit(-1); + } } - - public void addBlocks(List blocks) { - - // TODO: redesign this part when the state part and the genesis block is ready - - if (blocks.isEmpty()) return; - - Block firstBlockToAdd = blocks.get(blocks.size() - 1); - - // if it is the first block to add - // check that the parent is the genesis - if (blockChainDB.isEmpty() && - !Arrays.equals(StaticMessages.GENESIS_HASH, firstBlockToAdd.getParentHash())){ - return; - } - - // if there is some blocks already - // keep chain continuity - if (!blockChainDB.isEmpty() ){ - Block lastBlock = blockChainDB.get(blockChainDB.size() - 1); - String hashLast = Hex.toHexString(lastBlock.getHash()); - String blockParentHash = Hex.toHexString(firstBlockToAdd.getParentHash()); - if (!hashLast.equals(blockParentHash)) return; - } - - for (int i = blocks.size() - 1; i >= 0 ; --i){ - Block block = blocks.get(i); - blockChainDB.add(block); - - if (logger.isInfoEnabled()) - logger.info("block added to the chain hash: {}", Hex.toHexString(block.getHash())); - - this.gasPrice = block.getMinGasPrice(); - - - wallet.processBlock(block); - } - - // Remove all pending transactions as they already approved by the net - for (Block block : blocks){ - for (Transaction tx : block.getTransactionsList()){ - if (logger.isDebugEnabled()) - logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash())); - removePendingTransaction(tx); - } - } - logger.info("*** Block chain size: [ {} ]", blockChainDB.size()); - } - - public byte[] getLatestBlockHash(){ - if (blockChainDB.isEmpty()) - return StaticMessages.GENESIS_HASH; - else - return blockChainDB.get(blockChainDB.size() - 1).getHash(); - } - - public List getAllBlocks(){ - return blockChainDB; + + public Blockchain getBlockchain() { + return blockChain; } public Wallet getWallet() { @@ -146,39 +87,6 @@ public class MainData { return activePeer; } - /* - * 1) the dialog put a pending transaction on the list - * 2) the dialog send the transaction to a net - * 3) wherever the transaction got for the wire in will change to approve state - * 4) only after the approve a) Wallet state changes - * 5) After the block is received with that tx the pending been clean up - */ - public PendingTransaction addPendingTransaction(Transaction transaction) { - - BigInteger hash = new BigInteger(transaction.getHash()); - logger.info("pending transaction placed hash: {} ", hash.toString(16) ); - - PendingTransaction pendingTransaction = pendingTransactions.get(hash); - if (pendingTransaction != null) - pendingTransaction.incApproved(); - else { - pendingTransaction = new PendingTransaction(transaction); - pendingTransactions.put(hash, pendingTransaction); - } - return pendingTransaction; - } - - public void removePendingTransaction(Transaction transaction){ - - BigInteger hash = new BigInteger(transaction.getHash()); - logger.info("pending transaction removed hash: {} ", hash.toString(16) ); - pendingTransactions.remove(hash); - } - - public long getGasPrice() { - return gasPrice; - } - public List getPeers() { return peers; } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java b/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java index ce126bd0..eb8c810d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java @@ -202,7 +202,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { RLPList rlpList = RLP.decode2(payload); TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList); for (Transaction tx : transactionsMessage.getTransactions()) - MainData.instance.addPendingTransaction(tx); + MainData.instance.getBlockchain().addPendingTransaction(tx); // todo: if you got transactions send it to your connected peers logger.info(transactionsMessage.toString()); @@ -256,7 +256,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { }, 3000, secToAskForChain * 1000); } - MainData.instance.addBlocks(blockList); + MainData.instance.getBlockchain().addBlocks(blockList); logger.info(blocksMessage.toString()); if (peerListener != null) peerListener.console(blocksMessage.toString()); } @@ -343,7 +343,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { private void sendGetChain(ChannelHandlerContext ctx){ - byte[] hash = MainData.instance.getLatestBlockHash(); + byte[] hash = MainData.instance.getBlockchain().getLatestBlockHash(); GetChainMessage chainMessage = new GetChainMessage((byte)100, hash); ByteBuf buffer = ctx.alloc().buffer(chainMessage.getPayload().length + 8); diff --git a/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java b/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java index 0cf9208c..7247d343 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java @@ -34,7 +34,8 @@ public class TransactionTask implements Callable { ClientPeer peer = MainData.instance.getActivePeer(); - PendingTransaction pendingTransaction = MainData.instance.addPendingTransaction(tx); + PendingTransaction pendingTransaction = MainData.instance + .getBlockchain().addPendingTransaction(tx); peer.sendTransaction(tx); while(pendingTransaction.getApproved() < 1 ){ @@ -44,7 +45,7 @@ public class TransactionTask implements Callable { logger.info("return approved: {}", pendingTransaction.getApproved()); } catch (Throwable th) { logger.info("exception caugh: {}", th.getCause()); - MainData.instance.removePendingTransaction(tx); + MainData.instance.getBlockchain().removePendingTransaction(tx); } return null; diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java index c8d1ac80..8459ad07 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java @@ -1,9 +1,5 @@ package org.ethereum.trie; -import static org.iq80.leveldb.impl.Iq80DBFactory.factory; - -import java.io.File; -import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -11,7 +7,6 @@ import java.util.Map; import org.ethereum.crypto.HashUtil; import org.ethereum.util.Value; import org.iq80.leveldb.DB; -import org.iq80.leveldb.Options; public class Cache { @@ -20,17 +15,6 @@ public class Cache { private boolean isDirty; public Cache(DB db) { - if(db == null) { - try { - /* **** Experimental LevelDB Code **** */ - Options options = new Options(); - options.createIfMissing(true); - this.db = factory.open(new File("ethereumdb"), options); - /* **** Experimental LevelDB Code **** */ - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } this.db = db; nodes = new HashMap(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java b/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java index 9c4e609d..0feaf32e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java @@ -2,6 +2,8 @@ package org.ethereum.util; import java.io.Serializable; +import org.spongycastle.util.encoders.Hex; + public class DecodeResult implements Serializable { private int pos; @@ -18,4 +20,23 @@ public class DecodeResult implements Serializable { public Object getDecoded() { return decoded; } + + public String toString() { + return asString(this.decoded); + } + + private String asString(Object decoded) { + if(decoded instanceof String) { + return (String) decoded; + } else if (decoded instanceof byte[]) { + return Hex.toHexString((byte[]) decoded); + } else if (decoded instanceof Object[]) { + String result = ""; + for (Object item : (Object[]) decoded) { + result += asString(item); + } + return result; + } + throw new RuntimeException("Not a valid type. Should not occur"); + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java index 6a55cc92..c119fac3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java @@ -660,7 +660,7 @@ public class RLP { if (data == null || data.length < 1) { return null; } - + int prefix = data[pos] & 0xFF; if (prefix == OFFSET_SHORT_ITEM) { return new DecodeResult(pos+1, new byte[0]); // means no length or 0 diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index 4aca913e..88496c78 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -1,10 +1,7 @@ - - # if the system will work as a server also # accept for incoming connections [true/false] server.acceptConnections = false - # one default access point to start # discover the network e.g. ip: [54.201.28.117] port: [30303] peer.discovery.ip = 54.201.28.117 @@ -36,3 +33,10 @@ peer.discovery.timeout = 2 # retrieved from the peer [seconds] transaction.approve.timeout = 5 +# everytime the application starts +# the existing database will be +# destroyed and all the data will be +# downloaded from peers again +database.reset = true + + diff --git a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java index 70ea5c2b..f38e0d1d 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java @@ -1,6 +1,7 @@ package org.ethereum.core; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.IOException; import java.math.BigInteger; @@ -148,7 +149,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txSigned.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txSigned.getValue())); assertNull(txSigned.getData()); - assertNull(txSigned.getInit()); assertEquals(27, txSigned.getSignature().v); assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().r))); assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().s))); @@ -169,7 +169,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txUnsigned.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txUnsigned.getValue())); assertNull(txUnsigned.getData()); - assertNull(txUnsigned.getInit()); assertEquals(27, txUnsigned.getSignature().v); assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().r))); assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().s))); @@ -185,7 +184,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txNew.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txNew.getValue())); assertEquals("", Hex.toHexString(txNew.getData())); - assertNull(txNew.getInit()); assertNull(txNew.getSignature()); assertEquals(RLP_ENCODED_RAW_TX, Hex.toHexString(txNew.getEncodedRaw())); @@ -228,7 +226,6 @@ public class TransactionTest { assertEquals(HASH_TX_UNSIGNED, Hex.toHexString(tx.getHash())); } - @Test public void testTransactionCreateContract(){ @@ -266,8 +263,6 @@ public class TransactionTest { System.out.println("plainTx1: " + plainTx1 ); System.out.println("plainTx2: " + plainTx2 ); - System.out.println( Hex.toHexString( tx2.getSender() )); - } } diff --git a/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java b/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java index 9c514ecf..77fa752f 100644 --- a/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java @@ -202,7 +202,7 @@ public class MessagesTest { assertEquals("64", Hex.toHexString(tx.getGasPrice())); assertEquals("09184e72a000", Hex.toHexString(tx.getGasLimit())); assertEquals("null", ByteUtil.toHexString(tx.getData())); - assertEquals("null", ByteUtil.toHexString(tx.getInit())); + assertEquals("1b", Hex.toHexString(new byte[] { tx.getSignature().v })); assertEquals("5c89ebf2b77eeab88251e553f6f9d53badc1d800bbac02d830801c2aa94a4c9f", Hex.toHexString(tx.getSignature().r.toByteArray())); assertEquals("0b7907532b1f29c79942b75fff98822293bf5fdaa3653a8d9f424c6a3265f06c", Hex.toHexString(tx.getSignature().s.toByteArray())); @@ -263,9 +263,6 @@ public class MessagesTest { assertEquals("606956330c0d630000003359366000530a0d630000003359602060005301356000533557604060005301600054630000000c58", Hex.toHexString( tx.getData() )); - assertEquals("33606957", - Hex.toHexString( tx.getInit() )); - assertEquals("1c", Hex.toHexString( new byte[] {tx.getSignature().v} )); @@ -298,9 +295,6 @@ public class MessagesTest { assertEquals("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d0aceee7e5ab874e22ccf8d1a649f59106d74e8", Hex.toHexString( tx.getData() )); - assertEquals("null", - Hex.toHexString( tx.getInit() )); - assertEquals("1b", Hex.toHexString( new byte[] {tx.getSignature().v} )); diff --git a/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java b/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java index 077dc04c..d57b2d6b 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java @@ -17,7 +17,7 @@ public class UtilsTest { @Test public void getValueShortString1(){ - String expected = "123 (10^24)"; + String expected = "123·(10^24)"; String result = Utils.getValueShortString(new BigInteger("123456789123445654363653463")); assertEquals(expected, result); @@ -26,7 +26,7 @@ public class UtilsTest { @Test public void getValueShortString2(){ - String expected = "123 (10^3)"; + String expected = "123·(10^3)"; String result = Utils.getValueShortString(new BigInteger("123456")); assertEquals(expected, result); @@ -35,7 +35,7 @@ public class UtilsTest { @Test public void getValueShortString3(){ - String expected = "1 (10^3)"; + String expected = "1·(10^3)"; String result = Utils.getValueShortString(new BigInteger("1234")); assertEquals(expected, result); @@ -44,7 +44,7 @@ public class UtilsTest { @Test public void getValueShortString4(){ - String expected = "123 (10^0)"; + String expected = "123·(10^0)"; String result = Utils.getValueShortString(new BigInteger("123")); assertEquals(expected, result); @@ -54,7 +54,7 @@ public class UtilsTest { public void getValueShortString5(){ byte[] decimal = Hex.decode("3913517ebd3c0c65000000"); - String expected = "69 (10^24)"; + String expected = "69·(10^24)"; String result = Utils.getValueShortString(new BigInteger(decimal)); assertEquals(expected, result); From 25b38c81282f0110225610f67c2ec043d0bfb178 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Thu, 29 May 2014 21:38:51 +0200 Subject: [PATCH 2/4] Generate stateRoot from actual state --- .../java/org/ethereum/core/AccountState.java | 92 ++++++++ .../java/org/ethereum/core/AddressState.java | 52 ----- .../main/java/org/ethereum/core/Block.java | 37 ++-- .../main/java/org/ethereum/core/Genesis.java | 27 ++- .../main/java/org/ethereum/core/GoState.java | 16 +- .../java/org/ethereum/core/StateObject.java | 6 +- .../main/java/org/ethereum/core/Wallet.java | 18 +- .../org/ethereum/gui/ContractCallDialog.java | 14 +- .../ethereum/gui/ContractSubmitDialog.java | 16 +- .../java/org/ethereum/gui/PayOutDialog.java | 8 +- .../org/ethereum/gui/WalletAddressPanel.java | 4 +- .../java/org/ethereum/gui/WalletWindow.java | 4 +- .../java/org/ethereum/manager/MainData.java | 4 +- .../main/java/org/ethereum/trie/Cache.java | 6 + .../src/main/java/org/ethereum/trie/Trie.java | 31 ++- .../src/main/java/org/ethereum/util/RLP.java | 11 +- .../main/java/org/ethereum/util/Value.java | 4 +- .../org/ethereum/core/AccountStateTest.java | 19 ++ .../java/org/ethereum/core/StateTest.java | 53 +++++ .../java/org/ethereum/core/WalletTest.java | 4 +- .../java/org/ethereum/crypto/CryptoTest.java | 2 +- .../test/java/org/ethereum/trie/TrieTest.java | 207 ++++++++++-------- .../test/java/org/ethereum/util/RLPTest.java | 12 +- .../java/org/ethereum/util/RlpTestData.java | 2 +- 24 files changed, 409 insertions(+), 240 deletions(-) create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/AccountState.java delete mode 100644 ethereumj-core/src/main/java/org/ethereum/core/AddressState.java create mode 100644 ethereumj-core/src/test/java/org/ethereum/core/AccountStateTest.java create mode 100644 ethereumj-core/src/test/java/org/ethereum/core/StateTest.java diff --git a/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java b/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java new file mode 100644 index 00000000..87cf6329 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/AccountState.java @@ -0,0 +1,92 @@ +package org.ethereum.core; + +import org.ethereum.crypto.ECKey; +import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; +import org.ethereum.util.Utils; + +import java.math.BigInteger; + +public class AccountState { + + private ECKey ecKey; + private byte[] rlpEncoded; + + /* A value equal to the number of transactions sent + * from this address, or, in the case of contract accounts, + * the number of contract-creations made by this account */ + private BigInteger nonce; + + /* A scalar value equal to the number of Wei owned by this address */ + private BigInteger balance; + + /* A 256-bit hash of the root node of a trie structure + * that encodes the storage contents of the contract, + * itself a simple mapping between byte arrays of size 32. + * The hash is formally denoted σ[a] s . + * + * Since I typically wish to refer not to the trie’s root hash + * but to the underlying set of key/value pairs stored within, + * I define a convenient equi valence TRIE (σ[a] s ) ≡ σ[a] s . + * It shall be understood that σ[a] s is not a ‘physical’ member + * of the account and does not contribute to its later serialisation */ + private byte[] stateRoot = new byte[0]; + + /* The hash of the EVM code of this contract—this is the code + * that gets executed should this address receive a message call; + * it is immutable and thus, unlike all other fields, cannot be changed + * after construction. All such code fragments are contained in + * the state database under their corresponding hashes for later + * retrieval */ + private byte[] codeHash = HashUtil.sha3(new byte[0]); + + public AccountState() { + this(new ECKey(Utils.getRandom())); + } + + public AccountState(ECKey ecKey) { + this(ecKey, BigInteger.ZERO, BigInteger.ZERO); + } + + public AccountState(ECKey ecKey, BigInteger nonce, BigInteger balance) { + this.ecKey = ecKey; + this.nonce = nonce; + this.balance = balance; + } + + public AccountState(BigInteger nonce, BigInteger balance) { + this.nonce = nonce; + this.balance = balance; + } + + public ECKey getEcKey() { + return ecKey; + } + + public BigInteger getNonce() { + return nonce; + } + + public void incrementNonce(){ + this.nonce = nonce.add(BigInteger.ONE); + } + + public BigInteger getBalance() { + return balance; + } + + public void addToBalance(BigInteger value){ + this.balance = balance.add(value); + } + + public byte[] getEncoded() { + if(rlpEncoded == null) { + byte[] nonce = RLP.encodeBigInteger(this.nonce); + byte[] balance = RLP.encodeBigInteger(this.balance); + byte[] stateRoot = RLP.encodeElement(this.stateRoot); + byte[] codeHash = RLP.encodeElement(this.codeHash); + this.rlpEncoded = RLP.encodeList(balance, nonce, stateRoot, codeHash); + } + return rlpEncoded; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java b/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java deleted file mode 100644 index b953b455..00000000 --- a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.ethereum.core; - -import org.ethereum.crypto.ECKey; -import org.ethereum.util.Utils; - -import java.math.BigInteger; - -/** - * www.ethereumJ.com - * User: Roman Mandeleil - * Created on: 21/05/2014 10:43 - */ -public class AddressState { - - private ECKey ecKey; - private BigInteger nonce; - private BigInteger balance; - - public AddressState() { - this(new ECKey(Utils.getRandom())); - } - - public AddressState(ECKey ecKey) { - this(ecKey, BigInteger.ZERO, BigInteger.ZERO); - } - - public AddressState(ECKey ecKey, BigInteger nonce, BigInteger balance) { - this.ecKey = ecKey; - this.nonce = nonce; - this.balance = balance; - } - - public ECKey getEcKey() { - return ecKey; - } - - public BigInteger getNonce() { - return nonce; - } - - public void incrementNonce(){ - this.nonce = nonce.add(BigInteger.ONE); - } - - public BigInteger getBalance() { - return balance; - } - - public void addToBalance(BigInteger value){ - this.balance = balance.add(value); - } -} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index f754a732..c44c2e75 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -1,6 +1,8 @@ package org.ethereum.core; import org.ethereum.crypto.HashUtil; +import org.ethereum.db.Config; +import org.ethereum.trie.Trie; import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPElement; @@ -71,6 +73,7 @@ public class Block { private List transactionsList = new ArrayList(); private List uncleList = new ArrayList(); + private Trie state; public Block(byte[] rawData) { this.rlpEncoded = rawData; @@ -78,14 +81,15 @@ public class Block { } public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, - byte[] stateRoot, byte[] txTrieRoot, byte[] difficulty, - long number, long minGasPrice, long gasLimit, long gasUsed, - long timestamp, byte[] extraData, byte[] nonce, - List transactionsList, List uncleList) { + byte[] txTrieRoot, byte[] difficulty, long number, + long minGasPrice, long gasLimit, long gasUsed, long timestamp, + byte[] extraData, byte[] nonce, List transactionsList, + List uncleList) { this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; - this.stateRoot = stateRoot; + this.state = new Trie(Config.STATE_DB.getDb()); + this.stateRoot = state.getRootHash(); this.txTrieRoot = txTrieRoot; this.difficulty = difficulty; this.number = number; @@ -104,14 +108,13 @@ public class Block { // difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, // extradata, nonce] private void parseRLP() { - + RLPList params = (RLPList) RLP.decode2(rlpEncoded); - - this.hash = HashUtil.sha3(rlpEncoded); - RLPList block = (RLPList) params.get(0); - RLPList header = (RLPList) block.get(0); + // Parse Header + RLPList header = (RLPList) block.get(0); + this.parentHash = ((RLPItem) header.get(0)).getRLPData(); this.unclesHash = ((RLPItem) header.get(1)).getRLPData(); this.coinbase = ((RLPItem) header.get(2)).getRLPData(); @@ -129,12 +132,12 @@ public class Block { this.minGasPrice = gpBytes == null ? 0 : (new BigInteger(1, gpBytes)).longValue(); this.gasLimit = glBytes == null ? 0 : (new BigInteger(1, glBytes)).longValue(); this.gasUsed = guBytes == null ? 0 : (new BigInteger(1, guBytes)).longValue(); - this.timestamp = tsBytes == null ? 0 : (new BigInteger(tsBytes)).longValue(); + this.timestamp = tsBytes == null ? 0 : (new BigInteger(1, tsBytes)).longValue(); this.extraData = ((RLPItem) header.get(11)).getRLPData(); this.nonce = ((RLPItem) header.get(12)).getRLPData(); - // parse transactions + // Parse Transactions RLPList transactions = (RLPList) block.get(1); for (RLPElement rlpTx : transactions){ @@ -148,13 +151,14 @@ public class Block { RLPElement txRecipe2 = ((RLPList)rlpTx).get(2); } - // parse uncles + // Parse Uncles RLPList uncleBlocks = (RLPList) block.get(2); for (RLPElement rawUncle : uncleBlocks){ Block blockData = new Block(rawUncle.getRLPData()); this.uncleList.add(blockData); } this.parsed = true; + this.hash = this.getHash(); } public byte[] getHash(){ @@ -184,7 +188,7 @@ public class Block { public byte[] getStateRoot() { if (!parsed) parseRLP(); - return stateRoot; + return this.stateRoot; } public byte[] getTxTrieRoot() { @@ -314,6 +318,11 @@ public class Block { return toStringBuff.toString(); } + public byte[] updateState(byte[] key, byte[] value) { + this.state.update(key, value); + return this.stateRoot = this.state.getRootHash(); + } + /** * This mechanism enforces a homeostasis in terms of the time between blocks; * a smaller period between the last two blocks results in an increase in the diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java index 2ad1dd7b..965c90dd 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -19,8 +19,6 @@ public class Genesis extends Block { public static byte[] PARENT_HASH = zeroHash256; public static byte[] UNCLES_HASH = sha3EmptyList; public static byte[] COINBASE = zeroHash160; - public static byte[] STATE_ROOT = // TODO: Get stateRoot from actual state - Hex.decode("12582945fc5ad12c3e7b67c4fc37a68fc0d52d995bb7f7291ff41a2739a7ca16"); public static byte[] TX_TRIE_ROOT = new byte[0]; public static byte[] DIFFICULTY = BigInteger.valueOf(2).pow(22).toByteArray(); public static long NUMBER = 0; @@ -32,9 +30,28 @@ public class Genesis extends Block { public static byte[] NONCE = HashUtil.sha3(new byte[]{42}); public Genesis() { - super(PARENT_HASH, UNCLES_HASH, COINBASE, STATE_ROOT, - TX_TRIE_ROOT, DIFFICULTY, NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, - TIMESTAMP, EXTRA_DATA, NONCE, null, null); + super(PARENT_HASH, UNCLES_HASH, COINBASE, TX_TRIE_ROOT, DIFFICULTY, + NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, TIMESTAMP, + EXTRA_DATA, NONCE, null, null); + // Premine state + AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + // # (M) + this.updateState(Hex.decode("2ef47100e0787b915105fd5e3f4ff6752079d5cb"), acct.getEncoded()); + // # (A) + this.updateState(Hex.decode("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"), acct.getEncoded()); + // # (J) + this.updateState(Hex.decode("e6716f9544a56c530d868e4bfbacb172315bdead"), acct.getEncoded()); + // # (G) + this.updateState(Hex.decode("8a40bfaa73256b60764c1bf40675a99083efb075"), acct.getEncoded()); + // # (CH) + this.updateState(Hex.decode("e4157b34ea9615cfbde6b4fda419828124b70c78"), acct.getEncoded()); + // # (V) + this.updateState(Hex.decode("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"), acct.getEncoded()); + // # (HH) + this.updateState(Hex.decode("6c386a4b26f73c802f34673f7248bb118f97424a"), acct.getEncoded()); + // # (R) + this.updateState(Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"), acct.getEncoded()); + System.out.println(Hex.toHexString(this.getStateRoot())); logger.info("Genesis-hash: " + Hex.toHexString(this.getHash())); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/GoState.java b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java index 5cfc84f3..d6f16e2b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/GoState.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java @@ -55,13 +55,13 @@ public class GoState { } public StateObject getContract(byte[] address) { - String data = this.trie.get(new String(address)); - if (data == "") { + byte[] data = this.trie.get(new String(address)); + if (data == null || data.length == 0) { return null; } // build contract - StateObject contract = new StateObject(address, data.getBytes()); + StateObject contract = new StateObject(address, data); // Check if there's a cached state for this contract GoState cachedState = this.states.get(new String(address)); @@ -76,11 +76,11 @@ public class GoState { } public StateObject getAccount(byte[] address) { - String data = this.trie.get(new String(address)); - if (data == "") { + byte[] data = this.trie.get(new String(address)); + if (data == null || data.length == 0) { return StateObject.createAccount(address, BigInteger.ZERO); } else { - return new StateObject(address, data.getBytes()); + return new StateObject(address, data); } } @@ -138,11 +138,11 @@ public class GoState { this.states.put(new String(addr), stateObject.getState()); } - this.trie.update(new String(addr), new String(stateObject.rlpEncode())); + this.trie.update(addr, stateObject.rlpEncode()); } public void put(byte[] key, byte[] object) { - this.trie.update(new String(key), new String(object)); + this.trie.update(key, object); } /** diff --git a/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java index 751daf82..aa0eca02 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java @@ -58,11 +58,11 @@ public class StateObject { } public Value getAddress(byte[] address) { - return new Value(this.state.getTrie().get(new String(address)).getBytes()); + return new Value(this.state.getTrie().get(new String(address))); } public void setAddress(byte[] address, Object value) { - this.state.getTrie().update(new String(address), new String(new Value(value).encode())); + this.state.getTrie().update(address, new Value(value).encode()); } public GoState getState() { @@ -89,7 +89,7 @@ public class StateObject { public void setMem(BigInteger num, Value val) { byte[] address = num.toByteArray(); - this.state.getTrie().update(new String(address), new String(val.encode())); + this.state.getTrie().update(address, val.encode()); } // Return the gas back to the origin. Used by the Virtual machine or Closures diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java index f5b3605f..73f7b70f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java @@ -34,7 +34,7 @@ public class Wallet { // private HashMap rows = new HashMap<>(); // table for a wallet - private HashMap rows = new HashMap(); + private HashMap rows = new HashMap(); private long high; private List listeners = new ArrayList(); @@ -42,14 +42,14 @@ public class Wallet { private HashMap transactionMap = new HashMap(); public void addNewKey(){ - AddressState addressState = new AddressState(); + AccountState addressState = new AccountState(); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); for (WalletListener listener : listeners) listener.valueChanged(); } public void importKey(byte[] privKey){ - AddressState addressState = new AddressState(ECKey.fromPrivate(privKey)); + AccountState addressState = new AccountState(ECKey.fromPrivate(privKey)); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); notifyListeners(); @@ -59,11 +59,11 @@ public class Wallet { this.listeners.add(walletListener); } - public Collection getAddressStateCollection(){ + public Collection getAddressStateCollection(){ return rows.values(); } - public AddressState getAddressState(byte[] addressBytes){ + public AccountState getAddressState(byte[] addressBytes){ String address = Hex.toHexString(addressBytes); return rows.get(address); } @@ -75,7 +75,7 @@ public class Wallet { public BigInteger totalBalance(){ BigInteger sum = BigInteger.ZERO; - for (AddressState addressState : rows.values()){ + for (AccountState addressState : rows.values()){ sum = sum.add(addressState.getBalance()); } return sum; @@ -86,7 +86,7 @@ public class Wallet { transactionMap.put(new BigInteger(transaction.getHash()), transaction ); byte[] senderAddress = transaction.getSender(); - AddressState senderState = rows.get(Hex.toHexString(senderAddress)); + AccountState senderState = rows.get(Hex.toHexString(senderAddress)); if (senderState != null){ BigInteger value = new BigInteger(transaction.getValue()); @@ -95,7 +95,7 @@ public class Wallet { } byte[] receiveAddress = transaction.getReceiveAddress(); - AddressState receiverState = rows.get(Hex.toHexString(receiveAddress)); + AccountState receiverState = rows.get(Hex.toHexString(receiveAddress)); if (receiverState != null){ receiverState.addToBalance(new BigInteger(1, transaction.getValue())); } @@ -214,7 +214,7 @@ public class Wallet { walletElement.setAttributeNode(high); int i = 0; - for (AddressState addressState : getAddressStateCollection()){ + for (AccountState addressState : getAddressStateCollection()){ Element raw = doc.createElement("raw"); Attr id = doc.createAttribute("id"); diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ContractCallDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/ContractCallDialog.java index a589a9dd..1d5a6c14 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ContractCallDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ContractCallDialog.java @@ -1,6 +1,6 @@ package org.ethereum.gui; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.core.Transaction; import org.ethereum.manager.MainData; import org.ethereum.net.client.ClientPeer; @@ -135,10 +135,10 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{ JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent()); editor.setForeground(Color.RED); - Collection addressStates = + Collection addressStates = MainData.instance.getWallet().getAddressStateCollection(); - for (AddressState addressState : addressStates){ + for (AccountState addressState : addressStates){ creatorAddressCombo.addItem(new AddressStateWraper(addressState)); } @@ -229,7 +229,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{ byte[] contractAddress = Hex.decode( contractAddrInput.getText()); - AddressState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState(); + AccountState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState(); byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes(); byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray(); @@ -271,13 +271,13 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{ public class AddressStateWraper { - private AddressState addressState; + private AccountState addressState; - public AddressStateWraper(AddressState addressState) { + public AddressStateWraper(AccountState addressState) { this.addressState = addressState; } - public AddressState getAddressState() { + public AccountState getAddressState() { return addressState; } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ContractSubmitDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/ContractSubmitDialog.java index 5bf2c4a9..e4faaddb 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ContractSubmitDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ContractSubmitDialog.java @@ -1,6 +1,6 @@ package org.ethereum.gui; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.core.Transaction; import org.ethereum.manager.MainData; import org.ethereum.net.client.ClientPeer; @@ -143,10 +143,10 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog{ JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent()); editor.setForeground(Color.RED); - Collection addressStates = + Collection addressStates = MainData.instance.getWallet().getAddressStateCollection(); - for (AddressState addressState : addressStates){ + for (AccountState addressState : addressStates){ creatorAddressCombo.addItem(new AddressStateWraper(addressState)); } @@ -234,7 +234,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog{ public void submitContract(){ - AddressState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState(); + AccountState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState(); byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes(); byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray(); @@ -276,7 +276,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog{ public static void main(String args[]) { - AddressState as = new AddressState(); + AccountState as = new AccountState(); ContractSubmitDialog pod = new ContractSubmitDialog(null, null); pod.setVisible(true); @@ -284,13 +284,13 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog{ public class AddressStateWraper{ - private AddressState addressState; + private AccountState addressState; - public AddressStateWraper(AddressState addressState) { + public AddressStateWraper(AccountState addressState) { this.addressState = addressState; } - public AddressState getAddressState() { + public AccountState getAddressState() { return addressState; } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java index 1cb498e9..a922cef9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java @@ -1,6 +1,6 @@ package org.ethereum.gui; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.core.Transaction; import org.ethereum.manager.MainData; import org.ethereum.net.client.ClientPeer; @@ -26,14 +26,14 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ PayOutDialog dialog; - AddressState addressState = null; + AccountState addressState = null; JLabel statusMsg = null; final JTextField receiverInput; final JTextField amountInput; final JTextField feeInput; - public PayOutDialog(Frame parent, final AddressState addressState) { + public PayOutDialog(Frame parent, final AccountState addressState) { super(parent, "Payout details: ", false); dialog = this; @@ -260,7 +260,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ } public static void main(String args[]) { - AddressState as = new AddressState(); + AccountState as = new AccountState(); PayOutDialog pod = new PayOutDialog(null, as); pod.setVisible(true); } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java b/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java index 50c671ba..c4275f4f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/WalletAddressPanel.java @@ -1,6 +1,6 @@ package org.ethereum.gui; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.util.Utils; import org.spongycastle.util.encoders.Hex; @@ -21,7 +21,7 @@ import java.net.URL; */ public class WalletAddressPanel extends JPanel{ - public WalletAddressPanel(final AddressState addressState) { + public WalletAddressPanel(final AccountState addressState) { final WalletAddressPanel walletAddressPanel = this; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java b/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java index 5499478e..7d685cca 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/WalletWindow.java @@ -1,6 +1,6 @@ package org.ethereum.gui; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.core.Wallet; import org.ethereum.manager.MainData; @@ -50,7 +50,7 @@ public class WalletWindow extends JFrame implements Wallet.WalletListener{ Wallet wallet = MainData.instance.getWallet(); - for (AddressState addressState : wallet.getAddressStateCollection()){ + for (AccountState addressState : wallet.getAddressStateCollection()){ WalletAddressPanel rowPanel = new WalletAddressPanel(addressState); diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java index 8ded8051..81c6d41f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java @@ -11,7 +11,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.ethereum.core.AddressState; +import org.ethereum.core.AccountState; import org.ethereum.core.Blockchain; import org.ethereum.core.Transaction; import org.ethereum.core.Wallet; @@ -51,7 +51,7 @@ public class MainData { ECKey key = ECKey.fromPrivate(cowAddr); wallet.importKey(cowAddr); - AddressState state = wallet.getAddressState(key.getAddress()); + AccountState state = wallet.getAddressState(key.getAddress()); state.addToBalance(BigInteger.valueOf(2).pow(200)); // 1606938044258990275541962092341162602522202993782792835301376 wallet.importKey(HashUtil.sha3("cat".getBytes())); diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java index 8459ad07..e511994c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java @@ -19,6 +19,12 @@ public class Cache { nodes = new HashMap(); } + /** + * Put the node in the cache if RLP encoded value is longer than 32 bytes + * + * @param o the Node which could be a pair-, multi-item Node or single Value + * @return sha3 hash of RLP encoded node if length > 32 otherwise return node itself + */ public Object put(Object o) { Value value = new Value(o); byte[] enc = value.encode(); diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java index debad971..df30ff82 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java @@ -82,10 +82,20 @@ public class Trie { * @param value */ public void update(String key, String value) { + this.update(key.getBytes(), value.getBytes()); + } + + /** + * Insert key/value pair into trie + * + * @param key + * @param value + */ + public void update(byte[] key, byte[] value) { if (key == null) throw new NullPointerException("Key should not be blank"); - byte[] k = binToNibbles(key.getBytes()); - this.root = this.insertOrDelete(this.root, k, value.getBytes()); + byte[] k = binToNibbles(key); + this.root = this.insertOrDelete(this.root, k, value); } /** @@ -94,10 +104,10 @@ public class Trie { * @param key * @return value */ - public String get(String key) { + public byte[] get(String key) { byte[] k = binToNibbles(key.getBytes()); Value c = new Value( this.get(this.root, k) ); - return c.asString(); + return c.asBytes(); } /** @@ -106,7 +116,7 @@ public class Trie { * @param key */ public void delete(String key) { - this.update(key, ""); + this.update(key.getBytes(), "".getBytes()); } /**************************************** @@ -322,7 +332,7 @@ public class Trie { // Simple compare function which compared the tries based on their stateRoot public boolean cmp(Trie trie) { - return this.getRootHash().equals(trie.getRootHash()); + return Arrays.equals(this.getRootHash(), trie.getRootHash()); } // Save the cached value to the database. @@ -368,19 +378,18 @@ public class Trie { return slice; } - public String getRootHash() { + public byte[] getRootHash() { Object root = this.getRoot(); if (root == null || (root instanceof byte[] && ((byte[]) root).length == 0) || (root instanceof String && "".equals((String) root))) { - return ""; + return new byte[0]; } else if (root instanceof byte[]) { - return Hex.toHexString((byte[])this.getRoot()); + return (byte[]) this.getRoot(); } else { Value rootValue = new Value(this.getRoot()); byte[] val = rootValue.encode(); - byte[] key = HashUtil.sha3(val); - return Hex.toHexString(key); + return HashUtil.sha3(val); } } } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java index c119fac3..48562cc1 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java @@ -9,6 +9,7 @@ import java.util.Queue; import static java.util.Arrays.copyOfRange; import static org.ethereum.util.ByteUtil.byteArrayToInt; import static org.spongycastle.util.Arrays.concatenate; +import static org.spongycastle.util.BigIntegers.asUnsignedByteArray; import java.util.List; @@ -233,7 +234,7 @@ public class RLP { } byte[] valueBytes = new byte[length]; System.arraycopy(data, index, valueBytes, 0, length); - value = new BigInteger(valueBytes); + value = new BigInteger(1, valueBytes); return value; } @@ -783,7 +784,7 @@ public class RLP { if(srcBigInteger == BigInteger.ZERO) return encodeByte((byte)0); else - return encodeElement(srcBigInteger.toByteArray()); + return encodeElement(asUnsignedByteArray(srcBigInteger)); } public static byte[] encodeElement(byte[] srcData) { @@ -878,13 +879,13 @@ public class RLP { return inputString.getBytes(); } else if(input instanceof Long) { Long inputLong = (Long) input; - return (inputLong == 0) ? new byte[0] : BigInteger.valueOf(inputLong).toByteArray(); + return (inputLong == 0) ? new byte[0] : asUnsignedByteArray(BigInteger.valueOf(inputLong)); } else if(input instanceof Integer) { Integer inputInt = (Integer) input; - return (inputInt == 0) ? new byte[0] : BigInteger.valueOf(inputInt.longValue()).toByteArray(); + return (inputInt == 0) ? new byte[0] : asUnsignedByteArray(BigInteger.valueOf(inputInt.intValue())); } else if(input instanceof BigInteger) { BigInteger inputBigInt = (BigInteger) input; - return (inputBigInt == BigInteger.ZERO) ? new byte[0] : inputBigInt.toByteArray(); + return (inputBigInt == BigInteger.ZERO) ? new byte[0] : asUnsignedByteArray(inputBigInt); } else if (input instanceof Value) { Value val = (Value) input; return toBytes(val.asObj()); diff --git a/ethereumj-core/src/main/java/org/ethereum/util/Value.java b/ethereumj-core/src/main/java/org/ethereum/util/Value.java index 9cb30af5..083f3e42 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/Value.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/Value.java @@ -44,7 +44,7 @@ public class Value { if (isInt()) { return (Integer) value; } else if (isBytes()) { - return new BigInteger(asBytes()).intValue(); + return new BigInteger(1, asBytes()).intValue(); } return 0; } @@ -53,7 +53,7 @@ public class Value { if (isLong()) { return (Long) value; } else if (isBytes()) { - return new BigInteger(asBytes()).longValue(); + return new BigInteger(1, asBytes()).longValue(); } return 0; } diff --git a/ethereumj-core/src/test/java/org/ethereum/core/AccountStateTest.java b/ethereumj-core/src/test/java/org/ethereum/core/AccountStateTest.java new file mode 100644 index 00000000..52b5aa97 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/core/AccountStateTest.java @@ -0,0 +1,19 @@ +package org.ethereum.core; + +import static org.junit.Assert.*; + +import java.math.BigInteger; + +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; + +public class AccountStateTest { + + @Test + public void testGetEncoded() { + String expected = "f83e9a01000000000000000000000000000000000000000000000000008080a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + assertEquals(expected, Hex.toHexString(acct.getEncoded())); + } + +} diff --git a/ethereumj-core/src/test/java/org/ethereum/core/StateTest.java b/ethereumj-core/src/test/java/org/ethereum/core/StateTest.java new file mode 100644 index 00000000..ff785cdd --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/core/StateTest.java @@ -0,0 +1,53 @@ +package org.ethereum.core; + +import static org.junit.Assert.*; + +import java.math.BigInteger; + +import org.ethereum.trie.MockDB; +import org.ethereum.trie.Trie; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; + +public class StateTest { + + @Test + public void testGenesisAccounts() { + Trie trie = new Trie(new MockDB()); + + // 2ef47100e0787b915105fd5e3f4ff6752079d5cb # (M) + AccountState acct5 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("2ef47100e0787b915105fd5e3f4ff6752079d5cb"), acct5.getEncoded()); + + // 1a26338f0d905e295fccb71fa9ea849ffa12aaf4 # (A) + AccountState acct4 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"), acct4.getEncoded()); + + // e6716f9544a56c530d868e4bfbacb172315bdead # (J) + AccountState acct2 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("e6716f9544a56c530d868e4bfbacb172315bdead"), acct2.getEncoded()); + + // 8a40bfaa73256b60764c1bf40675a99083efb075 # (G) + AccountState acct1 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("8a40bfaa73256b60764c1bf40675a99083efb075"), acct1.getEncoded()); + + // e4157b34ea9615cfbde6b4fda419828124b70c78 # (CH) + AccountState acct8 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("e4157b34ea9615cfbde6b4fda419828124b70c78"), acct8.getEncoded()); + + // 1e12515ce3e0f817a4ddef9ca55788a1d66bd2df # (V) + AccountState acct3 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"), acct3.getEncoded()); + + // 6c386a4b26f73c802f34673f7248bb118f97424a # (HH) + AccountState acct7 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("6c386a4b26f73c802f34673f7248bb118f97424a"), acct7.getEncoded()); + + // cd2a3d9f938e13cd947ec05abc7fe734df8dd826 # (R) + AccountState acct6 = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200)); + trie.update(Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"), acct6.getEncoded()); + + assertEquals("12582945fc5ad12c3e7b67c4fc37a68fc0d52d995bb7f7291ff41a2739a7ca16", Hex.toHexString(trie.getRootHash())); + } + +} diff --git a/ethereumj-core/src/test/java/org/ethereum/core/WalletTest.java b/ethereumj-core/src/test/java/org/ethereum/core/WalletTest.java index a86fc66a..b926261f 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/WalletTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/WalletTest.java @@ -29,8 +29,8 @@ public class WalletTest { wallet.importKey(catKey.getPrivKeyBytes()); - AddressState cowAddressState = (AddressState) wallet.getAddressState(cowKey.getAddress()); - AddressState catAddressState = (AddressState) wallet.getAddressState(catKey.getAddress()); + AccountState cowAddressState = (AccountState) wallet.getAddressState(cowKey.getAddress()); + AccountState catAddressState = (AccountState) wallet.getAddressState(catKey.getAddress()); cowAddressState.addToBalance(new BigInteger("234234")); catAddressState.addToBalance(new BigInteger("84758")); diff --git a/ethereumj-core/src/test/java/org/ethereum/crypto/CryptoTest.java b/ethereumj-core/src/test/java/org/ethereum/crypto/CryptoTest.java index 6c4463b1..4937a338 100644 --- a/ethereumj-core/src/test/java/org/ethereum/crypto/CryptoTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/crypto/CryptoTest.java @@ -90,7 +90,7 @@ public class CryptoTest { // todo: https://tools.ietf.org/html/rfc6979#section-2.2 // todo: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java - System.out.println(new BigInteger(1, Hex.decode("3913517ebd3c0c65000000"))); + System.out.println(new BigInteger(Hex.decode("3913517ebd3c0c65000000"))); System.out.println(Utils.getValueShortString(new BigInteger("69000000000000000000000000"))); diff --git a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java index ac062d07..0397a1fb 100644 --- a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.*; import org.ethereum.trie.Trie; import org.junit.Test; +import org.spongycastle.util.encoders.Hex; public class TrieTest { @@ -34,7 +35,7 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update("", dog); - assertEquals(dog, trie.get("")); + assertEquals(dog, new String(trie.get(""))); } @Test @@ -42,7 +43,7 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); } @Test @@ -50,33 +51,33 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); + assertEquals(LONG_STRING, new String(trie.get(cat))); } @Test public void testInsertMultipleItems1() { Trie trie = new Trie(mockDb); trie.update(ca, dude); - assertEquals(dude, trie.get(ca)); + assertEquals(dude, new String(trie.get(ca))); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(dog, test); - assertEquals(test, trie.get(dog)); + assertEquals(test, new String(trie.get(dog))); trie.update(doge, LONG_STRING); - assertEquals(LONG_STRING, trie.get(doge)); + assertEquals(LONG_STRING, new String(trie.get(doge))); trie.update(test, LONG_STRING); - assertEquals(LONG_STRING, trie.get(test)); + assertEquals(LONG_STRING, new String(trie.get(test))); // Test if everything is still there - assertEquals(dude, trie.get(ca)); - assertEquals(dog, trie.get(cat)); - assertEquals(test, trie.get(dog)); - assertEquals(LONG_STRING, trie.get(doge)); - assertEquals(LONG_STRING, trie.get(test)); + assertEquals(dude, new String(trie.get(ca))); + assertEquals(dog, new String(trie.get(cat))); + assertEquals(test, new String(trie.get(dog))); + assertEquals(LONG_STRING, new String(trie.get(doge))); + assertEquals(LONG_STRING, new String(trie.get(test))); } @Test @@ -84,26 +85,26 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(ca, dude); - assertEquals(dude, trie.get(ca)); + assertEquals(dude, new String(trie.get(ca))); trie.update(doge, LONG_STRING); - assertEquals(LONG_STRING, trie.get(doge)); + assertEquals(LONG_STRING, new String(trie.get(doge))); trie.update(dog, test); - assertEquals(test, trie.get(dog)); + assertEquals(test, new String(trie.get(dog))); trie.update(test, LONG_STRING); - assertEquals(LONG_STRING, trie.get(test)); + assertEquals(LONG_STRING, new String(trie.get(test))); // Test if everything is still there - assertEquals(dog, trie.get(cat)); - assertEquals(dude, trie.get(ca)); - assertEquals(LONG_STRING, trie.get(doge)); - assertEquals(test, trie.get(dog)); - assertEquals(LONG_STRING, trie.get(test)); + assertEquals(dog, new String(trie.get(cat))); + assertEquals(dude, new String(trie.get(ca))); + assertEquals(LONG_STRING, new String(trie.get(doge))); + assertEquals(test, new String(trie.get(dog))); + assertEquals(LONG_STRING, new String(trie.get(test))); } @Test @@ -111,19 +112,19 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(cat, dog+"1"); - assertEquals(dog+"1", trie.get(cat)); + assertEquals(dog+"1", new String(trie.get(cat))); } @Test public void testUpdateLongToLongString() { Trie trie = new Trie(mockDb); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); + assertEquals(LONG_STRING, new String(trie.get(cat))); trie.update(cat, LONG_STRING+"1"); - assertEquals(LONG_STRING+"1", trie.get(cat)); + assertEquals(LONG_STRING+"1", new String(trie.get(cat))); } @Test @@ -131,10 +132,10 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(cat, LONG_STRING+"1"); - assertEquals(LONG_STRING+"1", trie.get(cat)); + assertEquals(LONG_STRING+"1", new String(trie.get(cat))); } @Test @@ -142,10 +143,10 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); + assertEquals(LONG_STRING, new String(trie.get(cat))); trie.update(cat, dog+"1"); - assertEquals(dog+"1", trie.get(cat)); + assertEquals(dog+"1", new String(trie.get(cat))); } @Test @@ -155,15 +156,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(ca, dude); - assertEquals(dude, trie.get(ca)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(dude, new String(trie.get(ca))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(ca); - assertEquals("", trie.get(ca)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(ca))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -173,15 +174,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(ca, dude); - assertEquals(dude, trie.get(ca)); + assertEquals(dude, new String(trie.get(ca))); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(dog, new String(trie.get(cat))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(cat); - assertEquals("", trie.get(cat)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(cat))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -191,15 +192,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dude); - assertEquals(dude, trie.get(cat)); + assertEquals(dude, new String(trie.get(cat))); trie.update(dog, test); - assertEquals(test, trie.get(dog)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(test, new String(trie.get(dog))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(dog); - assertEquals("", trie.get(dog)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(dog))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -209,15 +210,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); + assertEquals(LONG_STRING, new String(trie.get(cat))); trie.update(dog, LONG_STRING); - assertEquals(LONG_STRING, trie.get(dog)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(LONG_STRING, new String(trie.get(dog))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(dog); - assertEquals("", trie.get(dog)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(dog))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -227,15 +228,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(ca, LONG_STRING); - assertEquals(LONG_STRING, trie.get(ca)); + assertEquals(LONG_STRING, new String(trie.get(ca))); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(LONG_STRING, new String(trie.get(cat))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(cat); - assertEquals("", trie.get(cat)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(cat))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -245,15 +246,15 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); + assertEquals(LONG_STRING, new String(trie.get(cat))); trie.update(ca, LONG_STRING); - assertEquals(LONG_STRING, trie.get(ca)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(LONG_STRING, new String(trie.get(ca))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(ca); - assertEquals("", trie.get(ca)); - assertEquals(ROOT_HASH_AFTER, trie.getRootHash()); + assertEquals("", new String(trie.get(ca))); + assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash())); } @Test @@ -264,28 +265,28 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(cat, dog); - assertEquals(dog, trie.get(cat)); + assertEquals(dog, new String(trie.get(cat))); trie.update(ca, dude); - assertEquals(dude, trie.get(ca)); + assertEquals(dude, new String(trie.get(ca))); trie.update(doge, LONG_STRING); - assertEquals(LONG_STRING, trie.get(doge)); + assertEquals(LONG_STRING, new String(trie.get(doge))); trie.update(dog, test); - assertEquals(test, trie.get(dog)); + assertEquals(test, new String(trie.get(dog))); trie.update(test, LONG_STRING); - assertEquals(LONG_STRING, trie.get(test)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(LONG_STRING, new String(trie.get(test))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(dog); - assertEquals("", trie.get(dog)); - assertEquals(ROOT_HASH_AFTER1, trie.getRootHash()); + assertEquals("", new String(trie.get(dog))); + assertEquals(ROOT_HASH_AFTER1, Hex.toHexString(trie.getRootHash())); trie.delete(test); - assertEquals("", trie.get(test)); - assertEquals(ROOT_HASH_AFTER2, trie.getRootHash()); + assertEquals("", new String(trie.get(test))); + assertEquals(ROOT_HASH_AFTER2, Hex.toHexString(trie.getRootHash())); } @Test @@ -296,39 +297,39 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update(c, LONG_STRING); - assertEquals(LONG_STRING, trie.get(c)); + assertEquals(LONG_STRING, new String(trie.get(c))); trie.update(ca, LONG_STRING); - assertEquals(LONG_STRING, trie.get(ca)); + assertEquals(LONG_STRING, new String(trie.get(ca))); trie.update(cat, LONG_STRING); - assertEquals(LONG_STRING, trie.get(cat)); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(LONG_STRING, new String(trie.get(cat))); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(ca); - assertEquals("", trie.get(ca)); - assertEquals(ROOT_HASH_AFTER1, trie.getRootHash()); + assertEquals("", new String(trie.get(ca))); + assertEquals(ROOT_HASH_AFTER1, Hex.toHexString(trie.getRootHash())); trie.delete(cat); - assertEquals("", trie.get(cat)); - assertEquals(ROOT_HASH_AFTER2, trie.getRootHash()); + assertEquals("", new String(trie.get(cat))); + assertEquals(ROOT_HASH_AFTER2, Hex.toHexString(trie.getRootHash())); } @Test public void testDeleteAll() { String ROOT_HASH_BEFORE = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d"; Trie trie = new Trie(mockDb); - assertEquals(ROOT_HASH_EMPTY, trie.getRootHash()); + assertEquals(ROOT_HASH_EMPTY, Hex.toHexString(trie.getRootHash())); trie.update(ca, dude); trie.update(cat, dog); trie.update(doge, LONG_STRING); - assertEquals(ROOT_HASH_BEFORE, trie.getRootHash()); + assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash())); trie.delete(ca); trie.delete(cat); trie.delete(doge); - assertEquals(ROOT_HASH_EMPTY, trie.getRootHash()); + assertEquals(ROOT_HASH_EMPTY, Hex.toHexString(trie.getRootHash())); } @Test @@ -339,12 +340,12 @@ public class TrieTest { trie1.update(doge, LONG_STRING); trie2.update(doge, LONG_STRING); assertTrue("Expected tries to be equal", trie1.cmp(trie2)); - assertEquals(trie1.getRootHash(), trie2.getRootHash()); + assertEquals(Hex.toHexString(trie1.getRootHash()), Hex.toHexString(trie2.getRootHash())); trie1.update(dog, LONG_STRING); trie2.update(cat, LONG_STRING); assertFalse("Expected tries not to be equal", trie1.cmp(trie2)); - assertNotEquals(trie1.getRootHash(), trie2.getRootHash()); + assertNotEquals(Hex.toHexString(trie1.getRootHash()), Hex.toHexString(trie2.getRootHash())); } @Test @@ -386,12 +387,26 @@ public class TrieTest { @Test public void testTrieCopy() { - fail("To be implemented"); + Trie trie = new Trie(mockDb); + trie.update("doe", "reindeer"); + Trie trie2 = trie.copy(); + assertFalse(trie.equals(trie2)); // avoid possibility that its just a reference copy + assertEquals(Hex.toHexString(trie.getRootHash()), Hex.toHexString(trie2.getRootHash())); + assertTrue(trie.cmp(trie2)); } @Test public void testTrieUndo() { - fail("To be implemented"); + Trie trie = new Trie(mockDb); + trie.update("doe", "reindeer"); + assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash())); + trie.sync(); + + trie.update("dog", "puppy"); + assertEquals("05ae693aac2107336a79309e0c60b24a7aac6aa3edecaef593921500d33c63c4", Hex.toHexString(trie.getRootHash())); + + trie.undo(); + assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash())); } // Using tests from: https://github.com/ethereum/tests/blob/master/trietest.json @@ -401,20 +416,20 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - assertEquals("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab", trie.getRootHash()); + assertEquals("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab", Hex.toHexString(trie.getRootHash())); } @Test public void testDogs() { Trie trie = new Trie(mockDb); trie.update("doe", "reindeer"); - assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", trie.getRootHash()); + assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash())); trie.update("dog", "puppy"); - assertEquals("05ae693aac2107336a79309e0c60b24a7aac6aa3edecaef593921500d33c63c4", trie.getRootHash()); + assertEquals("05ae693aac2107336a79309e0c60b24a7aac6aa3edecaef593921500d33c63c4", Hex.toHexString(trie.getRootHash())); trie.update("dogglesworth", "cat"); - assertEquals("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3", trie.getRootHash()); + assertEquals("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3", Hex.toHexString(trie.getRootHash())); } @Test @@ -425,7 +440,7 @@ public class TrieTest { trie.update("doge", "coin"); trie.update("dog", "puppy"); - assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", trie.getRootHash()); + assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", Hex.toHexString(trie.getRootHash())); } @Test @@ -440,7 +455,7 @@ public class TrieTest { trie.update("dog", "puppy"); trie.update("shaman", ""); - assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", trie.getRootHash()); + assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", Hex.toHexString(trie.getRootHash())); } @Test @@ -450,7 +465,7 @@ public class TrieTest { trie.update("food", "bat"); trie.update("food", "bass"); - assertEquals("17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3", trie.getRootHash()); + assertEquals("17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3", Hex.toHexString(trie.getRootHash())); } @Test @@ -460,7 +475,7 @@ public class TrieTest { trie.update("be", "e"); trie.update("dog", "puppy"); trie.update("bed", "d"); - assertEquals("3f67c7a47520f79faa29255d2d3c084a7a6df0453116ed7232ff10277a8be68b", trie.getRootHash()); + assertEquals("3f67c7a47520f79faa29255d2d3c084a7a6df0453116ed7232ff10277a8be68b", Hex.toHexString(trie.getRootHash())); } @Test @@ -468,9 +483,9 @@ public class TrieTest { Trie trie = new Trie(mockDb); trie.update("test", "test"); - assertEquals("85d106d4edff3b7a4889e91251d0a87d7c17a1dda648ebdba8c6060825be23b8", trie.getRootHash()); + assertEquals("85d106d4edff3b7a4889e91251d0a87d7c17a1dda648ebdba8c6060825be23b8", Hex.toHexString(trie.getRootHash())); trie.update("te", "testy"); - assertEquals("8452568af70d8d140f58d941338542f645fcca50094b20f3c3d8c3df49337928", trie.getRootHash()); + assertEquals("8452568af70d8d140f58d941338542f645fcca50094b20f3c3d8c3df49337928", Hex.toHexString(trie.getRootHash())); } } diff --git a/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java b/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java index 031c51fa..6d8755fe 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/RLPTest.java @@ -85,9 +85,9 @@ public class RLPTest { BigInteger peerId = RLP.decodeBigInteger(payload, nextIndex); BigInteger expectedPeerId = - new BigInteger("-3757679129454624401847229560118336025674165800491763435653406124634392418475888088940922052683656239666728251000337405781915693264977453978537202650777524"); + new BigInteger("9650128800487972697726795438087510101805200020100629942070155319087371611597658887860952245483247188023303607186148645071838189546969115967896446355306572"); assertEquals(expectedPeerId, peerId); - + nextIndex = RLP.getNextElementIndex(payload, nextIndex); nextIndex = RLP.getFirstListElement(payload, nextIndex); ip = RLP.decodeIP4Bytes(payload, nextIndex); @@ -101,7 +101,7 @@ public class RLPTest { peerId = RLP.decodeBigInteger(payload, nextIndex); expectedPeerId = - new BigInteger("-3757679129454624401847229560118336025674165800491763435653406124634392418475888088940922052683656239666728251000337405781915693264977453978537202650777524"); + new BigInteger("9650128800487972697726795438087510101805200020100629942070155319087371611597658887860952245483247188023303607186148645071838189546969115967896446355306572"); assertEquals(expectedPeerId ,peerId); @@ -429,7 +429,7 @@ public class RLPTest { assertEquals(expected, Hex.toHexString(encoderesult)); byte[] decodeResult = (byte[]) RLP.decode(encoderesult, 0).getDecoded(); - assertEquals(test, new BigInteger(decodeResult)); + assertEquals(test, new BigInteger(1, decodeResult)); } @Test @@ -615,10 +615,10 @@ public class RLPTest { assertEquals(test11, byteArrayToInt(decodedData)); decodedData = (byte[]) RLP.decode(Hex.decode(result12), pos).getDecoded(); - assertTrue(test12.compareTo(new BigInteger(decodedData)) == 0); + assertTrue(test12.compareTo(new BigInteger(1, decodedData)) == 0); decodedData = (byte[]) RLP.decode(Hex.decode(result13), pos).getDecoded(); - assertTrue(test13.compareTo(new BigInteger(decodedData)) == 0); + assertTrue(test13.compareTo(new BigInteger(1, decodedData)) == 0); // Need to test with different expected value, because decoding doesn't recognize types Object testObject1 = RLP.decode(Hex.decode(result14), pos).getDecoded(); diff --git a/ethereumj-core/src/test/java/org/ethereum/util/RlpTestData.java b/ethereumj-core/src/test/java/org/ethereum/util/RlpTestData.java index 44faaad2..6c60bb78 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/RlpTestData.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/RlpTestData.java @@ -41,7 +41,7 @@ public class RlpTestData { public static String result11 = "8203e8"; public static BigInteger test12 = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639935"); - public static String result12 = "a100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + public static String result12 = "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; public static BigInteger test13 = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639936"); public static String result13 = "a1010000000000000000000000000000000000000000000000000000000000000000"; From 6b22137c05663697cfa3ea63e836812497cf694b Mon Sep 17 00:00:00 2001 From: nicksavers Date: Thu, 29 May 2014 21:52:18 +0200 Subject: [PATCH 3/4] Merge pushes from romanman/master --- .../org/ethereum/config/SystemProperties.java | 5 + .../java/org/ethereum/core/AddressState.java | 13 +- .../main/java/org/ethereum/core/Block.java | 67 ++++--- .../java/org/ethereum/core/Blockchain.java | 142 ++++++++++++++ .../main/java/org/ethereum/core/Genesis.java | 32 ++-- .../main/java/org/ethereum/core/GoState.java | 160 ++++++++++++++++ .../main/java/org/ethereum/core/State.java | 12 ++ .../java/org/ethereum/core/StateObject.java | 180 ++++++++++++++++++ .../org/ethereum/core/StateObjectCache.java | 36 ++++ .../java/org/ethereum/core/Transaction.java | 7 +- .../main/java/org/ethereum/core/Wallet.java | 13 +- .../src/main/java/org/ethereum/db/Config.java | 8 + .../main/java/org/ethereum/db/Database.java | 116 +++++++++++ .../org/ethereum/{geodb => db}/IpGeoDB.java | 2 +- .../org/ethereum/gui/BlockChainTable.java | 10 +- .../org/ethereum/gui/BlockTableModel.java | 6 +- .../ethereum/gui/ConnectionConsoleWindow.java | 31 --- .../java/org/ethereum/gui/PayOutDialog.java | 4 +- .../org/ethereum/gui/PeersTableModel.java | 2 +- .../main/java/org/ethereum/gui/ToolBar.java | 9 - .../java/org/ethereum/manager/MainData.java | 152 +++------------ .../net/client/EthereumProtocolHandler.java | 6 +- .../ethereum/net/submit/TransactionTask.java | 5 +- .../main/java/org/ethereum/trie/Cache.java | 16 -- .../java/org/ethereum/util/DecodeResult.java | 21 ++ .../src/main/java/org/ethereum/util/RLP.java | 2 +- .../src/main/resources/system.properties | 37 +++- .../org/ethereum/core/TransactionTest.java | 9 +- .../java/org/ethereum/net/MessagesTest.java | 8 +- .../java/org/ethereum/util/UtilsTest.java | 10 +- 30 files changed, 838 insertions(+), 283 deletions(-) create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/GoState.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/State.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/StateObject.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/db/Config.java create mode 100644 ethereumj-core/src/main/java/org/ethereum/db/Database.java rename ethereumj-core/src/main/java/org/ethereum/{geodb => db}/IpGeoDB.java (97%) diff --git a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java index e46f3b45..6bf05e80 100644 --- a/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java +++ b/ethereumj-core/src/main/java/org/ethereum/config/SystemProperties.java @@ -91,6 +91,11 @@ public class SystemProperties { if(prop.isEmpty()) return 30303; return Integer.parseInt(prop.getProperty("peer.discovery.port")); } + + public boolean databaseReset(){ + if(prop.isEmpty()) return false; + return Boolean.parseBoolean(prop.getProperty("database.reset")); + } public String activePeerIP(){ if(prop.isEmpty()) return "54.201.28.117"; diff --git a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java b/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java index e73b1f06..b953b455 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/AddressState.java @@ -17,14 +17,11 @@ public class AddressState { private BigInteger balance; public AddressState() { - ecKey = new ECKey(Utils.getRandom()); - nonce = BigInteger.ZERO; - balance = BigInteger.ZERO; + this(new ECKey(Utils.getRandom())); } public AddressState(ECKey ecKey) { - this(); - this.ecKey = ecKey; + this(ecKey, BigInteger.ZERO, BigInteger.ZERO); } public AddressState(ECKey ecKey, BigInteger nonce, BigInteger balance) { @@ -41,8 +38,8 @@ public class AddressState { return nonce; } - public void incrementTheNonce(){ - nonce = nonce.add(BigInteger.ONE); + public void incrementNonce(){ + this.nonce = nonce.add(BigInteger.ONE); } public BigInteger getBalance() { @@ -50,6 +47,6 @@ public class AddressState { } public void addToBalance(BigInteger value){ - balance = balance.add(value); + this.balance = balance.add(value); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index 392f5433..f754a732 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -7,6 +7,7 @@ import org.ethereum.util.RLPElement; import org.ethereum.util.RLPItem; import org.ethereum.util.RLPList; import org.ethereum.util.Utils; +import org.spongycastle.util.BigIntegers; import java.math.BigInteger; import java.util.ArrayList; @@ -21,10 +22,8 @@ import java.util.List; */ public class Block { - private static int LIMIT_FACTOR = (int) Math.pow(2, 16); - private static double EMA_FACTOR = 1.5; - /* A scalar value equal to the current limit of gas expenditure per block */ - private static int GAS_LIMIT = (int) Math.pow(10, 6); + /* A scalar value equal to the mininum limit of gas expenditure per block */ + private static long MIN_GAS_LIMIT = BigInteger.valueOf(10).pow(4).longValue(); private byte[] rlpEncoded; private boolean parsed = false; @@ -316,29 +315,49 @@ public class Block { } /** - * Because every transaction published into the blockchain imposes on the - * network the cost of needing to download and verify it, there is a need - * for some regulatory mechanism to prevent abuse. - * - * To solve this we simply institute a floating cap: - * - * No block can have more operations than BLK_LIMIT_FACTOR times - * the long-term exponential moving average. + * This mechanism enforces a homeostasis in terms of the time between blocks; + * a smaller period between the last two blocks results in an increase in the + * difficulty level and thus additional computation required, lengthening the + * likely next period. Conversely, if the period is too large, the difficulty, + * and expected time to the next block, is reduced. + */ + private boolean isValid() { + boolean isValid = false; + + // verify difficulty meets requirements + isValid = this.getDifficulty() == this.calcDifficulty(); + // verify gasLimit meets requirements + isValid = this.getGasLimit() == this.calcGasLimit(); + // verify timestamp meets requirements + isValid = this.getTimestamp() > this.getParent().getTimestamp(); + + return isValid; + } + + /** + * Calculate GasLimit + * max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024) * - * Specifically: - * - * blk.oplimit = floor((blk.parent.oplimit * (EMAFACTOR - 1) - * + floor(GAS_LIMIT * BLK_LIMIT_FACTOR)) / EMA_FACTOR) - * - * BLK_LIMIT_FACTOR and EMA_FACTOR are constants that will be set - * to 65536 and 1.5 for the time being, but will likely be changed - * after further analysis. - * * @return */ - public double getOplimit() { - return Math.floor((this.getParent().getOplimit() * (EMA_FACTOR - 1) - + Math.floor(GAS_LIMIT * LIMIT_FACTOR)) / EMA_FACTOR); + public long calcGasLimit() { + if (parentHash == null) + return 1000000L; + else { + Block parent = this.getParent(); + return Math.max(MIN_GAS_LIMIT, (parent.gasLimit * (1024 - 1) + (parent.gasUsed * 6 / 5)) / 1024); + } + } + + public byte[] calcDifficulty() { + if (parentHash == null) + return Genesis.DIFFICULTY; + else { + Block parent = this.getParent(); + long parentDifficulty = new BigInteger(1, parent.difficulty).longValue(); + long newDifficulty = timestamp >= parent.timestamp + 42 ? parentDifficulty - (parentDifficulty >> 10) : (parentDifficulty + (parentDifficulty >> 10)); + return BigIntegers.asUnsignedByteArray(BigInteger.valueOf(newDifficulty)); + } } public byte[] getEncoded() { diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java new file mode 100644 index 00000000..23907477 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java @@ -0,0 +1,142 @@ +package org.ethereum.core; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.ethereum.db.Config; +import org.ethereum.db.Database; +import org.ethereum.net.message.StaticMessages; +import org.ethereum.net.submit.PendingTransaction; +import org.iq80.leveldb.DBIterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongycastle.util.encoders.Hex; + +public class Blockchain extends ArrayList { + + private static Logger logger = LoggerFactory.getLogger(Blockchain.class); + + private Database db; + private Wallet wallet; + private long gasPrice = 1000; + private Block lastBlock = new Genesis(); + + private Map pendingTransactions = + Collections.synchronizedMap(new HashMap()); + + public Blockchain(Wallet wallet) { + this.db = Config.CHAIN_DB; + this.wallet = wallet; + + DBIterator iterator = db.iterator(); + try { + for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { + byte[] value = iterator.peekNext().getValue(); + Block block = new Block(value); + if(block.getNumber() > lastBlock.getNumber()) lastBlock = block; + this.add(new Block(value)); + } + } finally { + // Make sure you close the iterator to avoid resource leaks. + try { + iterator.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public Block getLastBlock() { + return lastBlock; + } + + public void addBlocks(List blocks) { + + // TODO: redesign this part when the state part and the genesis block is ready + + if (blocks.isEmpty()) return; + + Block firstBlockToAdd = blocks.get(blocks.size() - 1); + + // if it is the first block to add + // check that the parent is the genesis + if (this.isEmpty() && + !Arrays.equals(StaticMessages.GENESIS_HASH, firstBlockToAdd.getParentHash())){ + return; + } + // if there is some blocks already keep chain continuity + if (!this.isEmpty() ){ + Block lastBlock = this.get(this.size() - 1); + String hashLast = Hex.toHexString(lastBlock.getHash()); + String blockParentHash = Hex.toHexString(firstBlockToAdd.getParentHash()); + if (!hashLast.equals(blockParentHash)) return; + } + for (int i = blocks.size() - 1; i >= 0 ; --i){ + Block block = blocks.get(i); + this.add(block); + if(block.getNumber() > lastBlock.getNumber()) lastBlock = block; + db.put(block.getHash(), block.getEncoded()); + if (logger.isDebugEnabled()) + logger.debug("block added to the chain with hash: {}", Hex.toHexString(block.getHash())); + this.gasPrice = block.getMinGasPrice(); + + wallet.processBlock(block); + } + // Remove all pending transactions as they already approved by the net + for (Block block : blocks){ + for (Transaction tx : block.getTransactionsList()){ + if (logger.isDebugEnabled()) + logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash())); + removePendingTransaction(tx); + } + } + + logger.info("*** Block chain size: [ {} ]", this.size()); + } + + /* + * 1) the dialog put a pending transaction on the list + * 2) the dialog send the transaction to a net + * 3) wherever the transaction got for the wire in will change to approve state + * 4) only after the approve a) Wallet state changes + * 5) After the block is received with that tx the pending been clean up + */ + public PendingTransaction addPendingTransaction(Transaction transaction) { + + BigInteger hash = new BigInteger(transaction.getHash()); + logger.info("pending transaction placed hash: {} ", hash.toString(16) ); + + PendingTransaction pendingTransaction = pendingTransactions.get(hash); + if (pendingTransaction != null) + pendingTransaction.incApproved(); + else { + pendingTransaction = new PendingTransaction(transaction); + pendingTransactions.put(hash, pendingTransaction); + } + return pendingTransaction; + } + + public void removePendingTransaction(Transaction transaction){ + + BigInteger hash = new BigInteger(transaction.getHash()); + logger.info("pending transaction removed with hash: {} ", hash.toString(16) ); + pendingTransactions.remove(hash); + } + + public long getGasPrice() { + return gasPrice; + } + + public byte[] getLatestBlockHash(){ + if (this.isEmpty()) + return StaticMessages.GENESIS_HASH; + else + return lastBlock.getHash(); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java index 2ff006aa..2ad1dd7b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -16,25 +16,25 @@ public class Genesis extends Block { private static byte[] zeroHash160 = new byte[20]; private static byte[] sha3EmptyList = HashUtil.sha3(RLP.encodeList()); - private static byte[] parentHash = zeroHash256; - private static byte[] unclesHash = sha3EmptyList; - private static byte[] coinbase = zeroHash160; - private static byte[] stateRoot = // TODO: Get stateRoot from actual state + public static byte[] PARENT_HASH = zeroHash256; + public static byte[] UNCLES_HASH = sha3EmptyList; + public static byte[] COINBASE = zeroHash160; + public static byte[] STATE_ROOT = // TODO: Get stateRoot from actual state Hex.decode("12582945fc5ad12c3e7b67c4fc37a68fc0d52d995bb7f7291ff41a2739a7ca16"); - private static byte[] txTrieRoot = new byte[0]; - private static byte[] difficulty = BigInteger.valueOf(2).pow(22).toByteArray(); - private static long number = 0; - private static long minGasPrice = 0; - private static long gasLimit = 1000000; - private static long gasUsed = 0; - private static long timestamp = 0; - private static byte[] extraData = new byte[0]; - private static byte[] nonce = HashUtil.sha3(new byte[]{42}); + public static byte[] TX_TRIE_ROOT = new byte[0]; + public static byte[] DIFFICULTY = BigInteger.valueOf(2).pow(22).toByteArray(); + public static long NUMBER = 0; + public static long MIN_GAS_PRICE = 0; + public static long GAS_LIMIT = 1000000; + public static long GAS_USED = 0; + public static long TIMESTAMP = 0; + public static byte[] EXTRA_DATA = new byte[0]; + public static byte[] NONCE = HashUtil.sha3(new byte[]{42}); public Genesis() { - super(parentHash, unclesHash, coinbase, stateRoot, - txTrieRoot, difficulty, number, minGasPrice, gasLimit, gasUsed, - timestamp, extraData, nonce, null, null); + super(PARENT_HASH, UNCLES_HASH, COINBASE, STATE_ROOT, + TX_TRIE_ROOT, DIFFICULTY, NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, + TIMESTAMP, EXTRA_DATA, NONCE, null, null); logger.info("Genesis-hash: " + Hex.toHexString(this.getHash())); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/core/GoState.java b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java new file mode 100644 index 00000000..5cfc84f3 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/GoState.java @@ -0,0 +1,160 @@ +package org.ethereum.core; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import org.ethereum.trie.Trie; + +public class GoState { + + // States within the ethereum protocol are used to store anything + // within the merkle trie. States take care of caching and storing + // nested states. It's the general query interface to retrieve: + // * Contracts + // * Accounts + + // The trie for this structure + private Trie trie; + // Nested states + private Map states; + + // Create a new state from a given trie + public GoState(Trie trie) { + this.trie = trie; + states = new HashMap(); + } + + public void add(String key, GoState state) { + this.states.put(key, state); + } + + // Resets the trie and all siblings + public void reset() { + this.trie.undo(); + + // Reset all nested states + for (GoState state : states.values()) { + state.reset(); + } + } + + // Syncs the trie and all siblings + public void sync() { + this.trie.sync(); + + // Sync all nested states + for (GoState state : states.values()) { + state.sync(); + } + } + + // Purges the current trie. + public int purge() { + return this.trie.getIterator().purge(); + } + + public StateObject getContract(byte[] address) { + String data = this.trie.get(new String(address)); + if (data == "") { + return null; + } + + // build contract + StateObject contract = new StateObject(address, data.getBytes()); + + // Check if there's a cached state for this contract + GoState cachedState = this.states.get(new String(address)); + if (cachedState != null) { + contract.setState( cachedState ); + } else { + // If it isn't cached, cache the state + this.states.put(new String(address), contract.getState()); + } + + return contract; + } + + public StateObject getAccount(byte[] address) { + String data = this.trie.get(new String(address)); + if (data == "") { + return StateObject.createAccount(address, BigInteger.ZERO); + } else { + return new StateObject(address, data.getBytes()); + } + } + + public boolean cmp(GoState other) { + return this.trie.cmp(other.getTrie()); + } + + public GoState copy() { + return new GoState(this.trie.copy()); + } + +// type ObjType byte +// +// enum ( +// NullTy ObjType = iota, +// AccountTy, +// ContractTy, +// UnknownTy +// ) + + // Returns the object stored at key and the type stored at key + // Returns null if nothing is stored +// public (*ethutil.Value, ObjType) getStateObject(byte[] key) { +// +// // Fetch data from the trie +// String data = this.trie.get(new String(key)); +// // Returns the null type, indicating nothing could be retrieved. +// // Anything using this function should check for this ret val +// if (data == "") { +// return (null, NullTy) +// } +// +// var enum ObjType +// Value val = new Value(data.getBytes()); +// // Check the length of the retrieved value. +// // Len 2 = Account +// // Len 3 = Contract +// // Other = invalid for now. If other types emerge, add them here +// if (val.length() == 2) { +// typ = AccountTy +// } else if (val.length == 3) { +// typ = ContractTy +// } else { +// typ = UnknownTy +// } +// +// return (val, typ); +// } + + // Updates any given state object + public void updateStateObject(StateObject stateObject) { + byte[] addr = stateObject.getAddress(); + + if (stateObject.getState() != null) { + this.states.put(new String(addr), stateObject.getState()); + } + + this.trie.update(new String(addr), new String(stateObject.rlpEncode())); + } + + public void put(byte[] key, byte[] object) { + this.trie.update(new String(key), new String(object)); + } + + /** + * Instead of calling this method, call state.getTrie().getRoot() + * @return + */ + @Deprecated() + public Object getRoot() { + return this.trie.getRoot(); + } + + public Trie getTrie() { + return this.trie; + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/State.java b/ethereumj-core/src/main/java/org/ethereum/core/State.java new file mode 100644 index 00000000..7aab74d6 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/State.java @@ -0,0 +1,12 @@ +package org.ethereum.core; + +import java.util.Map; + +import org.ethereum.trie.Trie; + +public class State { + + Trie trie; + Map states; + +} diff --git a/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java new file mode 100644 index 00000000..751daf82 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/StateObject.java @@ -0,0 +1,180 @@ +package org.ethereum.core; + +import java.math.BigInteger; + +import org.ethereum.db.Config; +import org.ethereum.trie.Trie; +import org.ethereum.util.RLP; +import org.ethereum.util.Value; + +import static java.util.Arrays.copyOfRange; + +public class StateObject { + + // Address of the object + private byte[] address; + // Shared attributes + private BigInteger amount; + + private long nonce; + // Contract related attributes + private GoState state; + + private byte[] init; + private byte[] body; + + // Returns a newly created contract at root + public static StateObject createContract(byte[] address, BigInteger amount, byte[] root) { + StateObject contract = new StateObject(address, amount); + contract.setState(new GoState(new Trie(Config.STATE_DB.getDb(), new String(root)))); + return contract; + } + + // Returns a newly created account + public static StateObject createAccount(byte[] address, BigInteger amount) { + return new StateObject(address, amount); + } + + public StateObject(byte[] address, BigInteger amount) { + this.address = address; + this.amount = amount; + } + + public StateObject(byte[] address, byte[] data) { + this.address = address; + this.rlpDecode(data); + } + + public void setState(GoState state) { + this.state = state; + } + + public void setBody(byte[] body) { + this.body = body; + } + + public void setInit(byte[] init) { + this.init = init; + } + + public Value getAddress(byte[] address) { + return new Value(this.state.getTrie().get(new String(address)).getBytes()); + } + + public void setAddress(byte[] address, Object value) { + this.state.getTrie().update(new String(address), new String(new Value(value).encode())); + } + + public GoState getState() { + return this.state; + } + + public Value getMem(BigInteger num) { + byte[] nb = num.toByteArray(); + return this.getAddress(nb); + } + + /** + * Get the instruction + * + * @param pc + * @return byte wrapped in a Value object + */ + public Value getInstr(BigInteger pc) { + if (this.body.length-1 < pc.longValue()) { + return new Value(0); + } + return new Value( new byte[] { this.body[pc.intValue()] } ); + } + + public void setMem(BigInteger num, Value val) { + byte[] address = num.toByteArray(); + this.state.getTrie().update(new String(address), new String(val.encode())); + } + + // Return the gas back to the origin. Used by the Virtual machine or Closures + public void returnGas(BigInteger gas, BigInteger gasPrice, GoState state) { + BigInteger remainder = gas.multiply(gasPrice); + this.addAmount(remainder); + } + + public BigInteger getAmount() { + return this.amount; + } + + public void addAmount(BigInteger amount) { + this.amount = this.amount.add(amount); + } + + public void subAmount(BigInteger amount) { + this.amount = this.amount.subtract(amount); + } + + public void convertGas(BigInteger gas, BigInteger gasPrice) throws RuntimeException { + BigInteger total = gas.multiply(gasPrice); + if (total.compareTo(this.amount) > 0) { + throw new RuntimeException("insufficient amount: " + this.amount + ", " + total); + } + this.subAmount(total); + } + + // Returns the address of the contract/account + public byte[] getAddress() { + return this.address; + } + + public long getNonce() { + return this.nonce; + } + + // Returns the main script body + public byte[] getBody() { + return this.body; + } + + // Returns the initialization script + public byte[] getInit() { + return this.init; + } + + // State object encoding methods + public byte[] rlpEncode() { + Object root; + if (this.state != null) { + root = this.state.getTrie().getRoot(); + } else { + root = null; + } + return RLP.encode( new Object[] {this.amount, this.nonce, root, this.body}); + } + + public void rlpDecode(byte[] data) { + Value decoder = new Value(data); + + this.amount = decoder.get(0).asBigInt(); + this.nonce = decoder.get(1).asInt(); + this.state = new GoState(new Trie(Config.STATE_DB.getDb(), decoder.get(2).asObj())); + this.body = decoder.get(3).asBytes(); + } + + // Converts an transaction in to a state object + public static StateObject createContract(Transaction tx, GoState state) { + // Create contract if there's no recipient + if (tx.isContract()) { + // FIXME + byte[] txHash = tx.getHash(); + byte[] contractAddress = copyOfRange(txHash, 12, txHash.length); + + BigInteger value = new BigInteger(1, tx.getValue()); + StateObject contract = StateObject.createContract(contractAddress, value, "".getBytes()); + state.updateStateObject(contract); + + contract.setBody(tx.getData()); + + state.updateStateObject(contract); + + return contract; + } + return null; + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java b/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java new file mode 100644 index 00000000..95a9ab99 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/core/StateObjectCache.java @@ -0,0 +1,36 @@ +package org.ethereum.core; + +import java.util.HashMap; +import java.util.Map; + +public class StateObjectCache { + + // The cached state and state object cache are helpers which will give you somewhat + // control over the nonce. When creating new transactions you're interested in the 'next' + // nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. + Map cachedObjects; + + public StateObjectCache() { + this.cachedObjects = new HashMap(); + } + + public CachedStateObject add(byte[] address, StateObject stateObject) { + CachedStateObject state = new CachedStateObject(stateObject.getNonce(), stateObject); + this.cachedObjects.put(new String(address), state); + return state; + } + + public CachedStateObject get(byte[] address) { + return this.cachedObjects.get(new String(address)); + } + + public class CachedStateObject { + private long nonce; + private StateObject stateObject; + + public CachedStateObject(long nonce, StateObject stateObject) { + this.nonce = nonce; + this.stateObject = stateObject; + } + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java index c2312076..65db0692 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java @@ -25,7 +25,7 @@ import java.util.Arrays; */ public class Transaction { - Logger logger = LoggerFactory.getLogger(this.getClass()); + private static Logger logger = LoggerFactory.getLogger(Transaction.class); public static final byte[] ZERO_ADDRESS = new byte[20]; @@ -157,11 +157,6 @@ public class Transaction { return data; } - public byte[] getInit() { - if (!parsed) rlpParse(); - return data; - } - public ECDSASignature getSignature() { if (!parsed) rlpParse(); return signature; diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java index 7a514384..f5b3605f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Wallet.java @@ -32,7 +32,7 @@ public class Wallet { // todo: a) the values I need to keep for address state is balance & nonce & ECKey // todo: b) keep it to be easy accessed by the toAddress() // private HashMap rows = new HashMap<>(); - + // table for a wallet private HashMap rows = new HashMap(); private long high; @@ -42,7 +42,6 @@ public class Wallet { private HashMap transactionMap = new HashMap(); public void addNewKey(){ - AddressState addressState = new AddressState(); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); @@ -50,7 +49,6 @@ public class Wallet { } public void importKey(byte[] privKey){ - AddressState addressState = new AddressState(ECKey.fromPrivate(privKey)); String address = Hex.toHexString(addressState.getEcKey().getAddress()); rows.put(address, addressState); @@ -66,7 +64,6 @@ public class Wallet { } public AddressState getAddressState(byte[] addressBytes){ - String address = Hex.toHexString(addressBytes); return rows.get(address); } @@ -94,7 +91,7 @@ public class Wallet { BigInteger value = new BigInteger(transaction.getValue()); senderState.addToBalance(value.negate()); - senderState.incrementTheNonce(); + senderState.incrementNonce(); } byte[] receiveAddress = transaction.getReceiveAddress(); @@ -116,12 +113,9 @@ public class Wallet { List transactions = block.getTransactionsList(); for (Transaction tx : transactions){ - boolean txExist = transactionMap.get(new BigInteger(tx.getHash())) != null; if (txExist) break; - else { - applyTransaction(tx); walletUpdated = true; } @@ -259,7 +253,8 @@ public class Wallet { } private void notifyListeners(){ - for (WalletListener listener : listeners) listener.valueChanged(); + for (WalletListener listener : listeners) + listener.valueChanged(); } public interface WalletListener{ diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Config.java b/ethereumj-core/src/main/java/org/ethereum/db/Config.java new file mode 100644 index 00000000..9266214e --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/db/Config.java @@ -0,0 +1,8 @@ +package org.ethereum.db; + +public class Config { + + public static Database CHAIN_DB = new Database("blockchain"); + public static Database STATE_DB = new Database("state"); + +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Database.java b/ethereumj-core/src/main/java/org/ethereum/db/Database.java new file mode 100644 index 00000000..3240e9a8 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/db/Database.java @@ -0,0 +1,116 @@ +package org.ethereum.db; + +import static org.iq80.leveldb.impl.Iq80DBFactory.factory; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.ethereum.config.SystemProperties; +import org.ethereum.core.Block; +import org.iq80.leveldb.DB; +import org.iq80.leveldb.DBIterator; +import org.iq80.leveldb.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongycastle.util.encoders.Hex; + +/** + * Generic interface for Ethereum database + * + * LevelDB key/value pair DB implementation will be used. + * Choice must be made between: + * Pure Java: https://github.com/dain/leveldb + * JNI binding: https://github.com/fusesource/leveldbjni + */ +public class Database { + + private static Logger logger = LoggerFactory.getLogger(Database.class); + private DB db; + + public Database(String name) { + // Initialize Database + Options options = new Options(); + options.createIfMissing(true); + try { + logger.debug("Opening database"); + if(SystemProperties.CONFIG.databaseReset()) { + logger.debug("Destroying '" + name + "' DB on startup ENABLED"); + destroyDB(name); + } + logger.debug("Initializing new or existing DB: '" + name + "'"); + options.createIfMissing(true); + db = factory.open(new File(name), options); + printDB(); +// logger.debug("Showing database stats"); +// String stats = DATABASE.getProperty("leveldb.stats"); +// logger.debug(stats); + } catch (IOException ioe) { + logger.error(ioe.getMessage(), ioe); + throw new RuntimeException("Can't initialize database"); + } + } + + public void destroyDB(String name) { + logger.debug("Destroying existing DB"); + Options options = new Options(); + try { + factory.destroy(new File(name), options); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + + public void printDB() { + DBIterator iterator = db.iterator(); + try { + Map blocks = new HashMap(); + int count = 0; + if (!iterator.hasNext()) { + logger.info("DB is empty"); + } else { + logger.info("Displaying blocks stored in DB sorted on key"); + } + for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { + byte[] key = iterator.peekNext().getKey(); + byte[] value = iterator.peekNext().getValue(); + Block block = new Block(value); + blocks.put(new Long(block.getNumber()), block); + logger.info("Block: " + count + " Key: " + Hex.toHexString(key) + " ---> " + block.toFlatString()); + count++; + } + } finally { + // Make sure you close the iterator to avoid resource leaks. + try { + iterator.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** Insert object(value) (key = sha3(value)) */ + public void put(byte[] key, byte[] value) { + db.put(key, value); + } + + /** Get object (key) -> value */ + public byte[] get(byte[] key) { + return db.get(key); + } + + /** Delete object (key) from db **/ + public void delete(byte[] key) { + delete(key); + } + + public DBIterator iterator() { + return db.iterator(); + } + + public DB getDb() { + return this.db; + } + +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java b/ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java similarity index 97% rename from ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java rename to ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java index bb718ea3..0fc1ac10 100644 --- a/ethereumj-core/src/main/java/org/ethereum/geodb/IpGeoDB.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/IpGeoDB.java @@ -1,4 +1,4 @@ -package org.ethereum.geodb; +package org.ethereum.db; import java.io.File; import java.net.InetAddress; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java b/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java index c79cfb5f..22706fff 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/BlockChainTable.java @@ -75,9 +75,9 @@ public class BlockChainTable extends JFrame { @Override public void actionPerformed(ActionEvent e) { - if (MainData.instance.getAllBlocks().size() - 1 < lastFindIndex) return; + if (MainData.instance.getBlockchain().size() - 1 < lastFindIndex) return; - Block block = MainData.instance.getAllBlocks().get(lastFindIndex); + Block block = MainData.instance.getBlockchain().get(lastFindIndex); StringSelection stsel = new StringSelection(block.toString()); Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard(); system.setContents(stsel,stsel); @@ -97,10 +97,10 @@ public class BlockChainTable extends JFrame { return; } - for (int i = lastFindIndex + 1; i < MainData.instance.getAllBlocks().size(); ++i) { + for (int i = lastFindIndex + 1; i < MainData.instance.getBlockchain().size(); ++i) { - if (MainData.instance.getAllBlocks().size() - 1 < i) return; - Block block = MainData.instance.getAllBlocks().get(i); + if (MainData.instance.getBlockchain().size() - 1 < i) return; + Block block = MainData.instance.getBlockchain().get(i); boolean found = block.toString().toLowerCase().contains(toFind.toLowerCase()); if (found) { // todo: now we find the first occur diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java b/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java index 12fa0f7d..cebcd1df 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/BlockTableModel.java @@ -9,13 +9,13 @@ import javax.swing.table.AbstractTableModel; * User: Roman Mandeleil * Created on: 15/05/14 12:42 */ -public class BlockTableModel extends AbstractTableModel { +public class BlockTableModel extends AbstractTableModel { @Override public int getRowCount() { fireTableDataChanged(); - int rowCount = MainData.instance.getAllBlocks().size(); + int rowCount = MainData.instance.getBlockchain().size(); return rowCount; } @@ -30,6 +30,6 @@ public class BlockTableModel extends AbstractTableModel { // byte[] hash = MainData.instance.getAllBlocks().get(rowIndex).getHash(); // return Hex.toHexString(hash); - return MainData.instance.getAllBlocks().get(rowIndex).toString(); + return MainData.instance.getBlockchain().get(rowIndex).toString(); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java index 2c531f7a..30ecd1f9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ConnectionConsoleWindow.java @@ -1,15 +1,10 @@ package org.ethereum.gui; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.net.InetAddress; -import java.net.UnknownHostException; import javax.swing.*; -import javax.tools.Tool; import org.ethereum.config.SystemProperties; import org.ethereum.net.client.ClientPeer; @@ -83,37 +78,13 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{ Thread t = new Thread() { public void run() { - - // Peer Server Zero: peer discovery -// new ClientPeer(thisConsole).connect("54.201.28.117", 30303); - - // Peer Server One: peer discovery -// new ClientPeer(thisConsole).connect("54.204.10.41", 30303); - - // Some dude in Canada -// new ClientPeer(thisConsole).connect("131.104.247.135", 30303); - - // Nick -// new ClientPeer(thisConsole).connect("82.217.72.169", 30303); - - // c++: ZeroGox -// new ClientPeer(thisConsole).connect("54.204.10.41", 30303); - - // RomanJ -// new ClientPeer(thisConsole).connect("54.211.14.10", 40404); - new ClientPeer(thisConsole).connect(SystemProperties.CONFIG.activePeerIP(), SystemProperties.CONFIG.activePeerPort()); - - } }; t.start(); } - - - @Override public void console(final String output) { SwingUtilities.invokeLater(new Runnable() { @@ -163,7 +134,6 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{ } } - public void addCloseAction(){ this.addWindowListener( new WindowAdapter() { @@ -176,7 +146,6 @@ public class ConnectionConsoleWindow extends JFrame implements PeerListener{ } - public static void main(String[] args) { // Start all Swing applications on the EDT. SwingUtilities.invokeLater(new Runnable() { diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java index c0e3bd1b..1cb498e9 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PayOutDialog.java @@ -115,7 +115,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes(); byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray(); - byte[] gasPrice = BigInteger.valueOf( MainData.instance.getGasPrice()).toByteArray(); + byte[] gasPrice = BigInteger.valueOf( MainData.instance.getBlockchain().getGasPrice()).toByteArray(); Transaction tx = new Transaction(nonce, gasPrice, BigIntegers .asUnsignedByteArray(fee), address, BigIntegers @@ -192,7 +192,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog{ // check if the tx is affordable BigInteger ammountValue = new BigInteger(amountText); BigInteger feeValue = new BigInteger(feeText); - BigInteger gasPrice = BigInteger.valueOf(MainData.instance.getGasPrice()); + BigInteger gasPrice = BigInteger.valueOf(MainData.instance.getBlockchain().getGasPrice()); BigInteger currentBalance = addressState.getBalance(); boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java index 55f1bdcf..73fab376 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/PeersTableModel.java @@ -7,7 +7,7 @@ import java.util.*; import javax.swing.ImageIcon; import javax.swing.table.AbstractTableModel; -import org.ethereum.geodb.IpGeoDB; +import org.ethereum.db.IpGeoDB; import org.ethereum.manager.MainData; import org.ethereum.net.client.PeerData; import org.ethereum.util.Utils; diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java index 8a117404..523162b5 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java @@ -1,13 +1,11 @@ package org.ethereum.gui; -import org.apache.log4j.PropertyConfigurator; import org.ethereum.manager.MainData; import org.ethereum.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; -import javax.tools.Tool; import java.awt.*; import java.awt.event.ItemEvent; @@ -20,7 +18,6 @@ import java.awt.event.ItemListener; */ public class ToolBar extends JFrame { - Logger logger = LoggerFactory.getLogger(getClass()); Logger introLogger = LoggerFactory.getLogger("Intro"); @@ -36,7 +33,6 @@ public class ToolBar extends JFrame { JToggleButton chainToggle; JToggleButton walletToggle; - public ToolBar() throws HeadlessException { introLogger.info(""); @@ -113,7 +109,6 @@ public class ToolBar extends JFrame { } } }); - logToggle = new JToggleButton(); logToggle.setIcon(image_2); logToggle.setToolTipText("Log Console"); @@ -139,7 +134,6 @@ public class ToolBar extends JFrame { } } }); - peersToggle = new JToggleButton(); peersToggle.setIcon(image_3); peersToggle.setToolTipText("Peers"); @@ -164,7 +158,6 @@ public class ToolBar extends JFrame { } } }); - chainToggle = new JToggleButton(); chainToggle.setIcon(image_4); chainToggle.setToolTipText("Block Chain"); @@ -190,7 +183,6 @@ public class ToolBar extends JFrame { } } }); - walletToggle = new JToggleButton(); walletToggle.setIcon(image_5); walletToggle.setToolTipText("Wallet"); @@ -218,7 +210,6 @@ public class ToolBar extends JFrame { } } ); - cp.add(editorToggle); cp.add(logToggle); cp.add(peersToggle); diff --git a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java index f135f509..8ded8051 100644 --- a/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java +++ b/ethereumj-core/src/main/java/org/ethereum/manager/MainData.java @@ -1,29 +1,31 @@ package org.ethereum.manager; +import static org.ethereum.config.SystemProperties.CONFIG; + import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.*; - -import com.maxmind.geoip.Location; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.ethereum.core.AddressState; -import org.ethereum.core.Block; +import org.ethereum.core.Blockchain; import org.ethereum.core.Transaction; import org.ethereum.core.Wallet; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; -import org.ethereum.geodb.IpGeoDB; +import org.ethereum.db.Config; +import org.ethereum.db.IpGeoDB; import org.ethereum.net.client.ClientPeer; import org.ethereum.net.client.PeerData; -import org.ethereum.net.message.StaticMessages; import org.ethereum.net.peerdiscovery.PeerDiscovery; -import org.ethereum.net.submit.PendingTransaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.spongycastle.util.encoders.Hex; -import static org.ethereum.config.SystemProperties.CONFIG; +import com.maxmind.geoip.Location; /** * www.ethereumJ.com @@ -35,35 +37,16 @@ public class MainData { Logger logger = LoggerFactory.getLogger(getClass().getName()); private List peers = Collections.synchronizedList(new ArrayList()); - private List blockChainDB = new ArrayList(); + private Blockchain blockChain; private Wallet wallet = new Wallet(); private ClientPeer activePeer; - private long gasPrice = 1000; - - private Map pendingTransactions = - Collections.synchronizedMap(new HashMap()); - PeerDiscovery peerDiscovery; public static MainData instance = new MainData(); public MainData() { - - InetAddress ip = null; - int port = 0; - try { - ip = InetAddress.getByName(CONFIG.peerDiscoveryIP()); - port = CONFIG.peerDiscoveryPort(); - } catch (UnknownHostException e) { - e.printStackTrace(); - System.exit(-1); - } - - PeerData peer = new PeerData( - ip.getAddress(), port, new byte[]{00}); - peers.add(peer); - + // Initialize Wallet byte[] cowAddr = HashUtil.sha3("cow".getBytes()); ECKey key = ECKey.fromPrivate(cowAddr); @@ -72,66 +55,24 @@ public class MainData { state.addToBalance(BigInteger.valueOf(2).pow(200)); // 1606938044258990275541962092341162602522202993782792835301376 wallet.importKey(HashUtil.sha3("cat".getBytes())); - peerDiscovery = new PeerDiscovery(peers); + // Initialize Blockchain + blockChain = new Blockchain(wallet); + + // Initialize PeerData + try { + InetAddress ip = InetAddress.getByName(CONFIG.peerDiscoveryIP()); + int port = CONFIG.peerDiscoveryPort(); + PeerData peer = new PeerData(ip.getAddress(), port, new byte[]{00}); + peers.add(peer); + peerDiscovery = new PeerDiscovery(peers); + } catch (UnknownHostException e) { + e.printStackTrace(); + System.exit(-1); + } } - - public void addBlocks(List blocks) { - - // TODO: redesign this part when the state part and the genesis block is ready - - if (blocks.isEmpty()) return; - - Block firstBlockToAdd = blocks.get(blocks.size() - 1); - - // if it is the first block to add - // check that the parent is the genesis - if (blockChainDB.isEmpty() && - !Arrays.equals(StaticMessages.GENESIS_HASH, firstBlockToAdd.getParentHash())){ - return; - } - - // if there is some blocks already - // keep chain continuity - if (!blockChainDB.isEmpty() ){ - Block lastBlock = blockChainDB.get(blockChainDB.size() - 1); - String hashLast = Hex.toHexString(lastBlock.getHash()); - String blockParentHash = Hex.toHexString(firstBlockToAdd.getParentHash()); - if (!hashLast.equals(blockParentHash)) return; - } - - for (int i = blocks.size() - 1; i >= 0 ; --i){ - Block block = blocks.get(i); - blockChainDB.add(block); - - if (logger.isInfoEnabled()) - logger.info("block added to the chain hash: {}", Hex.toHexString(block.getHash())); - - this.gasPrice = block.getMinGasPrice(); - - - wallet.processBlock(block); - } - - // Remove all pending transactions as they already approved by the net - for (Block block : blocks){ - for (Transaction tx : block.getTransactionsList()){ - if (logger.isDebugEnabled()) - logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash())); - removePendingTransaction(tx); - } - } - logger.info("*** Block chain size: [ {} ]", blockChainDB.size()); - } - - public byte[] getLatestBlockHash(){ - if (blockChainDB.isEmpty()) - return StaticMessages.GENESIS_HASH; - else - return blockChainDB.get(blockChainDB.size() - 1).getHash(); - } - - public List getAllBlocks(){ - return blockChainDB; + + public Blockchain getBlockchain() { + return blockChain; } public Wallet getWallet() { @@ -146,39 +87,6 @@ public class MainData { return activePeer; } - /* - * 1) the dialog put a pending transaction on the list - * 2) the dialog send the transaction to a net - * 3) wherever the transaction got for the wire in will change to approve state - * 4) only after the approve a) Wallet state changes - * 5) After the block is received with that tx the pending been clean up - */ - public PendingTransaction addPendingTransaction(Transaction transaction) { - - BigInteger hash = new BigInteger(transaction.getHash()); - logger.info("pending transaction placed hash: {} ", hash.toString(16) ); - - PendingTransaction pendingTransaction = pendingTransactions.get(hash); - if (pendingTransaction != null) - pendingTransaction.incApproved(); - else { - pendingTransaction = new PendingTransaction(transaction); - pendingTransactions.put(hash, pendingTransaction); - } - return pendingTransaction; - } - - public void removePendingTransaction(Transaction transaction){ - - BigInteger hash = new BigInteger(transaction.getHash()); - logger.info("pending transaction removed hash: {} ", hash.toString(16) ); - pendingTransactions.remove(hash); - } - - public long getGasPrice() { - return gasPrice; - } - public List getPeers() { return peers; } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java b/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java index ce126bd0..eb8c810d 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/client/EthereumProtocolHandler.java @@ -202,7 +202,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { RLPList rlpList = RLP.decode2(payload); TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList); for (Transaction tx : transactionsMessage.getTransactions()) - MainData.instance.addPendingTransaction(tx); + MainData.instance.getBlockchain().addPendingTransaction(tx); // todo: if you got transactions send it to your connected peers logger.info(transactionsMessage.toString()); @@ -256,7 +256,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { }, 3000, secToAskForChain * 1000); } - MainData.instance.addBlocks(blockList); + MainData.instance.getBlockchain().addBlocks(blockList); logger.info(blocksMessage.toString()); if (peerListener != null) peerListener.console(blocksMessage.toString()); } @@ -343,7 +343,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter { private void sendGetChain(ChannelHandlerContext ctx){ - byte[] hash = MainData.instance.getLatestBlockHash(); + byte[] hash = MainData.instance.getBlockchain().getLatestBlockHash(); GetChainMessage chainMessage = new GetChainMessage((byte)100, hash); ByteBuf buffer = ctx.alloc().buffer(chainMessage.getPayload().length + 8); diff --git a/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java b/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java index 0cf9208c..7247d343 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/submit/TransactionTask.java @@ -34,7 +34,8 @@ public class TransactionTask implements Callable { ClientPeer peer = MainData.instance.getActivePeer(); - PendingTransaction pendingTransaction = MainData.instance.addPendingTransaction(tx); + PendingTransaction pendingTransaction = MainData.instance + .getBlockchain().addPendingTransaction(tx); peer.sendTransaction(tx); while(pendingTransaction.getApproved() < 1 ){ @@ -44,7 +45,7 @@ public class TransactionTask implements Callable { logger.info("return approved: {}", pendingTransaction.getApproved()); } catch (Throwable th) { logger.info("exception caugh: {}", th.getCause()); - MainData.instance.removePendingTransaction(tx); + MainData.instance.getBlockchain().removePendingTransaction(tx); } return null; diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java index c8d1ac80..8459ad07 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Cache.java @@ -1,9 +1,5 @@ package org.ethereum.trie; -import static org.iq80.leveldb.impl.Iq80DBFactory.factory; - -import java.io.File; -import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -11,7 +7,6 @@ import java.util.Map; import org.ethereum.crypto.HashUtil; import org.ethereum.util.Value; import org.iq80.leveldb.DB; -import org.iq80.leveldb.Options; public class Cache { @@ -20,17 +15,6 @@ public class Cache { private boolean isDirty; public Cache(DB db) { - if(db == null) { - try { - /* **** Experimental LevelDB Code **** */ - Options options = new Options(); - options.createIfMissing(true); - this.db = factory.open(new File("ethereumdb"), options); - /* **** Experimental LevelDB Code **** */ - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } this.db = db; nodes = new HashMap(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java b/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java index 9c4e609d..0feaf32e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/DecodeResult.java @@ -2,6 +2,8 @@ package org.ethereum.util; import java.io.Serializable; +import org.spongycastle.util.encoders.Hex; + public class DecodeResult implements Serializable { private int pos; @@ -18,4 +20,23 @@ public class DecodeResult implements Serializable { public Object getDecoded() { return decoded; } + + public String toString() { + return asString(this.decoded); + } + + private String asString(Object decoded) { + if(decoded instanceof String) { + return (String) decoded; + } else if (decoded instanceof byte[]) { + return Hex.toHexString((byte[]) decoded); + } else if (decoded instanceof Object[]) { + String result = ""; + for (Object item : (Object[]) decoded) { + result += asString(item); + } + return result; + } + throw new RuntimeException("Not a valid type. Should not occur"); + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java index 6a55cc92..c119fac3 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java @@ -660,7 +660,7 @@ public class RLP { if (data == null || data.length < 1) { return null; } - + int prefix = data[pos] & 0xFF; if (prefix == OFFSET_SHORT_ITEM) { return new DecodeResult(pos+1, new byte[0]); // means no length or 0 diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index f913970c..40dec38e 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -1,15 +1,37 @@ - - # if the system will work as a server also # accept for incoming connections [true/false] server.acceptConnections = false - # one default access point to start # discover the network e.g. ip: [54.201.28.117] port: [30303] peer.discovery.ip = 54.201.28.117 peer.discovery.port = 30303 +# Peer Server Zero: peer discovery +#peer.discovery.ip = 54.201.28.117 +#peer.discovery.port = 30303 + +# Peer Server One: peer discovery +#peer.discovery.ip = 54.204.10.41 +#peer.discovery.port = 30303 + +# Some dude in Canada +#peer.discovery.ip = 131.104.247.135 +#peer.discovery.port = 30303 + +# Nick +#peer.discovery.ip = 82.217.72.169 +#peer.discovery.port = 30303 + +# ZeroGox +#peer.discovery.ip = 54.204.10.41 +#peer.discovery.port = 30303 + +# RomanJ +# peer.discovery.ip = 54.211.14.10 +# peer.discovery.port = 40404 + + # active peer ip and port # that is the peer through @@ -44,8 +66,13 @@ peer.discovery.timeout = 2 # retrieved from the peer [seconds] transaction.approve.timeout = 5 - # default directory where we keep # basic Serpent samples relative # to home.dir -samples.dir = samples \ No newline at end of file +samples.dir = samples + +# everytime the application starts +# the existing database will be +# destroyed and all the data will be +# downloaded from peers again +database.reset = true \ No newline at end of file diff --git a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java index 70ea5c2b..f38e0d1d 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java @@ -1,6 +1,7 @@ package org.ethereum.core; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.IOException; import java.math.BigInteger; @@ -148,7 +149,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txSigned.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txSigned.getValue())); assertNull(txSigned.getData()); - assertNull(txSigned.getInit()); assertEquals(27, txSigned.getSignature().v); assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().r))); assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txSigned.getSignature().s))); @@ -169,7 +169,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txUnsigned.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txUnsigned.getValue())); assertNull(txUnsigned.getData()); - assertNull(txUnsigned.getInit()); assertEquals(27, txUnsigned.getSignature().v); assertEquals("eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().r))); assertEquals("14a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1", Hex.toHexString(BigIntegers.asUnsignedByteArray(txUnsigned.getSignature().s))); @@ -185,7 +184,6 @@ public class TransactionTest { assertEquals(Hex.toHexString(testReceiveAddress), Hex.toHexString(txNew.getReceiveAddress())); assertEquals(new BigInteger(1, testValue), new BigInteger(1, txNew.getValue())); assertEquals("", Hex.toHexString(txNew.getData())); - assertNull(txNew.getInit()); assertNull(txNew.getSignature()); assertEquals(RLP_ENCODED_RAW_TX, Hex.toHexString(txNew.getEncodedRaw())); @@ -228,7 +226,6 @@ public class TransactionTest { assertEquals(HASH_TX_UNSIGNED, Hex.toHexString(tx.getHash())); } - @Test public void testTransactionCreateContract(){ @@ -266,8 +263,6 @@ public class TransactionTest { System.out.println("plainTx1: " + plainTx1 ); System.out.println("plainTx2: " + plainTx2 ); - System.out.println( Hex.toHexString( tx2.getSender() )); - } } diff --git a/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java b/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java index 9c514ecf..77fa752f 100644 --- a/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/net/MessagesTest.java @@ -202,7 +202,7 @@ public class MessagesTest { assertEquals("64", Hex.toHexString(tx.getGasPrice())); assertEquals("09184e72a000", Hex.toHexString(tx.getGasLimit())); assertEquals("null", ByteUtil.toHexString(tx.getData())); - assertEquals("null", ByteUtil.toHexString(tx.getInit())); + assertEquals("1b", Hex.toHexString(new byte[] { tx.getSignature().v })); assertEquals("5c89ebf2b77eeab88251e553f6f9d53badc1d800bbac02d830801c2aa94a4c9f", Hex.toHexString(tx.getSignature().r.toByteArray())); assertEquals("0b7907532b1f29c79942b75fff98822293bf5fdaa3653a8d9f424c6a3265f06c", Hex.toHexString(tx.getSignature().s.toByteArray())); @@ -263,9 +263,6 @@ public class MessagesTest { assertEquals("606956330c0d630000003359366000530a0d630000003359602060005301356000533557604060005301600054630000000c58", Hex.toHexString( tx.getData() )); - assertEquals("33606957", - Hex.toHexString( tx.getInit() )); - assertEquals("1c", Hex.toHexString( new byte[] {tx.getSignature().v} )); @@ -298,9 +295,6 @@ public class MessagesTest { assertEquals("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d0aceee7e5ab874e22ccf8d1a649f59106d74e8", Hex.toHexString( tx.getData() )); - assertEquals("null", - Hex.toHexString( tx.getInit() )); - assertEquals("1b", Hex.toHexString( new byte[] {tx.getSignature().v} )); diff --git a/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java b/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java index 077dc04c..d57b2d6b 100644 --- a/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/util/UtilsTest.java @@ -17,7 +17,7 @@ public class UtilsTest { @Test public void getValueShortString1(){ - String expected = "123 (10^24)"; + String expected = "123·(10^24)"; String result = Utils.getValueShortString(new BigInteger("123456789123445654363653463")); assertEquals(expected, result); @@ -26,7 +26,7 @@ public class UtilsTest { @Test public void getValueShortString2(){ - String expected = "123 (10^3)"; + String expected = "123·(10^3)"; String result = Utils.getValueShortString(new BigInteger("123456")); assertEquals(expected, result); @@ -35,7 +35,7 @@ public class UtilsTest { @Test public void getValueShortString3(){ - String expected = "1 (10^3)"; + String expected = "1·(10^3)"; String result = Utils.getValueShortString(new BigInteger("1234")); assertEquals(expected, result); @@ -44,7 +44,7 @@ public class UtilsTest { @Test public void getValueShortString4(){ - String expected = "123 (10^0)"; + String expected = "123·(10^0)"; String result = Utils.getValueShortString(new BigInteger("123")); assertEquals(expected, result); @@ -54,7 +54,7 @@ public class UtilsTest { public void getValueShortString5(){ byte[] decimal = Hex.decode("3913517ebd3c0c65000000"); - String expected = "69 (10^24)"; + String expected = "69·(10^24)"; String result = Utils.getValueShortString(new BigInteger(decimal)); assertEquals(expected, result); From 01ccfeb0fadd868ae11a7c856377238d87c44919 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Thu, 29 May 2014 22:08:41 +0200 Subject: [PATCH 4/4] Remove merge conflict in system.properties --- ethereumj-core/src/main/resources/system.properties | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index 5a1cfe3b..37740d15 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -32,7 +32,6 @@ peer.discovery.port = 30303 # peer.discovery.port = 40404 - # active peer ip and port # that is the peer through # we get the chain: [54.201.28.117] port: [30303] @@ -66,22 +65,13 @@ peer.discovery.timeout = 2 # retrieved from the peer [seconds] transaction.approve.timeout = 5 -<<<<<<< HEAD -======= # default directory where we keep # basic Serpent samples relative # to home.dir samples.dir = samples ->>>>>>> merge # everytime the application starts # the existing database will be # destroyed and all the data will be # downloaded from peers again -<<<<<<< HEAD database.reset = true - - -======= -database.reset = true ->>>>>>> merge