Merge pull request #27 from nicksavers/master

Refactoring and cleanup with documentation
This commit is contained in:
romanman 2014-06-07 22:43:50 +01:00
commit 037e1f230c
18 changed files with 274 additions and 323 deletions

View File

@ -0,0 +1,51 @@
package org.ethereum.core;
import java.math.BigInteger;
import org.ethereum.crypto.ECKey;
import org.ethereum.util.Utils;
/**
* Representation of an actual account or contract
*/
public class Account {
private ECKey ecKey;
private byte[] address;
private AccountState state;
public Account() {
this.ecKey = new ECKey(Utils.getRandom());
this.state = new AccountState();
}
public Account(ECKey ecKey) {
this.ecKey = ecKey;
this.state = new AccountState();
}
public Account(ECKey ecKey, BigInteger nonce, BigInteger balance) {
this.ecKey = ecKey;
this.state = new AccountState(nonce, balance);
}
public ECKey getEcKey() {
return ecKey;
}
public byte[] getAddress() {
return address;
}
public void setAddress(byte[] address) {
this.address = address;
}
public AccountState getState() {
return state;
}
public void setState(AccountState state) {
this.state = state;
}
}

View File

@ -1,16 +1,13 @@
package org.ethereum.core;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
import org.ethereum.util.Utils;
import java.math.BigInteger;
public class AccountState {
private ECKey ecKey;
private byte[] rlpEncoded;
/* A value equal to the number of transactions sent
@ -42,39 +39,26 @@ public class AccountState {
private byte[] codeHash = HashUtil.sha3(new byte[0]);
public AccountState() {
this(new ECKey(Utils.getRandom()));
this(BigInteger.ZERO, BigInteger.ZERO);
}
public AccountState(ECKey ecKey) {
this(ecKey, BigInteger.ZERO, BigInteger.ZERO);
public AccountState(BigInteger nonce, BigInteger balance) {
this.nonce = nonce;
this.balance = balance;
}
public AccountState(byte[] rlpData) {
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.balance = new BigInteger(1, items.get(1).getRLPData());
this.balance = new BigInteger(1, ((items.get(1).getRLPData()) == null ? new byte[]{0} :
items.get(1).getRLPData()));
this.stateRoot = items.get(2).getRLPData();
this.codeHash = items.get(3).getRLPData();
}
public AccountState(ECKey ecKey, BigInteger nonce, BigInteger balance) {
this.ecKey = ecKey;
this.nonce = nonce;
this.balance = balance;
}
public AccountState(BigInteger nonce, BigInteger balance) {
this.nonce = nonce;
this.balance = balance;
}
public ECKey getEcKey() {
return ecKey;
}
public BigInteger getNonce() {
return nonce;
}

View File

@ -56,7 +56,7 @@ public class Block {
difficulty, number, minGasPrice, gasLimit, gasUsed,
timestamp, extraData, nonce);
this.txsState = new Trie(null);
this.header.setStateRoot(WorldManager.instance.allAccountsState.getRootHash());
this.header.setStateRoot(WorldManager.instance.worldState.getRootHash());
this.header.setTxTrieRoot(txsState.getRootHash());
this.transactionsList = transactionsList;
this.uncleList = uncleList;
@ -275,8 +275,8 @@ public class Block {
public byte[] updateState(byte[] key, byte[] value) {
WorldManager.instance.allAccountsState.update(key, value);
byte[] stateRoot = WorldManager.instance.allAccountsState.getRootHash();
WorldManager.instance.worldState.update(key, value);
byte[] stateRoot = WorldManager.instance.worldState.getRootHash();
this.header.setStateRoot(stateRoot);
return stateRoot;
}

View File

@ -18,7 +18,7 @@ public class Blockchain extends ArrayList<Block> {
private static final long serialVersionUID = -143590724563460486L;
private static Logger logger = LoggerFactory.getLogger(Blockchain.class);
private static Logger logger = LoggerFactory.getLogger("Chain");
// to avoid using minGasPrice=0 from Genesis for the wallet
private static long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();

View File

@ -12,7 +12,6 @@ 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;

View File

@ -23,9 +23,8 @@ import java.util.HashMap;
import java.util.List;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 17/05/14 15:53
* The Wallet handles the management of accounts with addresses and private keys.
* New accounts can be generated and added to the wallet and existing accounts can be queried.
*/
public class Wallet {
@ -34,24 +33,24 @@ public class Wallet {
// private HashMap<Address, BigInteger> rows = new HashMap<>();
// <address, info> table for a wallet
private HashMap<String, AccountState> rows = new HashMap<String, AccountState>();
private HashMap<String, Account> rows = new HashMap<String, Account>();
private long high;
private List<WalletListener> listeners = new ArrayList<WalletListener>();
private HashMap<BigInteger, Transaction> transactionMap = new HashMap<BigInteger, Transaction>();
public void addNewKey(){
AccountState addressState = new AccountState();
String address = Hex.toHexString(addressState.getEcKey().getAddress());
rows.put(address, addressState);
public void addNewAccount() {
Account account = new Account();
String address = Hex.toHexString(account.getEcKey().getAddress());
rows.put(address, account);
for (WalletListener listener : listeners) listener.valueChanged();
}
public void importKey(byte[] privKey){
AccountState addressState = new AccountState(ECKey.fromPrivate(privKey));
String address = Hex.toHexString(addressState.getEcKey().getAddress());
rows.put(address, addressState);
Account account = new Account(ECKey.fromPrivate(privKey));
String address = Hex.toHexString(account.getEcKey().getAddress());
rows.put(address, account);
notifyListeners();
}
@ -59,24 +58,24 @@ public class Wallet {
this.listeners.add(walletListener);
}
public Collection<AccountState> getAddressStateCollection(){
public Collection<Account> getAccountCollection(){
return rows.values();
}
public AccountState getAddressState(byte[] addressBytes){
public AccountState getAccountState(byte[] addressBytes){
String address = Hex.toHexString(addressBytes);
return rows.get(address);
return rows.get(address).getState();
}
public BigInteger getBalance(byte[] addressBytes){
String address = Hex.toHexString(addressBytes);
return rows.get(address).getBalance();
return rows.get(address).getState().getBalance();
}
public BigInteger totalBalance(){
BigInteger sum = BigInteger.ZERO;
for (AccountState addressState : rows.values()){
sum = sum.add(addressState.getBalance());
for (Account account : rows.values()){
sum = sum.add(account.getState().getBalance());
}
return sum;
}
@ -86,18 +85,18 @@ public class Wallet {
transactionMap.put(new BigInteger(transaction.getHash()), transaction );
byte[] senderAddress = transaction.getSender();
AccountState senderState = rows.get(Hex.toHexString(senderAddress));
if (senderState != null){
Account sender = rows.get(Hex.toHexString(senderAddress));
if (sender != null){
BigInteger value = new BigInteger(-1, transaction.getValue());
senderState.addToBalance(value);
senderState.incrementNonce();
sender.getState().addToBalance(value);
sender.getState().incrementNonce();
}
byte[] receiveAddress = transaction.getReceiveAddress();
AccountState receiverState = rows.get(Hex.toHexString(receiveAddress));
if (receiverState != null){
receiverState.addToBalance(new BigInteger(1, transaction.getValue()));
Account receiver = rows.get(Hex.toHexString(receiveAddress));
if (receiver != null){
receiver.getState().addToBalance(new BigInteger(1, transaction.getValue()));
}
notifyListeners();
@ -214,7 +213,7 @@ public class Wallet {
walletElement.setAttributeNode(high);
int i = 0;
for (AccountState addressState : getAddressStateCollection()){
for (Account account : getAccountCollection()){
Element raw = doc.createElement("raw");
Attr id = doc.createAttribute("id");
@ -222,17 +221,17 @@ public class Wallet {
raw.setAttributeNode(id);
Element addressE = doc.createElement("address");
addressE.setTextContent(Hex.toHexString(addressState.getEcKey().getAddress()));
addressE.setTextContent(Hex.toHexString(account.getEcKey().getAddress()));
Attr nonce = doc.createAttribute("nonce");
nonce.setValue("0");
addressE.setAttributeNode(nonce);
Element privKey = doc.createElement("privkey");
privKey.setTextContent(Hex.toHexString(addressState.getEcKey().getPrivKeyBytes()));
privKey.setTextContent(Hex.toHexString(account.getEcKey().getPrivKeyBytes()));
Element value = doc.createElement("value");
value.setTextContent(addressState.getBalance().toString());
value.setTextContent(account.getState().getBalance().toString());
raw.appendChild(addressE);
raw.appendChild(privKey);

View File

@ -1,6 +1,6 @@
package org.ethereum.gui;
import org.ethereum.core.AccountState;
import org.ethereum.core.Account;
import org.ethereum.core.Transaction;
import org.ethereum.manager.MainData;
import org.ethereum.net.client.ClientPeer;
@ -36,7 +36,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
ContractCallDialog dialog;
JComboBox<AddressStateWraper> creatorAddressCombo;
JComboBox<AccountWrapper> creatorAddressCombo;
final JTextField gasInput;
final JTextField contractAddrInput;
JTextArea msgDataTA;
@ -116,7 +116,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
gasInput.setText("1000");
JComboBox<AddressStateWraper> creatorAddressCombo = new JComboBox<AddressStateWraper>() {
JComboBox<AccountWrapper> creatorAddressCombo = new JComboBox<AccountWrapper>() {
@Override
public ComboBoxUI getUI() {
BasicComboBoxUI ui = (BasicComboBoxUI) super.getUI();
@ -135,11 +135,11 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent());
editor.setForeground(Color.RED);
Collection<AccountState> addressStates =
MainData.instance.getWallet().getAddressStateCollection();
Collection<Account> accounts =
MainData.instance.getWallet().getAccountCollection();
for (AccountState addressState : addressStates){
creatorAddressCombo.addItem(new AddressStateWraper(addressState));
for (Account account : accounts){
creatorAddressCombo.addItem(new AccountWrapper(account));
}
creatorAddressCombo.setRenderer(new DefaultListCellRenderer() {
@ -229,10 +229,10 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
byte[] contractAddress = Hex.decode( contractAddrInput.getText());
AccountState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState();
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = account.getState().getNonce() == BigInteger.ZERO ? null : account.getState().getNonce().toByteArray();
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
BigInteger gasBI = new BigInteger(gasInput.getText());
@ -269,22 +269,22 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
worker.execute();
}
public class AddressStateWraper {
public class AccountWrapper {
private AccountState addressState;
private Account account;
public AddressStateWraper(AccountState addressState) {
this.addressState = addressState;
public AccountWrapper(Account account) {
this.account = account;
}
public AccountState getAddressState() {
return addressState;
public Account getAccount() {
return account;
}
@Override
public String toString() {
String addressShort = Utils.getAddressShortString(addressState.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(addressState.getBalance());
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(account.getState().getBalance());
String result = String.format(" By: [%s] %s", addressShort,
valueShort);
return result;

View File

@ -1,5 +1,6 @@
package org.ethereum.gui;
import org.ethereum.core.Account;
import org.ethereum.core.AccountState;
import org.ethereum.core.Transaction;
import org.ethereum.manager.MainData;
@ -32,7 +33,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
private static final long serialVersionUID = -3622984456084608996L;
ContractSubmitDialog dialog;
JComboBox<AddressStateWraper> creatorAddressCombo;
JComboBox<AccountWrapper> creatorAddressCombo;
final JTextField gasInput;
final JTextField contractAddrInput;
@ -139,7 +140,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
gasInput.setText("1000");
JComboBox<AddressStateWraper> creatorAddressCombo = new JComboBox<AddressStateWraper>(){
JComboBox<AccountWrapper> creatorAddressCombo = new JComboBox<AccountWrapper>(){
@Override
public ComboBoxUI getUI() {
@ -160,12 +161,11 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent());
editor.setForeground(Color.RED);
Collection<AccountState> addressStates =
MainData.instance.getWallet().getAddressStateCollection();
Collection<Account> accounts =
MainData.instance.getWallet().getAccountCollection();
for (AccountState addressState : addressStates){
creatorAddressCombo.addItem(new AddressStateWraper(addressState));
for (Account account : accounts){
creatorAddressCombo.addItem(new AccountWrapper(account));
}
creatorAddressCombo.setRenderer(new DefaultListCellRenderer() {
@ -251,10 +251,10 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
public void submitContract(){
AccountState addressState = ((AddressStateWraper)creatorAddressCombo.getSelectedItem()).getAddressState();
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = account.getState().getNonce() == BigInteger.ZERO ? null : account.getState().getNonce().toByteArray();
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
BigInteger gasBI = new BigInteger(gasInput.getText());
@ -297,22 +297,22 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
pod.setVisible(true);
}
public class AddressStateWraper{
public class AccountWrapper{
private AccountState addressState;
private Account account;
public AddressStateWraper(AccountState addressState) {
this.addressState = addressState;
public AccountWrapper(Account account) {
this.account = account;
}
public AccountState getAddressState() {
return addressState;
public Account getAccount() {
return account;
}
@Override
public String toString() {
String addressShort = Utils.getAddressShortString(addressState.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(addressState.getBalance());
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(account.getState().getBalance());
String result =
String.format(" By: [%s] %s", addressShort, valueShort);

View File

@ -1,5 +1,6 @@
package org.ethereum.gui;
import org.ethereum.core.Account;
import org.ethereum.core.AccountState;
import org.ethereum.core.Transaction;
import org.ethereum.manager.MainData;
@ -35,11 +36,11 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
final JTextField amountInput;
final JTextField feeInput;
public PayOutDialog(Frame parent, final AccountState addressState) {
public PayOutDialog(Frame parent, final Account account) {
super(parent, "Payout details: ", false);
dialog = this;
this.addressState = addressState;
this.addressState = account.getState();
receiverInput = new JTextField(18);
GUIUtils.addStyle(receiverInput, "Pay to:");
@ -114,7 +115,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
return;
}
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
byte[] gasPrice = BigInteger.valueOf( MainData.instance.getBlockchain().getGasPrice()).toByteArray();
@ -189,7 +190,6 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
return false;
}
// check if the tx is affordable
BigInteger ammountValue = new BigInteger(amountText);
BigInteger feeValue = new BigInteger(feeText);
@ -261,8 +261,8 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
}
public static void main(String args[]) {
AccountState as = new AccountState();
PayOutDialog pod = new PayOutDialog(null, as);
Account account = new Account();
PayOutDialog pod = new PayOutDialog(null, account);
pod.setVisible(true);
}
}

View File

@ -1,5 +1,6 @@
package org.ethereum.gui;
import org.ethereum.core.Account;
import org.ethereum.core.AccountState;
import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex;
@ -21,7 +22,7 @@ import java.net.URL;
*/
public class WalletAddressPanel extends JPanel{
public WalletAddressPanel(final AccountState addressState) {
public WalletAddressPanel(final Account account) {
final WalletAddressPanel walletAddressPanel = this;
@ -35,7 +36,7 @@ public class WalletAddressPanel extends JPanel{
addressField.setBorder(border);
addressField.setEnabled(true);
addressField.setEditable(false);
addressField.setText(Hex.toHexString(addressState.getEcKey().getAddress()).toUpperCase());
addressField.setText(Hex.toHexString(account.getEcKey().getAddress()).toUpperCase());
addressField.setForeground(new Color(143, 170, 220));
addressField.setFont(new Font("Monospaced", 0, 12));
addressField.setPreferredSize(new Dimension(300, 35));
@ -46,7 +47,7 @@ public class WalletAddressPanel extends JPanel{
amount.setBorder(border);
amount.setEnabled(true);
amount.setEditable(false);
amount.setText(Utils.getValueShortString(addressState.getBalance()));
amount.setText(Utils.getValueShortString(account.getState().getBalance()));
amount.setForeground(new Color(143, 170, 220));
amount.setBackground(Color.WHITE);
amount.setPreferredSize(new Dimension(100, 35));
@ -65,7 +66,7 @@ public class WalletAddressPanel extends JPanel{
PayOutDialog payOutDialog =
new PayOutDialog((Frame)SwingUtilities.getAncestorOfClass(JFrame.class,
walletAddressPanel), addressState);
walletAddressPanel), account);
}
});

View File

@ -1,6 +1,6 @@
package org.ethereum.gui;
import org.ethereum.core.AccountState;
import org.ethereum.core.Account;
import org.ethereum.core.Wallet;
import org.ethereum.manager.MainData;
@ -56,10 +56,9 @@ public class WalletWindow extends JFrame implements Wallet.WalletListener{
Wallet wallet = MainData.instance.getWallet();
for (AccountState addressState : wallet.getAddressStateCollection()){
for (Account account : wallet.getAccountCollection()){
WalletAddressPanel rowPanel =
new WalletAddressPanel(addressState);
WalletAddressPanel rowPanel = new WalletAddressPanel(account);
contentPane.add(rowPanel);
}
@ -78,13 +77,13 @@ public class WalletWindow extends JFrame implements Wallet.WalletListener{
public void mouseClicked(MouseEvent e) {
Wallet wallet = MainData.instance.getWallet();
if (wallet.getAddressStateCollection().size() >=5){
if (wallet.getAccountCollection().size() >=5){
JOptionPane.showMessageDialog(walletWindow,
"Hey do you really need more than 5 address for a demo wallet");
return;
}
wallet.addNewKey();
wallet.addNewAccount();
Dimension dimension = walletWindow.getSize();
int height = dimension.height;
int width = dimension.width;

View File

@ -47,7 +47,7 @@ public class MainData {
ECKey key = ECKey.fromPrivate(cowAddr);
wallet.importKey(cowAddr);
AccountState state = wallet.getAddressState(key.getAddress());
AccountState state = wallet.getAccountState(key.getAddress());
state.addToBalance(BigInteger.valueOf(2).pow(200));
wallet.importKey(HashUtil.sha3("cat".getBytes()));

View File

@ -1,6 +1,5 @@
package org.ethereum.manager;
import io.netty.channel.AddressedEnvelope;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
@ -21,15 +20,12 @@ import java.util.List;
import java.util.Map;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 07/06/2014 10:08
* WorldManager is the main class to handle the processing of transactions and managing the world state.
*/
public class WorldManager {
Logger logger = LoggerFactory.getLogger("main");
Logger stateLogger = LoggerFactory.getLogger("state");
private Logger logger = LoggerFactory.getLogger("main");
private Logger stateLogger = LoggerFactory.getLogger("state");
public static WorldManager instance = new WorldManager();
@ -39,17 +35,16 @@ public class WorldManager {
public Database chainDB = new Database("blockchain");
public Database stateDB = new Database("state");
public Trie allAccountsState = new Trie(stateDB.getDb());
public Trie worldState = new Trie(stateDB.getDb());
public void applyTransaction(Transaction tx) {
public void applyTransaction(Transaction tx){
// todo: refactor the wallet transactions to the world manager
// 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);
byte[] stateData = worldState.get(senderAddress);
if (stateData == null || stateData.length == 0) {
if (stateLogger.isWarnEnabled())
@ -58,61 +53,51 @@ public class WorldManager {
}
AccountState senderState = new AccountState(stateData);
if (senderState.getNonce().compareTo(new BigInteger(tx.getNonce())) != 0){
if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
senderState.getNonce(),
new BigInteger(tx.getNonce()));
if (senderState.getNonce().compareTo(new BigInteger(tx.getNonce())) != 0) {
if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
senderState.getNonce(), new BigInteger(tx.getNonce()));
return;
}
// 2. THE SIMPLE BALANCE CHANGE SHOULD HAPPEN ANYWAY
AccountState recieverState = null;
AccountState receiverState = null;
// Check if the receive is a new contract
if (tx.isContractCreation()){
byte[] contractAddress= tx.getContractAddress();
AccountState contractAccount = new AccountState(BigInteger.ZERO, BigInteger.valueOf(0));
allAccountsState.update(contractAddress, contractAccount.getEncoded());
recieverState = contractAccount;
if (tx.isContractCreation()) {
byte[] contractAddress = tx.getContractAddress();
receiverState = new AccountState();
worldState.update(contractAddress, receiverState.getEncoded());
stateLogger.info("New contract created address={}",
Hex.toHexString(contractAddress));
}
// if reciver was not set by
// creation of contract
if (recieverState == null) {
byte[] accountData = this.allAccountsState.get(tx.getReceiveAddress());
} else {
// receiver was not set by creation of contract
byte[] accountData = this.worldState.get(tx.getReceiveAddress());
if (accountData.length == 0){
recieverState = new AccountState(tx.getKey());
receiverState = new AccountState();
if (stateLogger.isInfoEnabled())
stateLogger.info("New account created address={}",
Hex.toHexString(tx.getReceiveAddress()));
} else {
recieverState = new AccountState(accountData);
receiverState = new AccountState(accountData);
if (stateLogger.isInfoEnabled())
stateLogger.info("Account updated address={}",
Hex.toHexString(tx.getReceiveAddress()));
}
}
recieverState.addToBalance(new BigInteger(1, tx.getValue()));
senderState.addToBalance(new BigInteger(1, tx.getValue()).negate());
if (senderState.getBalance().compareTo(BigInteger.ZERO) == 1){
senderState.incrementNonce();
allAccountsState.update(tx.getSender(), senderState.getEncoded());
allAccountsState.update(tx.getReceiveAddress(), recieverState.getEncoded());
if(tx.getValue() != null) {
receiverState.addToBalance(new BigInteger(1, tx.getValue()));
senderState.addToBalance(new BigInteger(1, tx.getValue()).negate());
}
if (senderState.getBalance().compareTo(BigInteger.ZERO) == 1) {
senderState.incrementNonce();
worldState.update(tx.getSender(), senderState.getEncoded());
worldState.update(tx.getReceiveAddress(), receiverState.getEncoded());
}
// 3. FIND OUT WHAT IS THE TRANSACTION TYPE
if (tx.isContractCreation()){
if (tx.isContractCreation()) {
byte[] initCode = tx.getData();
@ -122,7 +107,7 @@ public class WorldManager {
ProgramResult result = program.getResult();
byte[] bodyCode = result.gethReturn().array();
// todo: what if the body code is null , still submit ?
// TODO: what if the body code is null , still submit ?
// TODO: (!!!!!) ALL THE CHECKS FOR THE PROGRAM RESULT
@ -133,40 +118,31 @@ public class WorldManager {
stateLogger.info("saving code of the contract to the db: sha3(code)={} code={}",
Hex.toHexString(key),
Hex.toHexString(bodyCode));
} else{
// todo 2. check if the address is a contract, if it is perform contract call
} else {
// TODO: 2. check if the address is a contract, if it is perform contract call
}
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
}
public void applyTransactionList(List<Transaction> txList){
public void applyTransactionList(List<Transaction> txList) {
for (Transaction tx : txList){
applyTransaction(tx);
}
}
public void applyBlock(Block block){
public void applyBlock(Block block) {
List<Transaction> txList = block.getTransactionsList();
applyTransactionList(txList);
}
public void applyBlockList(List<Block> blocks){
for (int i = blocks.size() - 1; i >= 0 ; --i){
public void applyBlockList(List<Block> blocks) {
for (int i = blocks.size() - 1; i >= 0 ; --i) {
applyBlock(blocks.get(i));
}
}
public void close(){
public void close() {
chainDB.close();
stateDB.close();
}
}

View File

@ -7,7 +7,6 @@ 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;
@ -23,7 +22,6 @@ import java.util.concurrent.TimeUnit;
import static org.ethereum.config.SystemProperties.CONFIG;
/**
* www.ethereumJ.com
* User: Roman Mandeleil

View File

@ -10,7 +10,6 @@ import io.netty.channel.FixedRecvByteBufAllocator;
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;
@ -38,7 +37,7 @@ import org.spongycastle.util.encoders.Hex;
*/
public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
Logger logger = LoggerFactory.getLogger("wire");
Logger logger = LoggerFactory.getLogger("Wire");
Timer chainAskTimer = new Timer();
int secToAskForChain = 1;

View File

@ -5,7 +5,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.FixedRecvByteBufAllocator;
import org.ethereum.crypto.HashUtil;
import org.ethereum.gui.PeerListener;
import org.ethereum.manager.MainData;
import org.ethereum.net.Command;
@ -29,13 +28,12 @@ import static org.ethereum.net.Command.*;
*/
public class EthereumPeerTasterHandler extends ChannelInboundHandlerAdapter {
Logger logger = LoggerFactory.getLogger(getClass());
Logger logger = LoggerFactory.getLogger("Wire");
Timer timer = null;
private final static byte[] MAGIC_PREFIX = {(byte)0x22, (byte)0x40, (byte)0x08, (byte)0x91};
private final static byte[] HELLO_MESSAGE = StaticMessages.HELLO_MESSAGE.getPayload();
private final static byte[] HELLO_MESSAGE_LEN = ByteUtil.calcPacketLength(HELLO_MESSAGE);
private long lastPongTime = 0;
private boolean tearDown = false;
@ -200,7 +198,4 @@ public class EthereumPeerTasterHandler extends ChannelInboundHandlerAdapter {
buffer.writeBytes(StaticMessages.GET_PEERS);
ctx.writeAndFlush(buffer);
}
}

View File

@ -10,21 +10,47 @@ import java.nio.ByteBuffer;
import static org.ethereum.vm.OpCode.PUSH1;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 01/06/2014 10:44
* The Ethereum Virtual Machine (EVM) is responsible for initialization
* and executing a transaction on a contract.
*
* It is a quasi-Turing-complete machine; the quasi qualification
* comes from the fact that the computation is intrinsically bounded
* through a parameter, gas, which limits the total amount of computation done.
*
* The EVM is a simple stack-based architecture. The word size of the machine
* (and thus size of stack item) is 256-bit. This was chosen to facilitate
* the SHA3-256 hash scheme and elliptic-curve computations. The memory model
* is a simple word-addressed byte array. The stack has an unlimited size.
* The machine also has an independent storage model; this is similar in concept
* to the memory but rather than a byte array, it is a word-addressable word array.
*
* Unlike memory, which is volatile, storage is non volatile and is
* maintained as part of the system state. All locations in both storage
* and memory are well-defined initially as zero.
*
* The machine does not follow the standard von Neumann architecture.
* Rather than storing program code in generally-accessible memory or storage,
* it is stored separately in a virtual ROM interactable only though
* a specialised instruction.
*
* The machine can have exceptional execution for several reasons,
* including stack underflows and invalid instructions. These unambiguously
* and validly result in immediate halting of the machine with all state changes
* left intact. The one piece of exceptional execution that does not leave
* state changes intact is the out-of-gas (OOG) exception.
*
* Here, the machine halts immediately and reports the issue to
* the execution agent (either the transaction processor or, recursively,
* the spawning execution environment) and which will deal with it separately.
*/
public class VM {
static private BigInteger _32_ = BigInteger.valueOf(32);
Logger logger = LoggerFactory.getLogger("VM");
private Logger logger = LoggerFactory.getLogger(VM.class);
public void step(Program program) {
try {
byte op = program.getCurrentOp();
@ -35,73 +61,63 @@ public class VM {
switch (OpCode.code(op)) {
case SHA3:
program.spendGas(GasCost.SHA3);
break;
break;
case SLOAD:
program.spendGas(GasCost.SLOAD);
break;
break;
case SSTORE:
// todo: calc gas in the execution
// todo: according to the size
break;
break;
case BALANCE:
program.spendGas(GasCost.BALANCE);
break;
break;
case CREATE:
program.spendGas(GasCost.CREATE);
break;
break;
case CALL:
program.spendGas(GasCost.CALL);
break;
break;
case MSTORE8:
case MSTORE:
// todo: calc gas in the execution
// todo: according to the size
break;
break;
default:
program.spendGas(GasCost.STEP);
break;
break;
}
switch (OpCode.code(op)) {
/**
* Stop and Arithmetic Operations
*/
case STOP:{
program.setHReturn(ByteBuffer.allocate(0));
program.stop();
}
break;
} break;
case ADD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.add(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case MUL:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.mul(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case SUB:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.sub(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case DIV:{
DataWord word1 = program.stackPop();
@ -109,8 +125,7 @@ public class VM {
word1.div(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case SDIV:{
DataWord word1 = program.stackPop();
@ -118,44 +133,35 @@ public class VM {
word1.sDiv(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case MOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.mod(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case SMOD:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.sMod(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case EXP:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.exp(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case NEG:{
DataWord word1 = program.stackPop();
word1.negate();
program.stackPush(word1);
program.step();
}
break;
} break;
case LT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
@ -167,10 +173,8 @@ public class VM {
}
program.stackPush(word1);
program.step();
}
break;
} break;
case SLT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
@ -182,9 +186,8 @@ public class VM {
}
program.stackPush(word1);
program.step();
}break;
} break;
case SGT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
@ -196,9 +199,8 @@ public class VM {
}
program.stackPush(word1);
program.step();
}break;
} break;
case GT:{
// todo: can be improved by not using BigInteger
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
@ -210,7 +212,7 @@ public class VM {
}
program.stackPush(word1);
program.step();
}break;
} break;
case EQ:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
@ -222,8 +224,7 @@ public class VM {
}
program.stackPush(word1);
program.step();
}
break;
} break;
case NOT: {
DataWord word1 = program.stackPop();
if (word1.isZero()){
@ -233,42 +234,35 @@ public class VM {
}
program.stackPush(word1);
program.step();
}
break;
} break;
/**
* Bitwise Logic Operations
*/
case AND:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.and(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case OR: {
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.or(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case XOR: {
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
word1.xor(word2);
program.stackPush(word1);
program.step();
}
break;
} break;
case BYTE:{
DataWord word1 = program.stackPop();
DataWord word2 = program.stackPop();
DataWord result = null;
if (word1.value().compareTo(_32_) == -1){
byte tmp = word2.getData()[word1.value().intValue()];
@ -280,13 +274,11 @@ public class VM {
}
program.stackPush(result);
program.step();
}
break;
} break;
/**
* SHA3
*/
case SHA3:{
DataWord memOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
@ -297,60 +289,50 @@ public class VM {
program.stackPush(word);
program.step();
}
break;
} break;
/**
* Environmental Information
*/
case ADDRESS:{
DataWord address = program.getOwnerAddress();
program.stackPush(address);
program.step();
}
break;
} break;
case BALANCE:{
DataWord balance = program.getBalance();
program.stackPush(balance);
program.step();
}
break;
} break;
case ORIGIN:{
DataWord originAddress = program.getOriginAddress();
program.stackPush(originAddress);
program.step();
}
break;
} break;
case CALLER:{
DataWord callerAddress = program.getCallerAddress();
program.stackPush(callerAddress);
program.step();
}
break;
} break;
case CALLVALUE:{
DataWord callValue = program.getCallValue();
program.stackPush(callValue);
program.step();
}
break;
} break;
case CALLDATALOAD:{
DataWord dataOffs = program.stackPop();
DataWord value = program.getDataValue(dataOffs);
program.stackPush(value);
program.step();
}
break;
} break;
case CALLDATASIZE:{
DataWord dataSize = program.getDataSize();
program.stackPush(dataSize);
program.step();
}
break;
} break;
case CALLDATACOPY:{
DataWord memOffsetData = program.stackPop();
DataWord dataOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
@ -358,15 +340,12 @@ public class VM {
byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
program.memorySave(memOffsetData.data, msgData);
program.step();
} break;
} break;
case CODESIZE:{
DataWord length = new DataWord(program.ops.length);
program.stackPush(length);
program.step();
}
break;
} break;
case CODECOPY:{
DataWord memOffsetData = program.stackPop();
DataWord codeOffsetData = program.stackPop();
@ -377,7 +356,6 @@ public class VM {
int memOffset = memOffsetData.value().intValue();
if (program.ops.length < length + codeOffset){
program.stop();
break;
}
@ -387,8 +365,7 @@ public class VM {
program.memorySave(memOffsetData.getData(), code);
program.step();
}
break;
} break;
case GASPRICE:
break;
@ -408,52 +385,43 @@ public class VM {
break;
case GASLIMIT:
break;
case POP:{
program.stackPop();
program.step();
}
break;
} break;
case DUP:{
DataWord word_1 = program.stackPop();
DataWord word_2 = word_1.clone();
program.stackPush(word_1);
program.stackPush(word_2);
program.step();
}
break;
} break;
case SWAP:{
DataWord word_1 = program.stackPop();
DataWord word_2 = program.stackPop();
program.stackPush(word_1);
program.stackPush(word_2);
program.step();
}
break;
} break;
case MLOAD:{
DataWord addr = program.stackPop();
DataWord data = program.memoryLoad(addr);
program.stackPush(data);
program.step();
}
break;
} break;
case MSTORE:{
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
program.memorySave(addr, value);
program.step();
}
break;
} break;
case MSTORE8:{
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
byte[] byteVal = {value.getData()[31]};
program.memorySave(addr.getData(), byteVal);
program.step();
}
break;
} break;
case SLOAD:{
DataWord key = program.stackPop();
DataWord val = program.storageLoad(key);
@ -462,8 +430,7 @@ public class VM {
}
program.stackPush(val);
program.step();
}
break;
} break;
case SSTORE:{
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
@ -477,15 +444,12 @@ public class VM {
program.spendGas(GasCost.SSTORE * 0);
} else
program.spendGas(GasCost.SSTORE);
program.step();
}
break;
} break;
case JUMP:{
DataWord pos = program.stackPop();
program.setPC(pos);
}
break;
} break;
case JUMPI:{
DataWord pos = program.stackPop();
DataWord cond = program.stackPop();
@ -495,23 +459,19 @@ public class VM {
} else{
program.step();
}
}
break;
} break;
case PC:{
int pc = program.getPC();
DataWord pcWord = new DataWord(pc);
program.stackPush(pcWord);
program.step();
}
break;
} break;
case MSIZE:{
int memSize = program.getMemSize();
DataWord wordMemSize = new DataWord(memSize);
program.stackPush(wordMemSize);
program.step();
}
break;
} break;
case GAS:
break;
@ -519,14 +479,12 @@ public class VM {
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
case PUSH17: case PUSH18: case PUSH19: case PUSH20: case PUSH21: case PUSH22: case PUSH23: case PUSH24:
case PUSH25: case PUSH26: case PUSH27: case PUSH28: case PUSH29: case PUSH30: case PUSH31: case PUSH32:{
program.step();
int nPush = op - PUSH1.val() + 1;
byte[] data = program.sweep(nPush);
program.stackPush(data);
}
break;
} break;
case CREATE:{
DataWord gas = program.stackPop();
DataWord inOffset = program.stackPop();
@ -535,8 +493,7 @@ public class VM {
// todo: implement contract creation
program.step();
}
break;
} break;
case CALL:{
DataWord gas = program.stackPop();
DataWord toAddress = program.stackPop();
@ -548,10 +505,8 @@ public class VM {
// todo: the contract for real
program.step();
}
break;
} break;
case RETURN:{
DataWord offset = program.stackPop();
DataWord size = program.stackPop();
@ -560,14 +515,12 @@ public class VM {
program.step();
program.stop();
}
break;
} break;
case SUICIDE:{
DataWord address = program.stackPop();
// todo: transfer left balance to the address
program.stop();
}
break;
} break;
default:{
}
@ -583,15 +536,12 @@ public class VM {
}
public void play(Program program){
public void play(Program program) {
try {
while(!program.isStopped())
this.step(program);
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
}
}
}

View File

@ -29,8 +29,8 @@ public class WalletTest {
wallet.importKey(catKey.getPrivKeyBytes());
AccountState cowAddressState = (AccountState) wallet.getAddressState(cowKey.getAddress());
AccountState catAddressState = (AccountState) wallet.getAddressState(catKey.getAddress());
AccountState cowAddressState = wallet.getAccountState(cowKey.getAddress());
AccountState catAddressState = wallet.getAccountState(catKey.getAddress());
cowAddressState.addToBalance(new BigInteger("234234"));
catAddressState.addToBalance(new BigInteger("84758"));