Merge pushes from romanman/master
This commit is contained in:
parent
bb9e0d1bee
commit
6b22137c05
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<Block> {
|
||||
|
||||
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<BigInteger, PendingTransaction> pendingTransactions =
|
||||
Collections.synchronizedMap(new HashMap<BigInteger, PendingTransaction>());
|
||||
|
||||
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<Block> 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();
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, GoState> states;
|
||||
|
||||
// Create a new state from a given trie
|
||||
public GoState(Trie trie) {
|
||||
this.trie = trie;
|
||||
states = new HashMap<String, GoState>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.ethereum.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.ethereum.trie.Trie;
|
||||
|
||||
public class State {
|
||||
|
||||
Trie trie;
|
||||
Map<String, State> states;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<String, CachedStateObject> cachedObjects;
|
||||
|
||||
public StateObjectCache() {
|
||||
this.cachedObjects = new HashMap<String, CachedStateObject>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<Address, BigInteger> rows = new HashMap<>();
|
||||
|
||||
|
||||
// <address, info> table for a wallet
|
||||
private HashMap<String, AddressState> rows = new HashMap<String, AddressState>();
|
||||
private long high;
|
||||
|
@ -42,7 +42,6 @@ public class Wallet {
|
|||
private HashMap<BigInteger, Transaction> transactionMap = new HashMap<BigInteger, Transaction>();
|
||||
|
||||
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<Transaction> 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{
|
||||
|
|
|
@ -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");
|
||||
|
||||
}
|
|
@ -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<Long, Block> blocks = new HashMap<Long, Block>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.ethereum.geodb;
|
||||
package org.ethereum.db;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<PeerData> peers = Collections.synchronizedList(new ArrayList<PeerData>());
|
||||
private List<Block> blockChainDB = new ArrayList<Block>();
|
||||
private Blockchain blockChain;
|
||||
private Wallet wallet = new Wallet();
|
||||
private ClientPeer activePeer;
|
||||
|
||||
private long gasPrice = 1000;
|
||||
|
||||
private Map<BigInteger, PendingTransaction> pendingTransactions =
|
||||
Collections.synchronizedMap(new HashMap<BigInteger, PendingTransaction>());
|
||||
|
||||
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<Block> 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<Block> 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<PeerData> getPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -34,7 +34,8 @@ public class TransactionTask implements Callable<Transaction> {
|
|||
|
||||
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<Transaction> {
|
|||
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;
|
||||
|
|
|
@ -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<byte[], Node>();
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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
|
|
@ -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() ));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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} ));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue