new shiny wallet design

This commit is contained in:
romanman 2014-09-07 21:22:43 +03:00
parent c738f750b7
commit 979763106d
13 changed files with 243 additions and 70 deletions

View File

@ -1,6 +1,7 @@
package org.ethereum.core;
import java.math.BigInteger;
import java.util.*;
import org.ethereum.crypto.ECKey;
import org.ethereum.manager.WorldManager;
@ -13,7 +14,10 @@ public class Account {
private ECKey ecKey;
private byte[] address;
private Set<Transaction> pendingTransactions =
Collections.synchronizedSet(new HashSet<Transaction>());
public Account() {
this.ecKey = new ECKey(Utils.getRandom());
address = this.ecKey.getAddress();
@ -24,7 +28,6 @@ public class Account {
address = this.ecKey.getAddress();
}
public ECKey getEcKey() {
return ecKey;
}
@ -49,10 +52,29 @@ public class Account {
AccountState accountState =
WorldManager.getInstance().getRepository().getAccountState(this.address);
if (accountState != null)
return accountState.getBalance();
BigInteger balance = BigInteger.ZERO;
return null;
if (accountState != null)
balance = accountState.getBalance();
synchronized (pendingTransactions){
if (!pendingTransactions.isEmpty()){
for (Transaction tx : pendingTransactions){
if (Arrays.equals(this.address, tx.getSender())){
balance = balance.subtract(new BigInteger(1, tx.getValue()));
}
if (Arrays.equals(this.address, tx.getReceiveAddress())){
balance = balance.add(new BigInteger(1, tx.getValue()));
}
}
// todo: calculate the fee for pending
}
}
return balance;
}
@ -61,9 +83,37 @@ public class Account {
AccountState accountState =
WorldManager.getInstance().getRepository().getAccountState(this.address);
if (accountState != null)
return accountState.getNonce();
BigInteger nonce = BigInteger.ZERO;
return null;
if (accountState != null)
nonce = accountState.getNonce();
synchronized (pendingTransactions){
if (!pendingTransactions.isEmpty()){
for (Transaction tx : pendingTransactions){
if (Arrays.equals(this.address, tx.getSender())){
nonce = nonce.add(BigInteger.ONE);
}
}
}
}
return nonce;
}
public void addPendingTransaction(Transaction transaction){
synchronized (pendingTransactions){
pendingTransactions.add(transaction);
}
}
public void clearAllPendingTransactions(){
synchronized (pendingTransactions){
pendingTransactions.clear();
}
}
}

View File

@ -95,9 +95,6 @@ public class Blockchain implements org.ethereum.facade.Blockchain{
if (block == null)
return;
if (block.getNumber() == 12390)
logger.debug("Block #12390");
// if it is the first block to add
// make sure the parent is genesis

View File

@ -286,4 +286,26 @@ public class Transaction {
receiveAddress, value, data, v, r, s);
return rlpEncoded;
}
@Override
public int hashCode() {
byte[] hash = this.getHash();
int hashCode = 0;
for (int i = 0; i < hash.length; ++i){
hashCode += hash[i] * i;
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Transaction)) return false;
Transaction tx = (Transaction)obj;
return tx.hashCode() == this.hashCode();
}
}

View File

@ -92,7 +92,24 @@ public class Wallet {
}
return sum;
}
/**
* The wallet will call this method once transaction been send to the network,
* once the the GET_TRANSACTION will be answered with that particular transaction
* it will be considered as received by the net
*
* @param transaction
* @return
*/
public WalletTransaction addByWalletTransaction(Transaction transaction){
String hash = Hex.toHexString(transaction.getHash());
WalletTransaction walletTransaction = new WalletTransaction(transaction);
this.walletTransactions.put(hash, walletTransaction);
return walletTransaction;
}
/***********************************************************************
* 1) the dialog put a pending transaction on the list
* 2) the dialog send the transaction to a net
@ -111,6 +128,9 @@ public class Wallet {
walletTransaction = new WalletTransaction(transaction);
this.walletTransactions.put(hash, walletTransaction);
}
this.applyTransaction(transaction);
return walletTransaction;
}
@ -141,16 +161,23 @@ public class Wallet {
byte[] senderAddress = transaction.getSender();
Account sender = rows.get(Hex.toHexString(senderAddress));
if (sender != null) {
sender.addPendingTransaction(transaction);
BigInteger value = new BigInteger(-1, transaction.getValue());
// sender.addToBalance(value);
// sender.incrementNonce();
logger.info("Pending transaction added to " +
"\n account: [ {} ], " +
"\n tx: [ {} ]",
Hex.toHexString(sender.getAddress()), Hex.toHexString(transaction.getHash()));
}
byte[] receiveAddress = transaction.getReceiveAddress();
Account receiver = rows.get(Hex.toHexString(receiveAddress));
if (receiver != null) {
// receiver.addToBalance(new BigInteger(1, transaction.getValue()));
receiver.addPendingTransaction(transaction);
logger.info("Pending transaction added to " +
"\n account: [ {} ], " +
"\n tx: [ {} ]",
Hex.toHexString(receiver.getAddress()), Hex.toHexString(transaction.getHash()));
}
this.notifyListeners();
}
@ -158,24 +185,11 @@ public class Wallet {
public void processBlock(Block block) {
if (block == null) return;
// TODO: proceed coinbase when you are the miner that gets an award
boolean walletUpdated = false;
List<Transaction> transactions = block.getTransactionsList();
for (Transaction tx : transactions) {
boolean txExist = transactionMap.get(new ByteArrayWrapper(tx.getHash())) != null;
if (txExist) break;
else {
this.applyTransaction(tx);
walletUpdated = true;
}
for (Account account : getAccountCollection()){
account.clearAllPendingTransactions();
}
this.high = block.getNumber();
if (walletUpdated) notifyListeners();
notifyListeners();
}
/**

View File

@ -478,15 +478,28 @@ public class Repository {
}
public boolean isClosed(){
return chainDB == null;
}
public void close() {
if (this.chainDB != null)
chainDB.close();
if (this.stateDB != null)
if (this.chainDB != null){
chainDB.close();
chainDB = null;
}
if (this.stateDB != null){
stateDB.close();
if (this.detailsDB != null)
stateDB = null;
}
if (this.detailsDB != null){
detailsDB.close();
detailsDB = null;
}
}
private void validateAddress(byte[] addr) {
@ -496,3 +509,4 @@ public class Repository {
}
}
}

View File

@ -159,4 +159,8 @@ public class EthereumImpl implements Ethereum {
public boolean isConnected() {
return WorldManager.getInstance().getActivePeer() != null;
}
// public Future<Transaction> submitTransaction() -- wait for approve (like in wallet dialog)
}

View File

@ -29,7 +29,7 @@ import org.ethereum.net.peerdiscovery.PeerDiscovery;
public class WorldManager {
private Blockchain blockchain;
private final Repository repository;
private Repository repository;
private Wallet wallet;
private PeerDiscovery peerDiscovery;
@ -59,6 +59,13 @@ public class WorldManager {
}
// used for testing
public void reset(){
this.repository = new Repository();
this.blockchain = new Blockchain(repository);
}
public void init(){
this.wallet = new Wallet();

View File

@ -89,7 +89,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
public void run() {
sendGetTransactions();
}
}, 2000, 30000);
}, 2000, 10000);
chainAskTimer.scheduleAtFixedRate(new TimerTask() {
@ -198,8 +198,9 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
List<Transaction> txList = transactionsMessage.getTransactions();
for(Transaction tx : txList)
WorldManager.getInstance().getBlockchain()
.applyTransaction(null, tx);
// WorldManager.getInstance().getBlockchain()
// .applyTransaction(null, tx);
WorldManager.getInstance().getWallet().addTransaction(tx);
logger.info(transactionsMessage.toString());
if (peerListener != null) peerListener.console(transactionsMessage.toString());

View File

@ -33,7 +33,7 @@ public class TransactionTask implements Callable<Transaction> {
ClientPeer peer = WorldManager.getInstance().getActivePeer();
WalletTransaction walletTransaction = WorldManager.getInstance().getWallet().addTransaction(tx);
WalletTransaction walletTransaction = WorldManager.getInstance().getWallet().addByWalletTransaction(tx);
peer.sendTransaction(tx);
while(walletTransaction.getApproved() < 1) {

View File

@ -3,15 +3,25 @@ package org.ethereum.core;
import java.math.BigInteger;
import org.ethereum.manager.WorldManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.spongycastle.util.encoders.Hex;
import org.ethereum.core.Block;
import org.ethereum.core.Genesis;
import org.junit.Test;
import static org.junit.Assert.*;
public class BlockTest {
@BeforeClass
public static void setUp() {
if (WorldManager.getInstance().getRepository().isClosed())
WorldManager.getInstance().reset();
}
@AfterClass
public static void tearDown() {
WorldManager.getInstance().close();
}
// https://ethereum.etherpad.mozilla.org/12
private String CPP_PoC5_GENESIS_HEX_RLP_ENCODED = "f8abf8a7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a08dbd704eb38d1c2b73ee4788715ea5828a030650829703f077729b2b613dd20680834000008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
@ -108,24 +118,21 @@ public class BlockTest {
@Test
public void testCalcDifficulty() {
try {
Block genesis = Genesis.getInstance();
BigInteger difficulty = new BigInteger(1, genesis.calcDifficulty());
System.out.println("Genesis difficulty = " + difficulty.toString());
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
// Storing genesis because the parent needs to be in the DB for calculation.
WorldManager.getInstance().getBlockchain().add(genesis);
Block genesis = Genesis.getInstance();
BigInteger difficulty = new BigInteger(1, genesis.calcDifficulty());
System.out.println("Genesis difficulty = " + difficulty.toString());
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
Block block1 = new Block(Hex.decode(block_1));
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
BigInteger actualDifficulty = new BigInteger(1, block1.getDifficulty());
System.out.println("Block#1 actual difficulty = " + actualDifficulty.toString());
System.out.println("Block#1 calculated difficulty = " + calcDifficulty.toString());
assertEquals(actualDifficulty, calcDifficulty);
} finally {
WorldManager.getInstance().close();
}
// Storing genesis because the parent needs to be in the DB for calculation.
WorldManager.getInstance().getBlockchain().add(genesis);
Block block1 = new Block(Hex.decode(block_1));
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
BigInteger actualDifficulty = new BigInteger(1, block1.getDifficulty());
System.out.println("Block#1 actual difficulty = " + actualDifficulty.toString());
System.out.println("Block#1 calculated difficulty = " + calcDifficulty.toString());
assertEquals(actualDifficulty, calcDifficulty);
}
@Test

View File

@ -2,8 +2,11 @@ package org.ethereum.core;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.junit.Ignore;
import org.junit.Test;
import org.ethereum.db.Repository;
import org.ethereum.manager.WorldManager;
import org.junit.*;
import org.junit.runners.MethodSorters;
import org.spongycastle.util.encoders.Hex;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
@ -17,8 +20,70 @@ import java.math.BigInteger;
* @author: Roman Mandeleil
* Created on: 17/05/14 17:06
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class WalletTest {
@BeforeClass
public static void setUp() {
if (WorldManager.getInstance().getRepository().isClosed())
WorldManager.getInstance().reset();
}
@AfterClass
public static void tearDown() {
WorldManager.getInstance().close();
}
@Test // Testing account for simple balance set
public void accountTest_1(){
Repository repository = WorldManager.getInstance().getRepository();
ECKey cowKey = ECKey.fromPrivate(HashUtil.sha3("cow".getBytes()));
repository.createAccount(cowKey.getAddress());
repository.addBalance(cowKey.getAddress(), BigInteger.TEN);
Wallet wallet = new Wallet();
wallet.importKey(cowKey.getPrivKeyBytes());
BigInteger walletBalance = wallet.getBalance(cowKey.getAddress());
Assert.assertEquals(BigInteger.TEN, walletBalance);
}
@Test // test account balance with pending "unblocked" transaction
public void accountTest_2(){
Repository repository = WorldManager.getInstance().getRepository();
ECKey cowKey = ECKey.fromPrivate(HashUtil.sha3("cow".getBytes()));
repository.createAccount(cowKey.getAddress());
repository.addBalance(cowKey.getAddress(), BigInteger.TEN);
Wallet wallet = new Wallet();
wallet.importKey(cowKey.getPrivKeyBytes());
Transaction tx = new Transaction(
new byte[]{},
Hex.decode("09184E72A000"),
Hex.decode("03E8"),
cowKey.getAddress(),
Hex.decode("0A"),
new byte[]{}
);
ECKey catKey = ECKey.fromPrivate(HashUtil.sha3("cat".getBytes()));
tx.sign(catKey.getPrivKeyBytes());
wallet.applyTransaction(tx);
BigInteger walletBalance = wallet.getBalance(cowKey.getAddress());
Assert.assertEquals(BigInteger.valueOf(20), walletBalance);
}
@Test
public void testSave1() throws TransformerException, ParserConfigurationException {
@ -29,13 +94,6 @@ public class WalletTest {
wallet.importKey(cowKey.getPrivKeyBytes());
wallet.importKey(catKey.getPrivKeyBytes());
AccountState cowAddressState = wallet.getAccountState(cowKey.getAddress());
AccountState catAddressState = wallet.getAccountState(catKey.getAddress());
cowAddressState.addToBalance(new BigInteger("234234"));
catAddressState.addToBalance(new BigInteger("84758"));
wallet.setHigh(4354);
wallet.save();

View File

@ -58,7 +58,6 @@ public class DialogWorker extends SwingWorker<Transaction, Object> {
}
dialog.infoStatusMsg("Transaction got approved");
WorldManager.getInstance().getWallet().applyTransaction(tx);
return null;
}
}