Merge pull request #25 from nicksavers/master

Add failing Block calc tests and update Denomination enum
This commit is contained in:
romanman 2014-06-07 18:01:39 +01:00
commit 90e7ab6a33
9 changed files with 120 additions and 99 deletions

View File

@ -22,7 +22,7 @@ import java.util.List;
*/
public class Block {
/* A scalar longValue equal to the mininum limit of gas expenditure per block */
/* A scalar value equal to the mininum limit of gas expenditure per block */
private static long MIN_GAS_LIMIT = BigInteger.valueOf(10).pow(4).longValue();
private BlockHeader header;
@ -38,7 +38,6 @@ public class Block {
private byte[] rlpEncoded;
private boolean parsed = false;
private byte[] hash;
private Trie txsState;
@ -64,9 +63,6 @@ public class Block {
this.parsed = true;
}
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
// difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
// extradata, nonce]
private void parseRLP() {
RLPList params = (RLPList) RLP.decode2(rlpEncoded);
@ -87,7 +83,6 @@ public class Block {
this.uncleList.add(blockData);
}
this.parsed = true;
this.hash = this.getHash();
}
public byte[] getHash(){
@ -145,6 +140,10 @@ public class Block {
return this.header.getMinGasPrice();
}
public boolean isGenesis() {
return this.header.getNumber() == 0;
}
public long getGasLimit() {
if (!parsed) parseRLP();
return this.header.getGasLimit();
@ -204,7 +203,7 @@ public class Block {
toStringBuff.setLength(0);
toStringBuff.append("BlockData [\n");
toStringBuff.append(" hash=" + ByteUtil.toHexString(hash)).append("\n");
toStringBuff.append(" hash=" + ByteUtil.toHexString(this.getHash())).append("\n");
toStringBuff.append(header.toString());
for (TransactionReceipt txReceipt : getTxReceiptList()) {
@ -221,7 +220,7 @@ public class Block {
toStringBuff.setLength(0);
toStringBuff.append("BlockData [");
toStringBuff.append(" hash=" + ByteUtil.toHexString(hash)).append("");
toStringBuff.append(" hash=" + ByteUtil.toHexString(this.getHash())).append("");
toStringBuff.append(header.toFlatString());
for (Transaction tx : getTransactionsList()){
@ -289,36 +288,40 @@ public class Block {
* likely next period. Conversely, if the period is too large, the difficulty,
* and expected time to the next block, is reduced.
*/
private boolean isValid() {
boolean isValid = false;
public boolean isValid() {
boolean isValid = true;
// verify difficulty meets requirements
isValid = this.getDifficulty() == this.calcDifficulty();
//isValid = this.getDifficulty() == this.calcDifficulty();
// verify gasLimit meets requirements
isValid = this.getGasLimit() == this.calcGasLimit();
//isValid = this.getGasLimit() == this.calcGasLimit();
// verify timestamp meets requirements
isValid = this.getTimestamp() > this.getParent().getTimestamp();
//isValid = this.getTimestamp() > this.getParent().getTimestamp();
return isValid;
}
/**
* Calculate GasLimit
* max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024)
*
* @return
* See Yellow Paper: http://www.gavwood.com/Paper.pdf - page 5, 4.3.4 (25)
* @return long value of the gasLimit
*/
public long calcGasLimit() {
if (this.header.getParentHash() == null)
return 1000000L;
if (this.isGenesis())
return Genesis.GAS_LIMIT;
else {
Block parent = this.getParent();
return Math.max(MIN_GAS_LIMIT, (parent.header.getGasLimit() * (1024 - 1) + (parent.header.getGasUsed() * 6 / 5)) / 1024);
}
}
/**
* Calculate Difficulty
* See Yellow Paper: http://www.gavwood.com/Paper.pdf - page 5, 4.3.4 (24)
* @return byte array value of the difficulty
*/
public byte[] calcDifficulty() {
if (this.header.getParentHash() == null)
if (this.isGenesis())
return Genesis.DIFFICULTY;
else {
Block parent = this.getParent();

View File

@ -20,19 +20,20 @@ public class Blockchain extends ArrayList<Block> {
private static Logger logger = LoggerFactory.getLogger(Blockchain.class);
// to avoid using minGasPrice=0 from Genesis for the wallet
private static long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
private Database db;
private Wallet wallet;
private long gasPrice = 1000;
private Block lastBlock;
// 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 = WorldManager.instance.chainDB;
this.wallet = wallet;
@ -83,27 +84,22 @@ public class Blockchain extends ArrayList<Block> {
}
private void addBlock(Block block) {
if(block.isValid()) {
this.wallet.processBlock(block);
// 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();
this.gasPrice = block.isGenesis() ? INITIAL_MIN_GAS_PRICE : block.getMinGasPrice();
if(lastBlock == null || block.getNumber() > lastBlock.getNumber())
this.lastBlock = block;
this.add(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

View File

@ -10,7 +10,9 @@ public enum Denomination {
SHANNON(newBigInt(9)),
SZABO(newBigInt(12)),
FINNY(newBigInt(15)),
ETHER(newBigInt(18));
ETHER(newBigInt(18)),
EINSTEIN(newBigInt(21)),
DOUGLAS(newBigInt(42));
private BigInteger amount;
@ -18,10 +20,13 @@ public enum Denomination {
this.amount = value;
}
public BigInteger getDenomination() {
public BigInteger value() {
return amount;
}
public long longValue() {return getDenomination().longValue();}
public long longValue() {
return value().longValue();
}
private static BigInteger newBigInt(int value) {
return BigInteger.valueOf(10).pow(value);

View File

@ -1,18 +1,36 @@
package org.ethereum.core;
import java.math.BigInteger;
import org.ethereum.crypto.HashUtil;
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;
/**
* The genesis block is the first block in the chain and has fixed values according to
* the protocol specification. The genesis block is 13 items, and is specified thus:
*
* ( zerohash_256 , SHA3 RLP () , zerohash_160 , stateRoot, 0, 2^22 , 0, 0, 1000000, 0, 0, 0, SHA3 (42) , (), () )
*
* - Where zerohash_256 refers to the parent hash, a 256-bit hash which is all zeroes;
* - zerohash_160 refers to the coinbase address, a 160-bit hash which is all zeroes;
* - 2^22 refers to the difficulty;
* - 0 refers to the timestamp (the Unix epoch);
* - the transaction trie root and extradata are both 0, being equivalent to the empty byte array.
* - The sequences of both uncles and transactions are empty and represented by ().
* - SHA3 (42) refers to the SHA3 hash of a byte array of length one whose first and only byte is of value 42.
* - SHA3 RLP () value refers to the hash of the uncle lists in RLP, both empty lists.
*
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (Appendix I. Genesis Block)
*/
public class Genesis extends Block {
Logger logger = LoggerFactory.getLogger(this.getClass());
// The proof-of-concept series include a development premine, making the state root hash
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
private AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200));
private String[] premine = new String[] {
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // # (M)
@ -53,8 +71,6 @@ public class Genesis extends Block {
}
logger.info("Genesis-hash: " + Hex.toHexString(this.getHash()));
logger.info("Genesis-stateRoot: " + Hex.toHexString(this.getStateRoot()));
WorldManager.instance.chainDB.put(getParentHash(), getEncoded());
}
public static Block getInstance() {

View File

@ -0,0 +1,23 @@
package org.ethereum.vm;
/**
* The fundamental network cost unit. Paid for exclusively by Ether, which is converted
* freely to and from Gas as required. Gas does not exist outside of the internal Ethereum
* computation engine; its price is set by the Transaction and miners are free to
* ignore Transactions whose Gas price is too low.
*/
public class GasCost {
public static int STEP = 1;
public static int STOP = 0;
public static int SUICIDE = 0;
public static int SLOAD = 20;
public static int SHA3 = 20;
public static int SSTORE = 100;
public static int BALANCE = 20;
public static int CREATE = 100;
public static int CALL = 20;
public static int MEMORY = 1;
public static int TXDATA = 5;
public static int TRANSACTION = 500;
}

View File

@ -1,41 +0,0 @@
package org.ethereum.vm;
import java.util.HashMap;
/**
* www.ethereumJ.com
* User: Roman Mandeleil
* Created on: 04/06/2014 23:58
*/
public class GasLedger {
/* YP Appendix B.
Gstep 1 Default amount of gas to pay for execution cycle.
Gstop 0 Nothing paid for the STOP operation.
Gsuicide 0 Nothing paid for the SUICIDE operation.
Gsha3 20 Paid for a SHA3 operation.
Gsload 20 Paid for a SLOAD operation.
Gsstore 100 Paid for a normal SSTORE operation (doubled or waived sometimes).
Gbalance 20 Paid for a BALANCE operation.
Gcreate 100 Paid for a CREATE operation.
Gcall 20 Paid for a CALL operation.
Gmemory 1 Paid for every additional word when expanding memory.
Gtxdata 5 Paid for every byte of data or code for a transaction.
Gtransaction 500 Paid for every transaction.
*/
public static int G_STEP = 1;
public static int G_STOP = 0;
public static int G_SUICIDE = 0;
public static int G_SLOAD = 20;
public static int G_SHA3 = 20;
public static int G_SSTORE = 100;
public static int G_BALANCE = 20;
public static int G_CREATE = 100;
public static int G_CALL = 20;
public static int G_MEMORY = 1;
public static int G_TXDATA = 5;
public static int G_TRANSACTION = 500;
}

View File

@ -34,23 +34,23 @@ public class VM {
switch (OpCode.code(op)) {
case SHA3:
program.spendGas( GasLedger.G_SHA3 );
program.spendGas(GasCost.SHA3);
break;
case SLOAD:
program.spendGas( GasLedger.G_SLOAD );
program.spendGas(GasCost.SLOAD);
break;
case SSTORE:
// todo: calc gas in the execution
// todo: according to the size
break;
case BALANCE:
program.spendGas( GasLedger.G_BALANCE );
program.spendGas(GasCost.BALANCE);
break;
case CREATE:
program.spendGas( GasLedger.G_CREATE );
program.spendGas(GasCost.CREATE);
break;
case CALL:
program.spendGas( GasLedger.G_CALL );
program.spendGas(GasCost.CALL);
break;
case MSTORE8:
case MSTORE:
@ -58,7 +58,7 @@ public class VM {
// todo: according to the size
break;
default:
program.spendGas( GasLedger.G_STEP );
program.spendGas(GasCost.STEP);
break;
}
@ -472,11 +472,11 @@ public class VM {
DataWord oldValue = program.storageLoad(addr);
program.storageSave(addr, value);
if (oldValue == null && !value.isZero()){
program.spendGas(GasLedger.G_SSTORE * 2);
program.spendGas(GasCost.SSTORE * 2);
} else if (oldValue != null && value.isZero()){
program.spendGas(GasLedger.G_SSTORE * 0);
program.spendGas(GasCost.SSTORE * 0);
} else
program.spendGas(GasLedger.G_SSTORE);
program.spendGas(GasCost.SSTORE);
program.step();
}
@ -573,7 +573,7 @@ public class VM {
// memory gas calc
int newMemSize = program.getMemSize();
program.spendGas(GasLedger.G_MEMORY * (newMemSize - oldMemSize) /32);
program.spendGas(GasCost.MEMORY * (newMemSize - oldMemSize) /32);
}
program.fullTrace();
} catch (RuntimeException e) {

View File

@ -1,5 +1,7 @@
package org.ethereum.core;
import java.math.BigInteger;
import org.ethereum.net.message.BlocksMessage;
import org.ethereum.net.message.StaticMessages;
import org.ethereum.util.RLPList;
@ -86,4 +88,21 @@ public class BlockTest {
BlocksMessage blockData = new BlocksMessage(rlpList);
System.out.println(blockData.toString());
}
@Test
public void testCalcDifficulty() {
Block genesis = Genesis.getInstance();
byte[] diffBytes = genesis.calcDifficulty();
BigInteger difficulty = new BigInteger(1, diffBytes);
System.out.println("Genesis difficulty = " + difficulty.toString());
fail("Yet to be implemented.");
}
@Test
public void testCalcGasLimit() {
Block genesis = Genesis.getInstance();
long gasLimit = genesis.calcGasLimit();
System.out.println("Genesis gasLimit = " + gasLimit);
fail("Yet to be implemented.");
}
}