Merge pushes from romanman/master

This commit is contained in:
nicksavers 2014-05-29 21:52:18 +02:00
parent bb9e0d1bee
commit 6b22137c05
30 changed files with 838 additions and 283 deletions

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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();
}
}

View File

@ -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()));
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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{

View File

@ -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");
}

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
package org.ethereum.geodb;
package org.ethereum.db;
import java.io.File;
import java.net.InetAddress;

View File

@ -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

View File

@ -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();
}
}

View File

@ -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() {

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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>();
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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

View File

@ -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() ));
}
}

View File

@ -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} ));

View File

@ -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);