Merge pull request #25 from nicksavers/master
Add failing Block calc tests and update Denomination enum
This commit is contained in:
commit
90e7ab6a33
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue