Refactor Account inherits from AccountState

This commit is contained in:
nicksavers 2014-06-27 18:14:12 +02:00
parent f7f2f88946
commit 51292f8b20
8 changed files with 72 additions and 118 deletions

View File

@ -8,25 +8,22 @@ import org.ethereum.util.Utils;
/** /**
* Representation of an actual account or contract * Representation of an actual account or contract
*/ */
public class Account { public class Account extends AccountState {
private ECKey ecKey; private ECKey ecKey;
private byte[] address; private byte[] address;
private AccountState state;
public Account() { public Account() {
this.ecKey = new ECKey(Utils.getRandom()); this.ecKey = new ECKey(Utils.getRandom());
this.state = new AccountState();
} }
public Account(ECKey ecKey) { public Account(ECKey ecKey) {
this.ecKey = ecKey; this.ecKey = ecKey;
this.state = new AccountState();
} }
public Account(ECKey ecKey, BigInteger nonce, BigInteger balance) { public Account(ECKey ecKey, BigInteger nonce, BigInteger balance) {
super(nonce, balance);
this.ecKey = ecKey; this.ecKey = ecKey;
this.state = new AccountState(nonce, balance);
} }
public ECKey getEcKey() { public ECKey getEcKey() {
@ -40,12 +37,4 @@ public class Account {
public void setAddress(byte[] address) { public void setAddress(byte[] address) {
this.address = address; this.address = address;
} }
public AccountState getState() {
return state;
}
public void setState(AccountState state) {
this.state = state;
}
} }

View File

@ -64,18 +64,18 @@ public class Wallet {
public AccountState getAccountState(byte[] addressBytes){ public AccountState getAccountState(byte[] addressBytes){
String address = Hex.toHexString(addressBytes); String address = Hex.toHexString(addressBytes);
return rows.get(address).getState(); return rows.get(address);
} }
public BigInteger getBalance(byte[] addressBytes){ public BigInteger getBalance(byte[] addressBytes){
String address = Hex.toHexString(addressBytes); String address = Hex.toHexString(addressBytes);
return rows.get(address).getState().getBalance(); return rows.get(address).getBalance();
} }
public BigInteger totalBalance(){ public BigInteger totalBalance(){
BigInteger sum = BigInteger.ZERO; BigInteger sum = BigInteger.ZERO;
for (Account account : rows.values()){ for (Account account : rows.values()){
sum = sum.add(account.getState().getBalance()); sum = sum.add(account.getBalance());
} }
return sum; return sum;
} }
@ -89,17 +89,16 @@ public class Wallet {
if (sender != null){ if (sender != null){
BigInteger value = new BigInteger(-1, transaction.getValue()); BigInteger value = new BigInteger(-1, transaction.getValue());
sender.getState().addToBalance(value); sender.addToBalance(value);
sender.getState().incrementNonce(); sender.incrementNonce();
} }
byte[] receiveAddress = transaction.getReceiveAddress(); byte[] receiveAddress = transaction.getReceiveAddress();
Account receiver = rows.get(Hex.toHexString(receiveAddress)); Account receiver = rows.get(Hex.toHexString(receiveAddress));
if (receiver != null){ if (receiver != null){
receiver.getState().addToBalance(new BigInteger(1, transaction.getValue())); receiver.addToBalance(new BigInteger(1, transaction.getValue()));
} }
this.notifyListeners();
notifyListeners();
} }
@ -232,7 +231,7 @@ public class Wallet {
privKey.setTextContent(Hex.toHexString(account.getEcKey().getPrivKeyBytes())); privKey.setTextContent(Hex.toHexString(account.getEcKey().getPrivKeyBytes()));
Element value = doc.createElement("value"); Element value = doc.createElement("value");
value.setTextContent(account.getState().getBalance().toString()); value.setTextContent(account.getBalance().toString());
raw.appendChild(addressE); raw.appendChild(addressE);
raw.appendChild(privKey); raw.appendChild(privKey);

View File

@ -4,10 +4,8 @@ import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.ethereum.config.SystemProperties; import org.ethereum.config.SystemProperties;
@ -16,7 +14,6 @@ import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options; import org.iq80.leveldb.Options;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
/** /**
* Generic interface for Ethereum database * Generic interface for Ethereum database
@ -26,13 +23,12 @@ import org.spongycastle.util.encoders.Hex;
* Pure Java: https://github.com/dain/leveldb * Pure Java: https://github.com/dain/leveldb
* JNI binding: https://github.com/fusesource/leveldbjni * JNI binding: https://github.com/fusesource/leveldbjni
*/ */
public class DatabaseImpl implements Database{ public class DatabaseImpl implements Database {
private static Logger logger = LoggerFactory.getLogger(DatabaseImpl.class); private static Logger logger = LoggerFactory.getLogger(DatabaseImpl.class);
private DB db; private DB db;
private String name; private String name;
public DatabaseImpl(String name) { public DatabaseImpl(String name) {
// Initialize Database // Initialize Database
this.name = name; this.name = name;
@ -98,20 +94,15 @@ public class DatabaseImpl implements Database{
} }
} }
public List<ByteArrayWrapper> dumpKeys() {
public ArrayList<ByteArrayWrapper> dumpKeys(){
DBIterator iterator = getDb().iterator(); DBIterator iterator = getDb().iterator();
ArrayList<ByteArrayWrapper> keys = new ArrayList<>(); ArrayList<ByteArrayWrapper> keys = new ArrayList<ByteArrayWrapper>();
while(iterator.hasNext()){
while (iterator.hasNext()) {
ByteArrayWrapper key = new ByteArrayWrapper(iterator.next().getKey()); ByteArrayWrapper key = new ByteArrayWrapper(iterator.next().getKey());
keys.add(key); keys.add(key);
} }
Collections.sort((List<ByteArrayWrapper>) keys);
Collections.sort((List)keys);
return keys; return keys;
} }
} }

View File

@ -16,7 +16,7 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.ethereum.config.SystemProperties.CONFIG; import static org.ethereum.config.SystemProperties.CONFIG;
@ -25,7 +25,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
* *
* *
*********************************************************************************** ***********************************************************************************
MainRepository Repository
| |
--> AccountState ---> Trie ---> leveldb (state) /key=address --> AccountState ---> Trie ---> leveldb (state) /key=address
--> nonce --> nonce
@ -47,7 +47,7 @@ public class Repository {
private Logger logger = LoggerFactory.getLogger("repository"); private Logger logger = LoggerFactory.getLogger("repository");
Trie worldState; public Trie worldState;
TrackTrie accountStateDB; TrackTrie accountStateDB;
TrackDatabase contractDetailsDB; TrackDatabase contractDetailsDB;
@ -55,19 +55,15 @@ public class Repository {
// todo: Listeners listeners // todo: Listeners listeners
// todo: cash impl // todo: cash impl
DatabaseImpl detailsDB = null; private DatabaseImpl detailsDB = null;
DatabaseImpl stateDB = null; private DatabaseImpl stateDB = null;
public Repository() { public Repository() {
detailsDB = new DatabaseImpl("details"); detailsDB = new DatabaseImpl("details");
contractDetailsDB = new TrackDatabase(detailsDB); contractDetailsDB = new TrackDatabase(detailsDB);
stateDB = new DatabaseImpl("state"); stateDB = new DatabaseImpl("state");
worldState = new Trie(stateDB.getDb()); worldState = new Trie(stateDB.getDb());
accountStateDB = new TrackTrie(worldState); accountStateDB = new TrackTrie(worldState);
} }
@ -77,33 +73,30 @@ public class Repository {
} }
public Repository getTrack(){ public Repository getTrack(){
TrackTrie trackState = new TrackTrie(accountStateDB); TrackTrie trackState = new TrackTrie(accountStateDB);
TrackDatabase trackDetails = new TrackDatabase(contractDetailsDB); TrackDatabase trackDetails = new TrackDatabase(contractDetailsDB);
return new Repository (trackState, trackDetails); return new Repository (trackState, trackDetails);
} }
public void startTracking(){ public void startTracking() {
logger.info("start tracking"); logger.info("start tracking");
accountStateDB.startTrack(); accountStateDB.startTrack();
contractDetailsDB.startTrack(); contractDetailsDB.startTrack();
} }
public void commit(){ public void commit() {
logger.info("commit changes"); logger.info("commit changes");
accountStateDB.commitTrack(); accountStateDB.commitTrack();
contractDetailsDB.commitTrack(); contractDetailsDB.commitTrack();
} }
public void rollback(){ public void rollback() {
logger.info("rollback changes"); logger.info("rollback changes");
accountStateDB.rollbackTrack(); accountStateDB.rollbackTrack();
contractDetailsDB.rollbackTrack(); contractDetailsDB.rollbackTrack();
} }
public AccountState createAccount(byte[] addr){ public AccountState createAccount(byte[] addr) {
// 1. Save AccountState // 1. Save AccountState
AccountState state = new AccountState(); AccountState state = new AccountState();
@ -116,44 +109,40 @@ public class Repository {
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
logger.info("New account created: [ {} ]", Hex.toHexString(addr)); logger.info("New account created: [ {} ]", Hex.toHexString(addr));
return state; return state;
} }
public AccountState getAccountState(byte[] addr) {
public AccountState getAccountState(byte[] addr){
byte[] accountStateRLP = accountStateDB.get(addr); byte[] accountStateRLP = accountStateDB.get(addr);
if (accountStateRLP.length == 0){
if (accountStateRLP.length == 0){
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
logger.info("No account: [ {} ]", Hex.toHexString(addr)); logger.info("No account: [ {} ]", Hex.toHexString(addr));
return null; return null;
} }
AccountState state = new AccountState(accountStateRLP); AccountState state = new AccountState(accountStateRLP);
return state; return state;
} }
public ContractDetails getContractDetails(byte[] addr){ public ContractDetails getContractDetails(byte[] addr) {
byte[] accountDetailsRLP = contractDetailsDB.get(addr); byte[] accountDetailsRLP = contractDetailsDB.get(addr);
if (accountDetailsRLP == null){ if (accountDetailsRLP == null) {
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
logger.info("No account: [ {} ]", Hex.toHexString(addr)); logger.info("No account: [ {} ]", Hex.toHexString(addr));
return null; return null;
} }
ContractDetails details = new ContractDetails(accountDetailsRLP); ContractDetails details = new ContractDetails(accountDetailsRLP);
return details; return details;
} }
public BigInteger addBalance(byte[] address, BigInteger value){ public BigInteger addBalance(byte[] address, BigInteger value) {
AccountState state = getAccountState(address); AccountState state = getAccountState(address);
if (state == null) return BigInteger.ZERO; if (state == null)
return BigInteger.ZERO;
BigInteger newBalance = state.addToBalance(value); BigInteger newBalance = state.addToBalance(value);
@ -162,32 +151,25 @@ public class Repository {
Hex.toHexString(address), newBalance.toString()); Hex.toHexString(address), newBalance.toString());
accountStateDB.update(address, state.getEncoded()); accountStateDB.update(address, state.getEncoded());
return newBalance; return newBalance;
} }
public BigInteger getBalance(byte[] address){ public BigInteger getBalance(byte[] address){
AccountState state = getAccountState(address); AccountState state = getAccountState(address);
if (state == null) return BigInteger.ZERO; if (state == null) return BigInteger.ZERO;
return state.getBalance(); return state.getBalance();
} }
public BigInteger getNonce(byte[] address){ public BigInteger getNonce(byte[] address){
AccountState state = getAccountState(address); AccountState state = getAccountState(address);
if (state == null) return BigInteger.ZERO; if (state == null) return BigInteger.ZERO;
return state.getNonce(); return state.getNonce();
} }
public BigInteger increaseNonce(byte[] address){ public BigInteger increaseNonce(byte[] address){
AccountState state = getAccountState(address); AccountState state = getAccountState(address);
if (state == null) return BigInteger.ZERO; if (state == null) return BigInteger.ZERO;
state.incrementNonce(); state.incrementNonce();
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
@ -234,14 +216,12 @@ public class Repository {
} }
public byte[] getCode(byte[] address){ public byte[] getCode(byte[] address){
ContractDetails details = getContractDetails(address); ContractDetails details = getContractDetails(address);
if (details == null) return null; if (details == null) return null;
return details.getCode(); return details.getCode();
} }
public void saveCode(byte[] address, byte[] code){ public void saveCode(byte[] address, byte[] code) {
if (code == null) return; if (code == null) return;
@ -264,26 +244,23 @@ public class Repository {
contractDetailsDB.put(address, details.getEncoded()); contractDetailsDB.put(address, details.getEncoded());
} }
public byte[] getRootHash(){ public byte[] getRootHash() {
return this.worldState.getRootHash(); return this.worldState.getRootHash();
} }
public void close() {
public void close(){
if (this.stateDB != null) if (this.stateDB != null)
stateDB.close(); stateDB.close();
if (this.detailsDB != null) if (this.detailsDB != null)
detailsDB.close(); detailsDB.close();
} }
public void dumpState(long blockNumber, int txNumber, String txHash){ public void dumpState(long blockNumber, int txNumber, String txHash) {
if (!CONFIG.dumpFull()) return; if (!CONFIG.dumpFull()) return;
if (txHash == null) if (txHash == null)
if (CONFIG.dumpCleanOnRestart()){ if (CONFIG.dumpCleanOnRestart()) {
try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {} try {FileUtils.deleteDirectory(CONFIG.dumpDir());} catch (IOException e) {}
} }
@ -291,8 +268,7 @@ public class Repository {
String fileName = "0.dmp"; String fileName = "0.dmp";
if (txHash != null) if (txHash != null)
fileName = fileName = String.format("%d_%d_%s.dmp",
String.format("%d_%d_%s.dmp",
blockNumber, txNumber, txHash.substring(0, 8)); blockNumber, txNumber, txHash.substring(0, 8));
File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName); File dumpFile = new File(System.getProperty("user.dir") + "/" + dir + fileName);
@ -304,7 +280,7 @@ public class Repository {
FileWriter fw = new FileWriter(dumpFile.getAbsoluteFile()); FileWriter fw = new FileWriter(dumpFile.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw); BufferedWriter bw = new BufferedWriter(fw);
ArrayList<ByteArrayWrapper> keys = this.detailsDB.dumpKeys(); List<ByteArrayWrapper> keys = this.detailsDB.dumpKeys();
// dump json file // dump json file
for (ByteArrayWrapper key : keys){ for (ByteArrayWrapper key : keys){

View File

@ -397,7 +397,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount(); Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes(); byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = account.getState().getNonce() == BigInteger.ZERO ? null : account.getState().getNonce().toByteArray(); byte[] nonce = account.getNonce() == BigInteger.ZERO ? null : account.getNonce().toByteArray();
byte[] gasPrice = new BigInteger("10000000000000").toByteArray(); byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
BigInteger gasBI = new BigInteger(gasInput.getText()); BigInteger gasBI = new BigInteger(gasInput.getText());
@ -444,7 +444,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog{
@Override @Override
public String toString() { public String toString() {
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress()); String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(account.getState().getBalance()); String valueShort = Utils.getValueShortString(account.getBalance());
String result = String.format(" By: [%s] %s", addressShort, String result = String.format(" By: [%s] %s", addressShort,
valueShort); valueShort);
return result; return result;

View File

@ -292,7 +292,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount(); Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes(); byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = account.getState().getNonce() == BigInteger.ZERO ? null : account.getState().getNonce().toByteArray(); byte[] nonce = account.getNonce() == BigInteger.ZERO ? null : account.getNonce().toByteArray();
byte[] gasPrice = new BigInteger("10000000000000").toByteArray(); byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
BigInteger gasBI = new BigInteger(gasInput.getText()); BigInteger gasBI = new BigInteger(gasInput.getText());
@ -313,7 +313,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
private boolean validInput() { private boolean validInput() {
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount(); Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
BigInteger currentBalance = account.getState().getBalance(); BigInteger currentBalance = account.getBalance();
BigInteger gasPrice = BigInteger.valueOf(WorldManager.instance.getBlockChain().getGasPrice()); BigInteger gasPrice = BigInteger.valueOf(WorldManager.instance.getBlockChain().getGasPrice());
BigInteger gasInput = new BigInteger( this.gasInput.getText()); BigInteger gasInput = new BigInteger( this.gasInput.getText());
@ -350,7 +350,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
@Override @Override
public String toString() { public String toString() {
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress()); String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
String valueShort = Utils.getValueShortString(account.getState().getBalance()); String valueShort = Utils.getValueShortString(account.getBalance());
String result = String result =
String.format(" By: [%s] %s", addressShort, valueShort); String.format(" By: [%s] %s", addressShort, valueShort);

View File

@ -30,7 +30,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
private PayOutDialog dialog; private PayOutDialog dialog;
private AccountState addressState = null; private AccountState accountState = null;
private JLabel statusMsg = null; private JLabel statusMsg = null;
final JTextField receiverInput; final JTextField receiverInput;
@ -41,7 +41,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
super(parent, "Payout details: ", false); super(parent, "Payout details: ", false);
dialog = this; dialog = this;
this.addressState = account.getState(); this.accountState = account;
receiverInput = new JTextField(18); receiverInput = new JTextField(18);
GUIUtils.addStyle(receiverInput, "Pay to:"); GUIUtils.addStyle(receiverInput, "Pay to:");
@ -117,7 +117,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
} }
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes(); byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray(); byte[] nonce = accountState.getNonce() == BigInteger.ZERO ? null : accountState.getNonce().toByteArray();
byte[] gasPrice = BigInteger.valueOf( WorldManager.instance.getBlockChain().getGasPrice()).toByteArray(); byte[] gasPrice = BigInteger.valueOf( WorldManager.instance.getBlockChain().getGasPrice()).toByteArray();
@ -195,7 +195,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
BigInteger ammountValue = new BigInteger(amountText); BigInteger ammountValue = new BigInteger(amountText);
BigInteger feeValue = new BigInteger(feeText); BigInteger feeValue = new BigInteger(feeText);
BigInteger gasPrice = BigInteger.valueOf(WorldManager.instance.getBlockChain().getGasPrice()); BigInteger gasPrice = BigInteger.valueOf(WorldManager.instance.getBlockChain().getGasPrice());
BigInteger currentBalance = addressState.getBalance(); BigInteger currentBalance = accountState.getBalance();
boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1; boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1;

View File

@ -1,7 +1,6 @@
package org.ethereum.gui; package org.ethereum.gui;
import org.ethereum.core.Account; import org.ethereum.core.Account;
import org.ethereum.core.AccountState;
import org.ethereum.util.Utils; import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -47,7 +46,7 @@ public class WalletAddressPanel extends JPanel{
amount.setBorder(border); amount.setBorder(border);
amount.setEnabled(true); amount.setEnabled(true);
amount.setEditable(false); amount.setEditable(false);
amount.setText(Utils.getValueShortString(account.getState().getBalance())); amount.setText(Utils.getValueShortString(account.getBalance()));
amount.setForeground(new Color(143, 170, 220)); amount.setForeground(new Color(143, 170, 220));
amount.setBackground(Color.WHITE); amount.setBackground(Color.WHITE);
amount.setPreferredSize(new Dimension(100, 35)); amount.setPreferredSize(new Dimension(100, 35));