From b43209457adce699a1fb16edd54012baaa42f85c Mon Sep 17 00:00:00 2001 From: nicksavers Date: Fri, 8 Aug 2014 18:12:13 +0100 Subject: [PATCH] Decouple Genesis from repository and validate txStateRoot --- .../main/java/org/ethereum/core/Block.java | 55 ++++++++----------- .../java/org/ethereum/core/BlockHeader.java | 40 ++++++++------ .../main/java/org/ethereum/core/Genesis.java | 25 ++++++--- .../main/java/org/ethereum/db/Repository.java | 5 ++ .../ethereum/net/message/HelloMessage.java | 2 +- .../src/main/java/org/ethereum/trie/Trie.java | 5 +- 6 files changed, 69 insertions(+), 63 deletions(-) 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 5a07e1d1..0bddd87b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Block.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Block.java @@ -59,7 +59,7 @@ public class Block { /* Constructors */ public Block(byte[] rawData) { - logger.debug("RLP encoded [ " + Hex.toHexString(rawData) + " ]"); + logger.debug("new Block from RLP encoded [ " + Hex.toHexString(rawData) + " ]"); this.rlpEncoded = rawData; this.parsed = false; } @@ -71,10 +71,7 @@ public class Block { this.header = new BlockHeader(parentHash, unclesHash, coinbase, difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce); - this.txsState = new Trie(null); - - this.header.setTxTrieRoot(txsState.getRootHash()); - this.transactionsList = transactionsList; + this.transactionsList = transactionsList; this.uncleList = uncleList; this.parsed = true; } @@ -90,7 +87,7 @@ public class Block { // Parse Transactions RLPList txReceipts = (RLPList) block.get(1); - this.parseTxs(txReceipts); + this.parseTxs(this.header.getTxTrieRoot(), txReceipts); // Parse Uncles RLPList uncleBlocks = (RLPList) block.get(2); @@ -188,32 +185,25 @@ public class Block { this.header.setNonce(nonce); rlpEncoded = null; } - - public Trie getTxsState() { - return this.txsState; - } public List getTransactionsList() { if (!parsed) parseRLP(); - if (transactionsList == null) { - this.transactionsList = new ArrayList<>(); - } + if (transactionsList == null) + transactionsList = new ArrayList<>(); return transactionsList; } public List getTxReceiptList() { if (!parsed) parseRLP(); - if (transactionsList == null) { - this.txReceiptList = new ArrayList<>(); - } + if (txReceiptList == null) + txReceiptList = new ArrayList<>(); return txReceiptList; } public List getUncleList() { if (!parsed) parseRLP(); - if (uncleList == null) { - this.uncleList = new ArrayList<>(); - } + if (uncleList == null) + uncleList = new ArrayList<>(); return uncleList; } @@ -258,30 +248,31 @@ public class Block { return toStringBuff.toString(); } - private void parseTxs(RLPList txReceipts) { + private void parseTxs(byte[] expectedRoot, RLPList txReceipts) { this.txsState = new Trie(null); for (int i = 0; i < txReceipts.size(); i++) { RLPElement rlpTxReceipt = txReceipts.get(i); RLPElement txData = ((RLPList)rlpTxReceipt).get(0); - - Transaction tx = new Transaction(txData.getRLPData()); - this.addAndProcessTransaction(i, tx); // YP 4.3.1 - RLPElement cummGas = ((RLPList)rlpTxReceipt).get(1); - RLPElement pstTxState = ((RLPList)rlpTxReceipt).get(2); + RLPElement pstTxState = ((RLPList)rlpTxReceipt).get(1); + RLPElement cummGas = ((RLPList)rlpTxReceipt).get(2); + Transaction tx = new Transaction(txData.getRLPData()); + this.transactionsList.add(tx); TransactionReceipt txReceipt = - new TransactionReceipt(tx, cummGas.getRLPData(), pstTxState.getRLPData()); - txReceiptList.add(txReceipt); + new TransactionReceipt(tx, pstTxState.getRLPData(), cummGas.getRLPData()); + this.addTxReceipt(i, txReceipt); } - this.header.setTxTrieRoot(txsState.getRootHash()); + String calculatedRoot = Hex.toHexString(txsState.getRootHash()); + if(!calculatedRoot.equals(Hex.toHexString(expectedRoot))) + logger.error("Added tx receipts don't match the given txsStateRoot"); } - private void addAndProcessTransaction(int counter, Transaction tx) { - this.transactionsList.add(tx); - this.txsState.update(RLP.encodeInt(counter), tx.getEncoded()); + private void addTxReceipt(int counter, TransactionReceipt txReceipt) { + this.txReceiptList.add(txReceipt); + this.txsState.update(RLP.encodeInt(counter), txReceipt.getEncoded()); /* Figure out type of tx * 1. Contract creation @@ -294,8 +285,6 @@ public class Block { * 3. Account to account - * - update state object */ - -// this.allAccountsState.update(); } /** diff --git a/ethereumj-core/src/main/java/org/ethereum/core/BlockHeader.java b/ethereumj-core/src/main/java/org/ethereum/core/BlockHeader.java index 20df987c..05da6332 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -2,7 +2,7 @@ package org.ethereum.core; import java.math.BigInteger; -import org.ethereum.util.ByteUtil; +import static org.ethereum.util.ByteUtil.*; import org.ethereum.util.RLP; import org.ethereum.util.RLPItem; import org.ethereum.util.RLPList; @@ -59,9 +59,13 @@ public class BlockHeader { this.unclesHash = ((RLPItem) rlpHeader.get(1)).getRLPData(); this.coinbase = ((RLPItem) rlpHeader.get(2)).getRLPData(); this.stateRoot = ((RLPItem) rlpHeader.get(3)).getRLPData(); + this.txTrieRoot = ((RLPItem) rlpHeader.get(4)).getRLPData(); + if(this.txTrieRoot == null) + this.txTrieRoot = EMPTY_BYTE_ARRAY; + this.difficulty = ((RLPItem) rlpHeader.get(5)).getRLPData(); - + byte[] nrBytes = ((RLPItem) rlpHeader.get(6)).getRLPData(); byte[] gpBytes = ((RLPItem) rlpHeader.get(7)).getRLPData(); byte[] glBytes = ((RLPItem) rlpHeader.get(8)).getRLPData(); @@ -211,36 +215,36 @@ public class BlockHeader { public String toString() { toStringBuff.setLength(0); - toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append("\n"); - toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append("\n"); - toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append("\n"); - toStringBuff.append(" stateRoot=" + ByteUtil.toHexString(stateRoot)).append("\n"); - toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append("\n"); - toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append("\n"); + toStringBuff.append(" parentHash=" + toHexString(parentHash)).append("\n"); + toStringBuff.append(" unclesHash=" + toHexString(unclesHash)).append("\n"); + toStringBuff.append(" coinbase=" + toHexString(coinbase)).append("\n"); + toStringBuff.append(" stateRoot=" + toHexString(stateRoot)).append("\n"); + toStringBuff.append(" txTrieHash=" + toHexString(txTrieRoot)).append("\n"); + toStringBuff.append(" difficulty=" + toHexString(difficulty)).append("\n"); toStringBuff.append(" number=" + number).append("\n"); toStringBuff.append(" minGasPrice=" + minGasPrice).append("\n"); toStringBuff.append(" gasLimit=" + gasLimit).append("\n"); toStringBuff.append(" gasUsed=" + gasUsed).append("\n"); toStringBuff.append(" timestamp=" + timestamp + " (" + Utils.longToDateTime(timestamp) + ")").append("\n"); - toStringBuff.append(" extraData=" + ByteUtil.toHexString(extraData)).append("\n"); - toStringBuff.append(" nonce=" + ByteUtil.toHexString(nonce)).append("\n"); + toStringBuff.append(" extraData=" + toHexString(extraData)).append("\n"); + toStringBuff.append(" nonce=" + toHexString(nonce)).append("\n"); return toStringBuff.toString(); } public String toFlatString() { - toStringBuff.append(" parentHash=" + ByteUtil.toHexString(parentHash)).append(""); - toStringBuff.append(" unclesHash=" + ByteUtil.toHexString(unclesHash)).append(""); - toStringBuff.append(" coinbase=" + ByteUtil.toHexString(coinbase)).append(""); - toStringBuff.append(" stateRoot=" + ByteUtil.toHexString(stateRoot)).append(""); - toStringBuff.append(" txTrieHash=" + ByteUtil.toHexString(txTrieRoot)).append(""); - toStringBuff.append(" difficulty=" + ByteUtil.toHexString(difficulty)).append(""); + toStringBuff.append(" parentHash=" + toHexString(parentHash)).append(""); + toStringBuff.append(" unclesHash=" + toHexString(unclesHash)).append(""); + toStringBuff.append(" coinbase=" + toHexString(coinbase)).append(""); + toStringBuff.append(" stateRoot=" + toHexString(stateRoot)).append(""); + toStringBuff.append(" txTrieHash=" + toHexString(txTrieRoot)).append(""); + toStringBuff.append(" difficulty=" + toHexString(difficulty)).append(""); toStringBuff.append(" number=" + number).append(""); toStringBuff.append(" minGasPrice=" + minGasPrice).append(""); toStringBuff.append(" gasLimit=" + gasLimit).append(""); toStringBuff.append(" gasUsed=" + gasUsed).append(""); toStringBuff.append(" timestamp=" + timestamp).append(""); - toStringBuff.append(" extraData=" + ByteUtil.toHexString(extraData)).append(""); - toStringBuff.append(" nonce=" + ByteUtil.toHexString(nonce)).append(""); + toStringBuff.append(" extraData=" + toHexString(extraData)).append(""); + toStringBuff.append(" nonce=" + toHexString(nonce)).append(""); return toStringBuff.toString(); } 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 2f53d28a..153bcd35 100644 --- a/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/core/Genesis.java @@ -1,8 +1,7 @@ package org.ethereum.core; import org.ethereum.crypto.HashUtil; -import org.ethereum.db.Repository; -import org.ethereum.manager.WorldManager; +import org.ethereum.trie.Trie; import org.ethereum.util.RLP; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +28,7 @@ import java.math.BigInteger; */ public class Genesis extends Block { - private String[] premine = new String[] { + private static String[] premine = new String[] { "51ba59315b3a95761d0863b05ccc7a7f54703d99", "e4157b34ea9615cfbde6b4fda419828124b70c78", // # (CH) "b9c015918bdaba24b4ff057a92a3873d6eb201be", // # (V) @@ -64,16 +63,16 @@ public class Genesis extends Block { super(PARENT_HASH, UNCLES_HASH, COINBASE, DIFFICULTY, NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, TIMESTAMP, EXTRA_DATA, NONCE, null, null); - - Repository repository = WorldManager.getInstance().getRepository(); + + Trie state = new Trie(null); // 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. for (String address : premine) { - repository.createAccount(Hex.decode(address)); - repository.addBalance (Hex.decode(address), BigInteger.valueOf(2).pow(200) ); + AccountState acctState = new AccountState(); + acctState.addToBalance(getPremineAmount()); + state.update(Hex.decode(address), acctState.getEncoded()); } - setStateRoot(repository.getWorldState().getRootHash()); - repository.dumpState(0, 0, null); + setStateRoot(state.getRootHash()); logger.info("Genesis-hash: {}", Hex.toHexString(this.getHash())); logger.info("Genesis-stateRoot: {}", Hex.toHexString(this.getStateRoot())); @@ -85,4 +84,12 @@ public class Genesis extends Block { } return instance; } + + public final static String[] getPremine() { + return premine; + } + + public final static BigInteger getPremineAmount() { + return BigInteger.valueOf(2).pow(200); + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/db/Repository.java b/ethereumj-core/src/main/java/org/ethereum/db/Repository.java index e91f705e..7c01d47c 100644 --- a/ethereumj-core/src/main/java/org/ethereum/db/Repository.java +++ b/ethereumj-core/src/main/java/org/ethereum/db/Repository.java @@ -122,8 +122,13 @@ public class Repository { try { if (!iterator.hasNext()) { logger.info("DB is empty - adding Genesis"); + for (String address : Genesis.getPremine()) { + this.createAccount(Hex.decode(address)); + this.addBalance (Hex.decode(address), Genesis.getPremineAmount()); + } blockchain.storeBlock(Genesis.getInstance()); logger.debug("Block #{} -> {}", Genesis.NUMBER, blockchain.getLastBlock().toFlatString()); + dumpState(0, 0, null); } else { logger.debug("Displaying blocks stored in DB sorted on blocknumber"); diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java index ac3bf001..dd680a7e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/HelloMessage.java @@ -46,7 +46,7 @@ public class HelloMessage extends Message { // the message does no distinguish between the 0 and null so here I check command code for null // TODO: find out if it can be 00 - if (((RLPItem)(paramsList).get(0)).getRLPData() != null) { + if (((RLPItem)paramsList.get(0)).getRLPData() != null) { throw new Error("HelloMessage: parsing for mal data"); } diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java index 7a806d83..3bb3c550 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/Trie.java @@ -9,6 +9,7 @@ import java.util.Arrays; import org.ethereum.crypto.HashUtil; import org.ethereum.db.ByteArrayWrapper; +import org.ethereum.util.ByteUtil; import org.ethereum.util.Value; import org.iq80.leveldb.DB; import org.slf4j.Logger; @@ -46,7 +47,7 @@ public class Trie implements TrieFacade { private Object prevRoot; private Object root; private Cache cache; - + public Trie(DB db) { this(db, ""); } @@ -413,7 +414,7 @@ public class Trie implements TrieFacade { if (root == null || (root instanceof byte[] && ((byte[]) root).length == 0) || (root instanceof String && "".equals((String) root))) { - return new byte[0]; + return ByteUtil.EMPTY_BYTE_ARRAY; } else if (root instanceof byte[]) { return (byte[]) this.getRoot(); } else {