diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Block.java b/ethereumj-core/src/main/java/org/ethereum/core/Block.java index bda7e510..bde2061f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -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(){ @@ -144,6 +139,10 @@ public class Block { if (!parsed) parseRLP(); return this.header.getMinGasPrice(); } + + public boolean isGenesis() { + return this.header.getNumber() == 0; + } public long getGasLimit() { if (!parsed) parseRLP(); @@ -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(); diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java index 06164e0b..9ba69c9a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Blockchain.java @@ -20,19 +20,20 @@ public class Blockchain extends ArrayList { 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 walletTransactions = Collections.synchronizedMap(new HashMap()); - - public Blockchain(Wallet wallet) { this.db = WorldManager.instance.chainDB; this.wallet = wallet; @@ -83,27 +84,22 @@ public class Blockchain extends ArrayList { } private void addBlock(Block block) { - 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(); - - if(lastBlock == null || block.getNumber() > lastBlock.getNumber()) - this.lastBlock = block; - this.add(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.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 diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java index edfbc6f7..9a4af508 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Denomination.java @@ -10,18 +10,23 @@ 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; - + private Denomination(BigInteger value) { 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); diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java index 2dc391c5..9db273e7 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -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() { diff --git a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java index 40a57d37..6519264c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Transaction.java @@ -17,7 +17,7 @@ import java.security.SignatureException; import java.util.Arrays; /** - * A transaction (formally, T ) is a single cryptographically + * A transaction (formally, T) is a single cryptographically * signed instruction sent by an actor external to Ethereum. * An external actor can be a person (via a mobile device or desktop computer) * or could be from a piece of automated software running on a server. diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java b/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java new file mode 100644 index 00000000..ecc26842 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/vm/GasCost.java @@ -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; +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/GasLedger.java b/ethereumj-core/src/main/java/org/ethereum/vm/GasLedger.java deleted file mode 100644 index 314cdcbf..00000000 --- a/ethereumj-core/src/main/java/org/ethereum/vm/GasLedger.java +++ /dev/null @@ -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; -} diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java index af5c2d5c..b95177db 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/VM.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/VM.java @@ -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) { diff --git a/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java b/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java index 2210d050..21158254 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/BlockTest.java @@ -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."); + } } \ No newline at end of file