Major refactoring :

WorldManager introduced
WalletTransaction for wallet waiting tx
Pending Transaction for block creation
This commit is contained in:
romanman 2014-06-07 16:36:52 +01:00
parent dcf19fba08
commit 9319272a5a
21 changed files with 561 additions and 380 deletions

View File

@ -24,8 +24,10 @@ public class SystemProperties {
private static int DEFAULT_DISCOVERY_PORT = 30303;
private static String DEFAULT_ACTIVE_PEER_IP = "54.201.28.117";
private static int DEFAULT_ACTIVE_PORT = 30303;
private static String DEFAULT_SAMPLES_DIR = "samples";
private static String DEFAULT_SAMPLES_DIR = "samples";
private static String DEFAULT_COINBASE_SECRET = "monkey";
private static int DEFAULT_ACTIVE_PEER_CHANNEL_TIMEOUT = 5;
public static SystemProperties CONFIG = new SystemProperties();
private Properties prop = new Properties();
private InputStream input = null;
@ -117,7 +119,18 @@ public class SystemProperties {
return prop.getProperty("samples.dir");
}
public void print() {
public String coinbaseSecret(){
if(prop.isEmpty()) return DEFAULT_COINBASE_SECRET;
return prop.getProperty("coinbase.secret");
}
public Integer activePeerChannelTimeout(){
if(prop.isEmpty()) return DEFAULT_ACTIVE_PEER_CHANNEL_TIMEOUT;
return Integer.parseInt(prop.getProperty("active.peer.channel.timeout"));
}
public void print() {
Enumeration<?> e = prop.propertyNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();

View File

@ -13,7 +13,7 @@ public class AccountState {
private ECKey ecKey;
private byte[] rlpEncoded;
/* A value equal to the number of transactions sent
/* A value equal to the number of transactions sent
* from this address, or, in the case of contract accounts,
* the number of contract-creations made by this account */
private BigInteger nonce;
@ -27,7 +27,7 @@ public class AccountState {
* The hash is formally denoted σ[a] s .
*
* Since I typically wish to refer not to the tries root hash
* but to the underlying set of key/value pairs stored within,
* but to the underlying set of key/value pairs stored within,
* I define a convenient equivalence TRIE (σ[a] s ) σ[a] s .
* It shall be understood that σ[a] s is not a physical member
* of the account and does not contribute to its later serialisation */
@ -53,7 +53,8 @@ public class AccountState {
this.rlpEncoded = rlpData;
RLPList items = (RLPList) RLP.decode2(rlpEncoded).get(0);
this.nonce = new BigInteger(1, ((items.get(0).getRLPData()) == null ? new byte[0] : items.get(0).getRLPData()));
this.nonce = new BigInteger(1, ((items.get(0).getRLPData()) == null ? new byte[]{0} :
items.get(0).getRLPData()));
this.balance = new BigInteger(1, items.get(1).getRLPData());
this.stateRoot = items.get(2).getRLPData();
this.codeHash = items.get(3).getRLPData();

View File

@ -1,7 +1,7 @@
package org.ethereum.core;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Config;
import org.ethereum.manager.WorldManager;
import org.ethereum.trie.Trie;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
@ -22,7 +22,7 @@ import java.util.List;
*/
public class Block {
/* A scalar value equal to the mininum limit of gas expenditure per block */
/* A scalar longValue equal to the mininum limit of gas expenditure per block */
private static long MIN_GAS_LIMIT = BigInteger.valueOf(10).pow(4).longValue();
private BlockHeader header;
@ -40,7 +40,6 @@ public class Block {
private boolean parsed = false;
private byte[] hash;
private Trie accountState;
private Trie txsState;
/* Constructors */
@ -57,9 +56,8 @@ public class Block {
this.header = new BlockHeader(parentHash, unclesHash, coinbase,
difficulty, number, minGasPrice, gasLimit, gasUsed,
timestamp, extraData, nonce);
this.accountState = new Trie(Config.STATE_DB.getDb());
this.txsState = new Trie(null);
this.header.setStateRoot(accountState.getRootHash());
this.header.setStateRoot(WorldManager.instance.allAccountsState.getRootHash());
this.header.setTxTrieRoot(txsState.getRootHash());
this.transactionsList = transactionsList;
this.uncleList = uncleList;
@ -167,10 +165,6 @@ public class Block {
return this.header.getNonce();
}
public Trie getAccountState() {
return this.accountState;
}
public Trie getTxsState() {
return this.txsState;
}
@ -277,12 +271,13 @@ public class Block {
* - update state object
*/
// this.accountState.update();
// this.allAccountsState.update();
}
public byte[] updateState(byte[] key, byte[] value) {
this.accountState.update(key, value);
byte[] stateRoot = this.accountState.getRootHash();
WorldManager.instance.allAccountsState.update(key, value);
byte[] stateRoot = WorldManager.instance.allAccountsState.getRootHash();
this.header.setStateRoot(stateRoot);
return stateRoot;
}

View File

@ -1,22 +1,19 @@
package org.ethereum.core;
import java.io.IOException;
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.manager.WorldManager;
import org.ethereum.net.message.StaticMessages;
import org.ethereum.net.submit.PendingTransaction;
import org.ethereum.net.submit.WalletTransaction;
import org.iq80.leveldb.DBIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.IOException;
import java.util.*;
import static org.ethereum.core.Denomination.*;
public class Blockchain extends ArrayList<Block> {
private static final long serialVersionUID = -143590724563460486L;
@ -28,11 +25,16 @@ public class Blockchain extends ArrayList<Block> {
private long gasPrice = 1000;
private Block lastBlock;
private Map<String, PendingTransaction> pendingTransactions =
Collections.synchronizedMap(new HashMap<String, PendingTransaction>());
// This map of transaction designed
// to approve the tx by external trusted peer
private Map<String, WalletTransaction> walletTransactions =
Collections.synchronizedMap(new HashMap<String, WalletTransaction>());
public Blockchain(Wallet wallet) {
this.db = Config.CHAIN_DB;
this.db = WorldManager.instance.chainDB;
this.wallet = wallet;
this.loadChain();
}
@ -43,8 +45,6 @@ public class Blockchain extends ArrayList<Block> {
public void addBlocks(List<Block> blocks) {
// TODO: redesign this part when the state part and the genesis block is ready
if (blocks.isEmpty())
return;
@ -71,12 +71,12 @@ public class Blockchain extends ArrayList<Block> {
if (logger.isDebugEnabled())
logger.debug("block added to the chain with hash: {}", Hex.toHexString(block.getHash()));
}
// Remove all pending transactions as they already approved by the net
// Remove all wallet 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);
removeWalletTransaction(tx);
}
}
logger.info("*** Block chain size: [ {} ]", this.size());
@ -84,7 +84,12 @@ public class Blockchain extends ArrayList<Block> {
private void addBlock(Block block) {
this.wallet.processBlock(block);
this.gasPrice = block.getMinGasPrice();
// that is the genesis case , we don't want to rely
// on this price will use default 10000000000000
// todo: refactor this longValue some constant defaults class 10000000000000L
this.gasPrice = (block.getMinGasPrice() == 0) ? 10 * SZABO.longValue() : block.getMinGasPrice();
if(lastBlock == null || block.getNumber() > lastBlock.getNumber())
this.lastBlock = block;
this.add(block);
@ -93,7 +98,12 @@ public class Blockchain extends ArrayList<Block> {
public long getGasPrice() {
return gasPrice;
}
/***********************************************************************
* 1) the dialog put a pending transaction on the list
* 2) the dialog send the transaction to a net
@ -101,24 +111,24 @@ public class Blockchain extends ArrayList<Block> {
* 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) {
public WalletTransaction addWalletTransaction(Transaction transaction) {
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction placed hash: {} ", hash );
PendingTransaction pendingTransaction = pendingTransactions.get(hash);
if (pendingTransaction != null)
pendingTransaction.incApproved();
WalletTransaction walletTransaction = this.walletTransactions.get(hash);
if (walletTransaction != null)
walletTransaction.incApproved();
else {
pendingTransaction = new PendingTransaction(transaction);
pendingTransactions.put(hash, pendingTransaction);
walletTransaction = new WalletTransaction(transaction);
this.walletTransactions.put(hash, walletTransaction);
}
return pendingTransaction;
return walletTransaction;
}
public void removePendingTransaction(Transaction transaction){
public void removeWalletTransaction(Transaction transaction){
String hash = Hex.toHexString(transaction.getHash());
logger.info("pending transaction removed with hash: {} ", hash );
pendingTransactions.remove(hash);
walletTransactions.remove(hash);
}
public byte[] getLatestBlockHash(){

View File

@ -17,11 +17,12 @@ public enum Denomination {
private Denomination(BigInteger value) {
this.amount = value;
}
public BigInteger getDenomination() {
return amount;
}
public long longValue() {return getDenomination().longValue();}
private static BigInteger newBigInt(int value) {
return BigInteger.valueOf(10).pow(value);
}

View File

@ -1,14 +1,14 @@
package org.ethereum.core;
import java.math.BigInteger;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Config;
import org.ethereum.manager.WorldManager;
import org.ethereum.util.RLP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
public class Genesis extends Block {
Logger logger = LoggerFactory.getLogger(this.getClass());
@ -54,7 +54,7 @@ public class Genesis extends Block {
logger.info("Genesis-hash: " + Hex.toHexString(this.getHash()));
logger.info("Genesis-stateRoot: " + Hex.toHexString(this.getStateRoot()));
Config.CHAIN_DB.put(getParentHash(), getEncoded());
WorldManager.instance.chainDB.put(getParentHash(), getEncoded());
}
public static Block getInstance() {

View File

@ -12,6 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.BigIntegers;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.Arrays;
@ -76,7 +77,7 @@ public class Transaction {
/* creation contract tx
* [ nonce, gasPrice, gasLimit, 0000000000000000, endowment, init, signature(v, r, s) ]
* or simple send tx
* [ nonce, gasPrice, gasLimit, receiveAddress, value, data, signature(v, r, s) ]
* [ nonce, gasPrice, gasLimit, receiveAddress, longValue, data, signature(v, r, s) ]
*/
public Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data) {
this.nonce = nonce;
@ -129,6 +130,8 @@ public class Transaction {
public byte[] getNonce() {
if (!parsed) rlpParse();
if (nonce == null) return new byte[]{0};
return nonce;
}

View File

@ -1,8 +0,0 @@
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

@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
/**
* Generic interface for Ethereum database
*
* LevelDB key/value pair DB implementation will be used.
* LevelDB key/longValue 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
@ -60,12 +60,12 @@ public class Database {
}
}
/** Insert object(value) (key = sha3(value)) */
/** Insert object(longValue) (key = sha3(longValue)) */
public void put(byte[] key, byte[] value) {
db.put(key, value);
}
/** Get object (key) -> value */
/** Get object (key) -> longValue */
public byte[] get(byte[] key) {
return db.get(key);
}

View File

@ -1,13 +1,12 @@
package org.ethereum.gui;
import org.ethereum.db.Config;
import org.ethereum.manager.MainData;
import org.ethereum.manager.WorldManager;
import org.ethereum.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
@ -74,8 +73,8 @@ public class ToolBar extends JFrame {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Config.CHAIN_DB.close();
Config.STATE_DB.close();
WorldManager.instance.close();
}
});
@ -124,7 +123,7 @@ public class ToolBar extends JFrame {
logToggle = new JToggleButton();
logToggle.setIcon(image_2);
logToggle.setToolTipText("Log Console");
logToggle.setToolTipText("Connect");
logToggle.setContentAreaFilled(true);
logToggle.setBackground(Color.WHITE);
logToggle.setBorderPainted(false);

View File

@ -48,12 +48,17 @@ public class MainData {
wallet.importKey(cowAddr);
AccountState state = wallet.getAddressState(key.getAddress());
state.addToBalance(BigInteger.valueOf(2).pow(200)); // 1606938044258990275541962092341162602522202993782792835301376
state.addToBalance(BigInteger.valueOf(2).pow(200));
wallet.importKey(HashUtil.sha3("cat".getBytes()));
String secret = CONFIG.coinbaseSecret();
byte[] cbAddr = HashUtil.sha3(secret.getBytes());
wallet.importKey(cbAddr);
// Initialize Blockchain
blockChain = new Blockchain(wallet);
// Initialize PeerData
try {
InetAddress ip = InetAddress.getByName(CONFIG.peerDiscoveryIP());
@ -64,9 +69,9 @@ public class MainData {
} catch (UnknownHostException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
public Blockchain getBlockchain() {
return blockChain;
}

View File

@ -0,0 +1,122 @@
package org.ethereum.manager;
import org.ethereum.core.AccountState;
import org.ethereum.core.Transaction;
import org.ethereum.db.Database;
import org.ethereum.trie.Trie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.Arrays;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 07/06/2014 10:08
*/
public class WorldManager {
Logger logger = LoggerFactory.getLogger("main");
public static WorldManager instance = new WorldManager();
private Map<String, Transaction> pendingTransactions =
Collections.synchronizedMap(new HashMap<String, Transaction>());
public Database chainDB = new Database("blockchain");
public Database stateDB = new Database("state");
public Trie allAccountsState = new Trie(stateDB.getDb());
public void applyTransaction(Transaction tx){
// todo: refactor the wallet transactions to the world manager
MainData.instance.getBlockchain().addWalletTransaction(tx);
// 1. VALIDATE THE NONCE
byte[] senderAddress = tx.getSender();
byte[] stateData = allAccountsState.get(senderAddress);
if (stateData == null) {
if (logger.isWarnEnabled())
logger.warn("No such address: {}", Hex.toHexString(senderAddress));
return;
}
AccountState senderState = new AccountState(stateData);
if (senderState.getNonce().compareTo(new BigInteger(tx.getNonce())) != 0){
if (logger.isWarnEnabled())
logger.warn("Invalid nonce account.nonce={} tx.nonce={}",
senderState.getNonce(),
new BigInteger(tx.getNonce()));
return;
}
// 2. FIND OUT WHAT IS THE TRANSACTION TYPE
if (tx.isContractCreation()){
// todo 0. run the init method
} else{
AccountState recieverState;
byte[] accountData = this.allAccountsState.get(tx.getReceiveAddress());
if (accountData.length == 0){
if (logger.isInfoEnabled())
logger.info("New account created address={}",
Hex.toHexString(tx.getReceiveAddress()));
recieverState = new AccountState(tx.getKey());
} else {
recieverState = new AccountState(accountData);
}
// APPLY THE BALANCE VALUE
recieverState.addToBalance(new BigInteger(1, tx.getValue()));
senderState.addToBalance(new BigInteger(1, tx.getValue()).negate());
// todo 2. check if the address is a contract, if it is perform contract call
if (senderState.getBalance().compareTo(BigInteger.ZERO) == 1){
senderState.incrementNonce();
allAccountsState.update(tx.getSender(), senderState.getEncoded());
allAccountsState.update(tx.getReceiveAddress(), recieverState.getEncoded());
}
}
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
}
public void applyTransactionList(List<Transaction> txList){
for (Transaction tx : txList){
applyTransaction(tx);
}
}
public void applyBlock(){
}
public void close(){
chainDB.close();
stateDB.close();
}
}

View File

@ -7,6 +7,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.Transaction;
import org.ethereum.gui.PeerListener;
import org.ethereum.manager.MainData;
@ -18,6 +19,9 @@ import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import static org.ethereum.config.SystemProperties.CONFIG;
/**
@ -63,7 +67,8 @@ public class ClientPeer {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler(15));
ch.pipeline().addLast("readTimeoutHandler",
new ReadTimeoutHandler(CONFIG.activePeerChannelTimeout(), TimeUnit.SECONDS));
ch.pipeline().addLast(new EthereumFrameDecoder());
ch.pipeline().addLast(handler);
}

View File

@ -7,14 +7,13 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.FixedRecvByteBufAllocator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.*;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.gui.PeerListener;
import org.ethereum.manager.MainData;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.Command;
import org.ethereum.net.message.BlocksMessage;
import org.ethereum.net.message.DisconnectMessage;
@ -201,13 +200,13 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
RLPList rlpList = RLP.decode2(payload);
TransactionsMessage transactionsMessage = new TransactionsMessage(rlpList);
for (Transaction tx : transactionsMessage.getTransactions())
MainData.instance.getBlockchain().addPendingTransaction(tx);
// todo: if you got transactions send it to your connected peers
WorldManager.instance.applyTransactionList(transactionsMessage.getTransactions());
logger.info(transactionsMessage.toString());
if (peerListener != null) peerListener.console(transactionsMessage.toString());
}
// got BLOCKS
if (Command.fromInt(command) == BLOCKS) {
logger.info("[Recv: BLOCKS]");
@ -260,6 +259,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
logger.info(blocksMessage.toString());
if (peerListener != null) peerListener.console(blocksMessage.toString());
}
// got GETCHAIN
if (Command.fromInt(command) == GET_CHAIN) {
logger.info("[Recv: GET_CHAIN]");
@ -271,6 +271,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
logger.info(getChainMessage.toString());
if (peerListener != null) peerListener.console(getChainMessage.toString());
}
// got NOTINCHAIN
if (Command.fromInt(command) == NOT_IN_CHAIN) {
logger.info("[Recv: NOT_IN_CHAIN]");
@ -282,11 +283,20 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
logger.info(notInChainMessage.toString());
if (peerListener != null) peerListener.console(notInChainMessage.toString());
}
// got GETTRANSACTIONS
if (Command.fromInt(command) == GET_TRANSACTIONS) {
logger.info("[Recv: GET_TRANSACTIONS]");
if (peerListener != null) peerListener.console("[Recv: GET_TRANSACTIONS]");
// todo: send the queue of the transactions
// todo: return it in the future
// Collection<Transaction> pendingTxList =
// MainData.instance.getBlockchain().getPendingTransactionList();
// TransactionsMessage txMsg =
// new TransactionsMessage(new ArrayList(pendingTxList));
// sendMsg(txMsg, ctx);
}
}

View File

@ -34,18 +34,18 @@ public class TransactionTask implements Callable<Transaction> {
ClientPeer peer = MainData.instance.getActivePeer();
PendingTransaction pendingTransaction = MainData.instance
.getBlockchain().addPendingTransaction(tx);
WalletTransaction walletTransaction = MainData.instance
.getBlockchain().addWalletTransaction(tx);
peer.sendTransaction(tx);
while(pendingTransaction.getApproved() < 1 ){
while(walletTransaction.getApproved() < 1 ){
sleep(10);
}
logger.info("return approved: {}", pendingTransaction.getApproved());
logger.info("return approved: {}", walletTransaction.getApproved());
} catch (Throwable th) {
logger.info("exception caugh: {}", th.getCause());
MainData.instance.getBlockchain().removePendingTransaction(tx);
MainData.instance.getBlockchain().removeWalletTransaction(tx);
}
return null;

View File

@ -8,12 +8,12 @@ import org.ethereum.core.Transaction;
* Created on: 23/05/2014 18:41
*/
public class PendingTransaction {
public class WalletTransaction {
private Transaction tx;
int approved = 0; // each time the tx got from the wire this value increased
public PendingTransaction(Transaction tx) {
public WalletTransaction(Transaction tx) {
this.tx = tx;
}

View File

@ -23,13 +23,13 @@ import static org.spongycastle.util.encoders.Hex.toHexString;
*
* An alternative way of thinking about this to not think of there being a terminator symbol,
* but instead treat bit specifying the existence of the terminator symbol as a bit specifying
* that the given node encodes a final node, where the value is an actual value, rather than
* that the given node encodes a final node, where the value is an actual value, rather than
* the hash of yet another node.
*
* To solve both of these issues, we force the first nibble of the final byte-stream to encode
* two flags, specifying oddness of length (ignoring the 'T' symbol) and terminator status;
* these are placed, respectively, into the two lowest significant bits of the first nibble.
* In the case of an even-length hex string, we must introduce a second nibble (of value zero)
* In the case of an even-length hex string, we must introduce a second nibble (of value zero)
* to ensure the hex-string is even in length and thus is representable by a whole number of bytes.
*
* Examples:
@ -80,7 +80,7 @@ public class CompactEncoder {
/**
* Unpack a binary string to its nibbles equivalent
*
* @param string of binary data
* @param str of binary data
* @return array of nibbles in byte-format
*/
public static byte[] unpackToNibbles(byte[] str) {

View File

@ -808,7 +808,8 @@ public class RLP {
public static byte[] encodeElement(byte[] srcData) {
if (srcData == null) {
if ( srcData == null ||
(srcData.length == 1 && srcData[0] == 0) ) {
return new byte[]{(byte) 0x80};
} if (srcData.length == 1 && srcData[0] < 0x80) {

View File

@ -17,43 +17,46 @@ import java.util.*;
public class Program {
Logger logger = LoggerFactory.getLogger("VM");
ProgramListener listener;
Logger logger = LoggerFactory.getLogger("VM");
ProgramListener listener;
Stack<DataWord> stack = new Stack<DataWord>();
Map<DataWord, DataWord> storage = new HashMap<DataWord, DataWord>();
ByteBuffer memory = null;
Stack<DataWord> stack = new Stack<DataWord>();
Map<DataWord, DataWord> storage = new HashMap<DataWord, DataWord>();
ByteBuffer memory = null;
ByteBuffer hReturn = null;
ByteBuffer hReturn = null;
byte[] ops;
int pc = 0;
boolean stopped = false;
int spendGas = 0;
byte[] ops;
int pc = 0;
boolean stopped = false;
ProgramInvoke invokeData;
ProgramInvoke invokeData;
public Program(byte[] ops, ProgramInvoke invokeData) {
Map<byte[], DataWord> addressChange;
int spendGas = 0;
if (ops == null)
throw new RuntimeException("program can not run with ops: null");
this.invokeData = invokeData;
this.ops = ops;
}
public Program(byte[] ops, ProgramInvoke invokeData) {
public byte getCurrentOp() {
return ops[pc];
}
if (ops == null) throw new RuntimeException("program can not run with ops: null");
public void stackPush(byte[] data) {
DataWord stackWord = new DataWord(data);
stack.push(stackWord);
}
this.invokeData = invokeData;
this.ops = ops;
}
public void stackPush(DataWord stackWord) {
stack.push(stackWord);
}
public byte getCurrentOp(){
return ops[pc];
}
public void stackPush(byte[] data){
DataWord stackWord = new DataWord(data);
stack.push(stackWord);
}
public void stackPush(DataWord stackWord){
stack.push(stackWord);
}
public int getPC() {
return pc;
@ -72,296 +75,285 @@ public class Program {
this.pc = pc;
}
public boolean isStopped() {
return stopped;
}
public boolean isStopped(){
return stopped;
}
public void stop() {
stopped = true;
}
public void stop(){
stopped = true;
}
public void setHReturn(ByteBuffer buff) {
hReturn = buff;
}
public void setHReturn(ByteBuffer buff){
hReturn = buff;
}
public void step() {
++pc;
if (pc >= ops.length)
stop();
}
public void step(){
++pc;
if (pc >= ops.length) stop();
}
public byte[] sweep(int n) {
public byte[] sweep(int n){
if (pc + n > ops.length) {
stop();
throw new RuntimeException("pc overflow sweep n: " + n + " pc: "
+ pc);
}
if (pc + n > ops.length) {
stop();
throw new RuntimeException("pc overflow sweep n: " + n + " pc: " + pc);
}
byte[] data = Arrays.copyOfRange(ops, pc, pc + n);
pc += n;
if (pc >= ops.length)
stop();
byte[] data = Arrays.copyOfRange(ops, pc, pc + n);
pc += n;
if (pc >= ops.length) stop();
return data;
}
return data;
}
public DataWord stackPop() {
public DataWord stackPop(){
if (stack.size() == 0) {
stop();
throw new RuntimeException("attempted pull action for empty stack");
}
return stack.pop();
};
if (stack.size() == 0){
stop();
throw new RuntimeException("attempted pull action for empty stack");
}
return stack.pop();
};
public int getMemSize() {
public int getMemSize(){
int memSize = 0;
if (memory != null)
memSize = memory.limit();
return memSize;
}
int memSize = 0;
if (memory != null) memSize = memory.limit();
return memSize;
}
public void memorySave(DataWord addrB, DataWord value) {
memorySave(addrB.data, value.data);
}
public void memorySave(DataWord addrB, DataWord value){
memorySave(addrB.data, value.data);
}
public void memorySave(byte[] addr, byte[] value) {
public void memorySave(byte[] addr, byte[] value){
int address = new BigInteger(1, addr).intValue();
allocateMemory(address, value);
int address = new BigInteger(1, addr).intValue();
allocateMemory(address, value);
System.arraycopy(value, 0, memory.array(), address, value.length);
}
System.arraycopy(value, 0, memory.array(), address, value.length);
}
public DataWord memoryLoad(DataWord addr) {
public DataWord memoryLoad(DataWord addr){
int address = new BigInteger(1, addr.getData()).intValue();
allocateMemory(address, DataWord.ZERO.data);
int address = new BigInteger(1, addr.getData()).intValue();
allocateMemory(address, DataWord.ZERO.data);
byte[] data = new byte[32];
System.arraycopy(memory.array(), address, data, 0, 32);
byte[] data = new byte[32];
System.arraycopy(memory.array(), address, data , 0 ,32);
return new DataWord(data);
}
return new DataWord(data);
}
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData) {
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData){
int offset = offsetData.value().intValue();
int size = sizeData.value().intValue();
int offset = offsetData.value().intValue();
int size = sizeData.value().intValue();
byte[] chunk = new byte[size];
byte[] chunk = new byte[size];
if (memory.limit() < offset + size)
size = memory.limit() - offset;
System.arraycopy(memory.array(), offset, chunk, 0, size);
if (memory.limit() < offset + size) size = memory.limit() - offset;
return ByteBuffer.wrap(chunk);
}
private void allocateMemory(int address, byte[] value) {
System.arraycopy(memory.array(), offset, chunk, 0, size);
int memSize = 0;
if (memory != null)
memSize = memory.limit();
return ByteBuffer.wrap(chunk);
}
// check if you need to allocate
if (memSize < (address + value.length)) {
int sizeToAllocate = 0;
if (memSize > address) {
private void allocateMemory(int address, byte[] value){
sizeToAllocate = memSize + value.length;
} else {
sizeToAllocate = memSize + (address - memSize) + value.length;
}
int memSize = 0;
if (memory != null) memSize = memory.limit();
// complete to 32
sizeToAllocate = (sizeToAllocate % 32) == 0 ? sizeToAllocate
: sizeToAllocate + (32 - sizeToAllocate % 32);
// check if you need to allocate
if (memSize < (address + value.length)){
sizeToAllocate = (sizeToAllocate == 0) ? 32 : sizeToAllocate;
int sizeToAllocate = 0;
if (memSize > address){
ByteBuffer tmpMem = ByteBuffer.allocate(sizeToAllocate);
if (memory != null)
System.arraycopy(memory.array(), 0, tmpMem.array(), 0,
memory.limit());
sizeToAllocate = memSize + value.length;
} else {
sizeToAllocate = memSize + (address - memSize) + value.length;
}
memory = tmpMem;
}
}
// complete to 32
sizeToAllocate = (sizeToAllocate % 32)==0 ? sizeToAllocate :
sizeToAllocate + (32 - sizeToAllocate % 32);
public void spendGas(int gasValue) {
// todo: check it against avail gas
// todo: out of gas will revert the changes [YP 5, 6 ]
spendGas += gasValue;
}
sizeToAllocate = (sizeToAllocate == 0)? 32: sizeToAllocate;
public void storageSave(DataWord word1, DataWord word2) {
storageSave(word1.getData(), word2.getData());
}
ByteBuffer tmpMem = ByteBuffer.allocate(sizeToAllocate);
if (memory != null)
System.arraycopy(memory.array(), 0, tmpMem.array(), 0, memory.limit());
public void storageSave(byte[] key, byte[] val) {
DataWord keyWord = new DataWord(key);
DataWord valWord = new DataWord(val);
storage.put(keyWord, valWord);
}
public DataWord getOwnerAddress() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getOwnerAddress();
}
memory = tmpMem;
}
}
public DataWord getBalance() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getBalance();
}
public void sendToAddress(byte[] addr, DataWord bChange ){
public DataWord getOriginAddress() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getOriginAddress();
}
DataWord currentBChange = addressChange.get(addr);
if (currentBChange == null){
addressChange.put(addr, bChange);
} else {
currentBChange.add(bChange);
}
}
public DataWord getCallerAddress() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getCallerAddress();
}
public DataWord getMinGasPrice() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getMinGasPrice();
}
public DataWord getCallValue() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getCallValue();
}
public DataWord getDataSize() {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getDataSize();
}
public DataWord getDataValue(DataWord index) {
if (invokeData == null)
return new DataWord(new byte[0]);
return invokeData.getDataValue(index);
}
public byte[] getDataCopy(DataWord offset, DataWord length) {
if (invokeData == null)
return new byte[0];
return invokeData.getDataCopy(offset, length);
}
public DataWord storageLoad(DataWord key) {
return storage.get(key);
}
public void fullTrace() {
// todo: add gas to full trace calc
if (logger.isDebugEnabled()) {
StringBuilder stackData = new StringBuilder();
for (int i = 0; i < stack.size(); ++i) {
stackData.append(" ").append(stack.get(i));
if (i < stack.size() - 1)
stackData.append("\n");
}
if (stackData.length() > 0)
stackData.insert(0, "\n");
StringBuilder storageData = new StringBuilder();
for (DataWord key : storage.keySet()) {
storageData.append(" ").append(key).append(" -> ")
.append(storage.get(key)).append("\n");
}
if (storageData.length() > 0)
storageData.insert(0, "\n");
StringBuilder memoryData = new StringBuilder();
StringBuilder oneLine = new StringBuilder();
for (int i = 0; memory != null && i < memory.limit(); ++i) {
byte value = memory.get(i);
oneLine.append(Utils.oneByteToHexString(value)).append(" ");
if ((i + 1) % 16 == 0) {
String tmp = String.format("[%4s]-[%4s]",
Integer.toString(i - 15, 16),
Integer.toString(i, 16)).replace(" ", "0");
memoryData.append("").append(tmp).append(" ");
memoryData.append(oneLine);
if (i < memory.limit())
memoryData.append("\n");
oneLine.setLength(0);
}
}
if (memoryData.length() > 0)
memoryData.insert(0, "\n");
StringBuilder opsString = new StringBuilder();
for (int i = 0; i < ops.length; ++i) {
String tmpString = Integer.toString(ops[i] & 0xFF, 16);
tmpString = tmpString.length() == 1 ? "0" + tmpString
: tmpString;
if (i != pc)
opsString.append(tmpString);
else
opsString.append(" >>").append(tmpString).append("");
}
if (pc >= ops.length)
opsString.append(" >>");
if (opsString.length() > 0)
opsString.insert(0, "\n ");
logger.debug(" -- OPS -- {}", opsString);
logger.debug(" -- STACK -- {}", stackData);
logger.debug(" -- MEMORY -- {}", memoryData);
logger.debug(" -- STORAGE -- {}\n", storageData);
StringBuilder global = new StringBuilder("\n");
if (stackData.length() > 0)
stackData.append("\n");
global.append(" -- OPS -- ").append(opsString).append("\n");
global.append(" -- STACK -- ").append(stackData).append("\n");
global.append(" -- MEMORY -- ").append(memoryData).append("\n");
global.append(" -- STORAGE -- ").append(storageData).append("\n");
if (hReturn != null) {
global.append("\n HReturn: ").append(
Hex.toHexString(hReturn.array()));
}
if (listener != null) {
listener.output(global.toString());
}
}
}
public void addListener(ProgramListener listener) {
this.listener = listener;
}
public interface ProgramListener {
public void output(String out);
}
public void spendGas(int gasValue){
// todo: check it against avail gas
// todo: out of gas will revert the changes [YP 5, 6 ]
spendGas += gasValue;
}
public void storageSave(DataWord word1, DataWord word2){
storageSave(word1.getData(), word2.getData());
}
public void storageSave(byte[] key, byte[] val){
DataWord keyWord = new DataWord(key);
DataWord valWord = new DataWord(val);
storage.put(keyWord, valWord);
}
public DataWord getOwnerAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOwnerAddress();
}
public DataWord getBalance(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getBalance();
}
public DataWord getOriginAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getOriginAddress();
}
public DataWord getCallerAddress(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallerAddress();
}
public DataWord getMinGasPrice(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getMinGasPrice();
}
public DataWord getCallValue(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getCallValue();
}
public DataWord getDataSize(){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getDataSize();
}
public DataWord getDataValue(DataWord index){
if (invokeData == null) return new DataWord( new byte[0]);
return invokeData.getDataValue(index);
}
public byte[] getDataCopy(DataWord offset, DataWord length){
if (invokeData == null) return new byte[0];
return invokeData.getDataCopy(offset, length);
}
public DataWord storageLoad(DataWord key){
return storage.get(key);
}
public void fullTrace(){
// todo: add gas to full trace calc
if (logger.isDebugEnabled()){
StringBuilder stackData = new StringBuilder();
for (int i = 0; i < stack.size(); ++i){
stackData.append(" ").append(stack.get(i));
if (i < stack.size() - 1) stackData.append("\n");
}
if (stackData.length() > 0) stackData.insert(0, "\n");
StringBuilder storageData = new StringBuilder();
for (DataWord key : storage.keySet()){
storageData.append(" ").append(key).append(" -> ").append(storage.get(key)).append("\n");
}
if (storageData.length() > 0) storageData.insert(0, "\n");
StringBuilder memoryData = new StringBuilder();
StringBuilder oneLine = new StringBuilder();
for (int i = 0; memory != null && i < memory.limit(); ++i){
byte value = memory.get(i);
oneLine.append(Utils.oneByteToHexString(value)).append(" ");
if ((i + 1) % 16 == 0) {
String tmp = String.format("[%4s]-[%4s]", Integer.toString(i - 15, 16),
Integer.toString(i, 16)).replace(" ", "0");
memoryData.append("" ).append(tmp).append(" ");
memoryData.append(oneLine);
if (i < memory.limit()) memoryData.append("\n");
oneLine.setLength(0);
}
}
if (memoryData.length() > 0) memoryData.insert(0, "\n");
StringBuilder opsString = new StringBuilder();
for (int i = 0; i < ops.length; ++i){
String tmpString = Integer.toString(ops[i] & 0xFF, 16);
tmpString = tmpString.length() == 1? "0" + tmpString : tmpString;
if (i != pc)
opsString.append(tmpString);
else
opsString.append(" >>").append(tmpString).append("");
}
if (pc >= ops.length) opsString.append(" >>");
if (opsString.length() > 0) opsString.insert(0, "\n ");
logger.debug(" -- OPS -- {}", opsString);
logger.debug(" -- STACK -- {}", stackData);
logger.debug(" -- MEMORY -- {}", memoryData);
logger.debug(" -- STORAGE -- {}\n", storageData);
StringBuilder global = new StringBuilder("\n");
if (stackData.length() > 0) stackData.append("\n");
global.append(" -- OPS -- ").append(opsString).append("\n");
global.append(" -- STACK -- ").append(stackData).append("\n");
global.append(" -- MEMORY -- ").append(memoryData).append("\n");
global.append(" -- STORAGE -- ").append(storageData).append("\n");
if (hReturn != null){
global.append("\n HReturn: ").append(Hex.toHexString(hReturn.array()));
}
if (listener != null){
listener.output(global.toString());
}
};
}
public void addListener(ProgramListener listener){
this.listener = listener;
}
public interface ProgramListener{
public void output(String out);
}
}

View File

@ -527,10 +527,29 @@ public class VM {
program.stackPush(data);
}
break;
case CREATE:
break;
case CALL:
break;
case CREATE:{
DataWord gas = program.stackPop();
DataWord inOffset = program.stackPop();
DataWord inSize = program.stackPop();
// todo: implement contract creation
program.step();
}
break;
case CALL:{
DataWord gas = program.stackPop();
DataWord toAddress = program.stackPop();
DataWord value = program.stackPop();
program.sendToAddress(toAddress.data, value);
// todo: find out if we should or not execute
// todo: the contract for real
program.step();
}
break;
case RETURN:{
DataWord offset = program.stackPop();

View File

@ -26,8 +26,9 @@ peer.discovery.port = 30303
# Nick
#peer.active.ip = 82.217.72.169
#peer.active.port = 30303
# RomanJ
# RomanJ general
peer.active.ip = 54.211.14.10
peer.active.port = 50505
@ -58,7 +59,13 @@ peer.discovery.timeout = 2
# transaction got approved when
# include into a transactions msg
# retrieved from the peer [seconds]
transaction.approve.timeout = 5
transaction.approve.timeout = 360
# the parameter specifies how much
# time the active peer will wait
# for a message to come before kill
# the channel
active.peer.channel.timeout = 360
# default directory where we keep
# basic Serpent samples relative
@ -70,3 +77,9 @@ samples.dir = samples
# destroyed and all the data will be
# downloaded from peers again
database.reset = true
# this string is computed
# to be eventually the address
# that get the miner reward
coinbase.secret = "monkey"