Add genesis block and block encoding
This commit is contained in:
parent
41862c44a1
commit
02bce31d63
|
@ -1,6 +1,7 @@
|
||||||
package org.ethereum.core;
|
package org.ethereum.core;
|
||||||
|
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.util.RLP;
|
||||||
import org.ethereum.util.RLPElement;
|
import org.ethereum.util.RLPElement;
|
||||||
import org.ethereum.util.RLPItem;
|
import org.ethereum.util.RLPItem;
|
||||||
import org.ethereum.util.RLPList;
|
import org.ethereum.util.RLPList;
|
||||||
|
@ -25,7 +26,6 @@ public class Block {
|
||||||
private static int GAS_LIMIT = (int) Math.pow(10, 6);
|
private static int GAS_LIMIT = (int) Math.pow(10, 6);
|
||||||
|
|
||||||
private RLPList rawData;
|
private RLPList rawData;
|
||||||
private byte[] encodedBlock;
|
|
||||||
private boolean parsed = false;
|
private boolean parsed = false;
|
||||||
|
|
||||||
private byte[] hash;
|
private byte[] hash;
|
||||||
|
@ -77,14 +77,10 @@ public class Block {
|
||||||
this.parsed = false;
|
this.parsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block(byte[] encodedBlock) {
|
|
||||||
this.encodedBlock = encodedBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase,
|
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase,
|
||||||
byte[] stateRoot, byte[] txTrieRoot, byte[] difficulty,
|
byte[] stateRoot, byte[] txTrieRoot, byte[] difficulty,
|
||||||
long timestamp, long number, long minGasPrice, long gasLimit,
|
long number, long minGasPrice, long gasLimit, long gasUsed,
|
||||||
long gasUsed, byte[] extraData, byte[] nonce,
|
long timestamp, byte[] extraData, byte[] nonce,
|
||||||
List<Transaction> transactionsList, List<Block> uncleList) {
|
List<Transaction> transactionsList, List<Block> uncleList) {
|
||||||
this.parentHash = parentHash;
|
this.parentHash = parentHash;
|
||||||
this.unclesHash = unclesHash;
|
this.unclesHash = unclesHash;
|
||||||
|
@ -92,11 +88,11 @@ public class Block {
|
||||||
this.stateRoot = stateRoot;
|
this.stateRoot = stateRoot;
|
||||||
this.txTrieRoot = txTrieRoot;
|
this.txTrieRoot = txTrieRoot;
|
||||||
this.difficulty = difficulty;
|
this.difficulty = difficulty;
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.number = number;
|
this.number = number;
|
||||||
this.minGasPrice = minGasPrice;
|
this.minGasPrice = minGasPrice;
|
||||||
this.gasLimit = gasLimit;
|
this.gasLimit = gasLimit;
|
||||||
this.gasUsed = gasUsed;
|
this.gasUsed = gasUsed;
|
||||||
|
this.timestamp = timestamp;
|
||||||
this.extraData = extraData;
|
this.extraData = extraData;
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
this.transactionsList = transactionsList;
|
this.transactionsList = transactionsList;
|
||||||
|
@ -105,7 +101,7 @@ public class Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
||||||
// difficulty, timestamp, number, minGasPrice, gasLimit, gasUsed,
|
// difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
|
||||||
// extradata, nonce]
|
// extradata, nonce]
|
||||||
private void parseRLP() {
|
private void parseRLP() {
|
||||||
|
|
||||||
|
@ -113,37 +109,38 @@ public class Block {
|
||||||
|
|
||||||
this.hash = HashUtil.sha3(params.getRLPData());
|
this.hash = HashUtil.sha3(params.getRLPData());
|
||||||
|
|
||||||
|
RLPList header = (RLPList) params.get(0);
|
||||||
|
|
||||||
|
this.parentHash = ((RLPItem) header.get(0)).getData();
|
||||||
|
this.unclesHash = ((RLPItem) header.get(1)).getData();
|
||||||
|
this.coinbase = ((RLPItem) header.get(2)).getData();
|
||||||
|
this.stateRoot = ((RLPItem) header.get(3)).getData();
|
||||||
|
this.txTrieRoot = ((RLPItem) header.get(4)).getData();
|
||||||
|
this.difficulty = ((RLPItem) header.get(5)).getData();
|
||||||
|
|
||||||
this.parentHash = ((RLPItem) params.get(0)).getData();
|
byte[] tsBytes = ((RLPItem) header.get(6)).getData();
|
||||||
this.unclesHash = ((RLPItem) params.get(1)).getData();
|
byte[] nrBytes = ((RLPItem) header.get(7)).getData();
|
||||||
this.coinbase = ((RLPItem) params.get(2)).getData();
|
byte[] gpBytes = ((RLPItem) header.get(8)).getData();
|
||||||
this.stateRoot = ((RLPItem) params.get(3)).getData();
|
byte[] glBytes = ((RLPItem) header.get(9)).getData();
|
||||||
this.txTrieRoot = ((RLPItem) params.get(4)).getData();
|
byte[] guBytes = ((RLPItem) header.get(10)).getData();
|
||||||
this.difficulty = ((RLPItem) params.get(5)).getData();
|
|
||||||
|
|
||||||
byte[] tsBytes = ((RLPItem) params.get(6)).getData();
|
this.timestamp = tsBytes == null ? 0 : (new BigInteger(tsBytes)).longValue();
|
||||||
byte[] nrBytes = ((RLPItem) params.get(7)).getData();
|
this.number = nrBytes == null ? 0 : (new BigInteger(nrBytes)).longValue();
|
||||||
byte[] gpBytes = ((RLPItem) params.get(8)).getData();
|
|
||||||
byte[] glBytes = ((RLPItem) params.get(9)).getData();
|
|
||||||
byte[] guBytes = ((RLPItem) params.get(10)).getData();
|
|
||||||
|
|
||||||
this.timestamp = (new BigInteger(tsBytes)).longValue();
|
|
||||||
this.number = (new BigInteger(nrBytes)).longValue();
|
|
||||||
this.minGasPrice = gpBytes == null ? 0 : (new BigInteger(gpBytes)).longValue();
|
this.minGasPrice = gpBytes == null ? 0 : (new BigInteger(gpBytes)).longValue();
|
||||||
this.gasLimit = glBytes == null ? 0 : (new BigInteger(glBytes)).longValue();
|
this.gasLimit = glBytes == null ? 0 : (new BigInteger(glBytes)).longValue();
|
||||||
this.gasUsed = guBytes == null ? 0 : (new BigInteger(guBytes)).longValue();
|
this.gasUsed = guBytes == null ? 0 : (new BigInteger(guBytes)).longValue();
|
||||||
|
|
||||||
this.extraData = ((RLPItem) params.get(11)).getData();
|
this.extraData = ((RLPItem) header.get(11)).getData();
|
||||||
this.nonce = ((RLPItem) params.get(12)).getData();
|
this.nonce = ((RLPItem) header.get(12)).getData();
|
||||||
|
|
||||||
// parse transactions
|
// parse transactions
|
||||||
RLPList transactions = (RLPList) rawData.get(1);
|
RLPList transactions = (RLPList) params.get(1);
|
||||||
for (RLPElement rlpTx : transactions){
|
for (RLPElement rlpTx : transactions){
|
||||||
Transaction tx = new Transaction((RLPList)rlpTx);
|
Transaction tx = new Transaction((RLPList)rlpTx);
|
||||||
this.transactionsList.add(tx);
|
this.transactionsList.add(tx);
|
||||||
}
|
}
|
||||||
// parse uncles
|
// parse uncles
|
||||||
RLPList uncleBlocks = (RLPList) rawData.get(2);
|
RLPList uncleBlocks = (RLPList) params.get(2);
|
||||||
for (RLPElement rawUncle : uncleBlocks){
|
for (RLPElement rawUncle : uncleBlocks){
|
||||||
Block blockData = new Block((RLPList)rawUncle);
|
Block blockData = new Block((RLPList)rawUncle);
|
||||||
this.uncleList.add(blockData);
|
this.uncleList.add(blockData);
|
||||||
|
@ -153,7 +150,7 @@ public class Block {
|
||||||
|
|
||||||
public byte[] getHash(){
|
public byte[] getHash(){
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
return hash;
|
return HashUtil.sha3(this.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getParent() {
|
public Block getParent() {
|
||||||
|
@ -224,16 +221,22 @@ public class Block {
|
||||||
|
|
||||||
public List<Transaction> getTransactionsList() {
|
public List<Transaction> getTransactionsList() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
|
if (transactionsList == null) {
|
||||||
|
this.transactionsList = new ArrayList<Transaction>();
|
||||||
|
}
|
||||||
return transactionsList;
|
return transactionsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Block> getUncleList() {
|
public List<Block> getUncleList() {
|
||||||
if (!parsed) parseRLP();
|
if (!parsed) parseRLP();
|
||||||
|
if (uncleList == null) {
|
||||||
|
this.uncleList = new ArrayList<Block>();
|
||||||
|
}
|
||||||
return uncleList;
|
return uncleList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
// [parent_hash, uncles_hash, coinbase, state_root, tx_trie_root,
|
||||||
// difficulty, timestamp, number, minGasPrice, gasLimit, gasUsed,
|
// difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp,
|
||||||
// extradata, nonce]
|
// extradata, nonce]
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -247,11 +250,11 @@ public class Block {
|
||||||
", stateHash=" + Utils.toHexString(stateRoot) +
|
", stateHash=" + Utils.toHexString(stateRoot) +
|
||||||
", txTrieHash=" + Utils.toHexString(txTrieRoot) +
|
", txTrieHash=" + Utils.toHexString(txTrieRoot) +
|
||||||
", difficulty=" + Utils.toHexString(difficulty) +
|
", difficulty=" + Utils.toHexString(difficulty) +
|
||||||
", timestamp=" + timestamp +
|
|
||||||
", number=" + number +
|
", number=" + number +
|
||||||
", minGasPrice=" + minGasPrice +
|
", minGasPrice=" + minGasPrice +
|
||||||
", gasLimit=" + gasLimit +
|
", gasLimit=" + gasLimit +
|
||||||
", gasUsed=" + gasUsed +
|
", gasUsed=" + gasUsed +
|
||||||
|
", timestamp=" + timestamp +
|
||||||
", extraData=" + Utils.toHexString(extraData) +
|
", extraData=" + Utils.toHexString(extraData) +
|
||||||
", nonce=" + Utils.toHexString(nonce) +
|
", nonce=" + Utils.toHexString(nonce) +
|
||||||
']';
|
']';
|
||||||
|
@ -284,11 +287,35 @@ public class Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getEncoded() {
|
public byte[] getEncoded() {
|
||||||
if (this.encodedBlock == null) parseRLP();
|
byte[] parentHash = RLP.encodeElement(this.parentHash);
|
||||||
return this.encodedBlock;
|
byte[] unclesHash = RLP.encodeElement(this.unclesHash);
|
||||||
}
|
byte[] coinbase = RLP.encodeElement(this.coinbase);
|
||||||
|
byte[] stateRoot = RLP.encodeElement(this.stateRoot);
|
||||||
public byte[] hash() {
|
byte[] txTrieRoot = RLP.encodeElement(this.txTrieRoot);
|
||||||
return HashUtil.sha3(this.getEncoded());
|
byte[] difficulty = RLP.encodeElement(this.difficulty);
|
||||||
|
byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number));
|
||||||
|
byte[] minGasPrice = RLP.encodeBigInteger(BigInteger.valueOf(this.minGasPrice));
|
||||||
|
byte[] gasLimit = RLP.encodeBigInteger(BigInteger.valueOf(this.gasLimit));
|
||||||
|
byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed));
|
||||||
|
byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp));
|
||||||
|
byte[] extraData = RLP.encodeElement(this.extraData);
|
||||||
|
byte[] nonce = RLP.encodeElement(this.nonce);
|
||||||
|
|
||||||
|
byte[] header = RLP.encodeList(parentHash, unclesHash, coinbase,
|
||||||
|
stateRoot, txTrieRoot, difficulty, number,
|
||||||
|
minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce);
|
||||||
|
|
||||||
|
byte[] transactions = RLP.encodeList();
|
||||||
|
byte[] uncles = RLP.encodeList();
|
||||||
|
|
||||||
|
return RLP.encodeList(header, transactions, uncles);
|
||||||
|
|
||||||
|
// TODO: Alternative clean way to encode, using RLP.encode() after it's optimized
|
||||||
|
// Object[] header = new Object[] { parentHash, unclesHash, coinbase,
|
||||||
|
// stateRoot, txTrieRoot, difficulty, number, minGasPrice,
|
||||||
|
// gasLimit, gasUsed, timestamp, extraData, nonce };
|
||||||
|
// Object[] transactions = this.getTransactionsList().toArray();
|
||||||
|
// Object[] uncles = this.getUncleList().toArray();
|
||||||
|
// return RLP.encode(new Object[] { header, transactions, uncles });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.ethereum.core;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.util.RLP;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
public class Genesis extends Block {
|
||||||
|
|
||||||
|
private static byte[] zeroHash256 = new byte[32];
|
||||||
|
private static byte[] zeroHash160 = new byte[20];
|
||||||
|
private static byte[] sha3EmptyList = HashUtil.sha3(RLP.encodeList());
|
||||||
|
|
||||||
|
private static byte[] parentHash = zeroHash256;
|
||||||
|
private static byte[] unclesHash = sha3EmptyList;
|
||||||
|
private static byte[] coinbase = zeroHash160;
|
||||||
|
private static byte[] stateRoot = // TODO: Get stateRoot from actual state
|
||||||
|
Hex.decode("2f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817d");
|
||||||
|
private static byte[] txTrieRoot = zeroHash256;
|
||||||
|
private static byte[] difficulty = BigInteger.valueOf((long) Math.pow(2, 22)).toByteArray();
|
||||||
|
private static long number = 0;
|
||||||
|
private static long minGasPrice = 0;
|
||||||
|
private static long gasLimit = 1000000;
|
||||||
|
private static long gasUsed = 0;
|
||||||
|
private static long timestamp = 0;
|
||||||
|
private static byte[] extraData = new byte[0];
|
||||||
|
private static byte[] nonce = HashUtil.sha3(new byte[]{42});
|
||||||
|
|
||||||
|
public Genesis() {
|
||||||
|
super(parentHash, unclesHash, coinbase, stateRoot,
|
||||||
|
txTrieRoot, difficulty, number, minGasPrice, gasLimit, gasUsed,
|
||||||
|
timestamp, extraData, nonce, null, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,9 @@ package org.ethereum.block;
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import org.ethereum.core.Block;
|
import org.ethereum.core.Block;
|
||||||
|
import org.ethereum.core.Genesis;
|
||||||
import org.ethereum.crypto.HashUtil;
|
import org.ethereum.crypto.HashUtil;
|
||||||
|
import org.ethereum.util.ByteUtil;
|
||||||
import org.ethereum.util.RLP;
|
import org.ethereum.util.RLP;
|
||||||
import org.ethereum.util.RLPList;
|
import org.ethereum.util.RLPList;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -11,6 +13,7 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class BlockTest {
|
public class BlockTest {
|
||||||
|
|
||||||
|
@ -120,17 +123,12 @@ public class BlockTest {
|
||||||
public void testGenesisFromRLP(){
|
public void testGenesisFromRLP(){
|
||||||
// from RLP encoding
|
// from RLP encoding
|
||||||
byte[] genesisBytes = Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED);
|
byte[] genesisBytes = Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED);
|
||||||
Block genesis = new Block(genesisBytes);
|
Block genesis = new Block(RLP.decode2(genesisBytes));
|
||||||
assertEquals(CPP_PoC5_GENESIS_HEX_HASH, Hex.toHexString(genesis.hash()));
|
assertEquals(CPP_PoC5_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenesisFromNew() {
|
public void testGenesisFromNew() {
|
||||||
|
|
||||||
System.out.println(CPP_PoC5_GENESIS_HEX_RLP_ENCODED);
|
|
||||||
Object genesisItems = RLP.decode(Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED), 0).getDecoded();
|
|
||||||
// TODO: verify genesis items with expected values
|
|
||||||
|
|
||||||
/* From: https://ethereum.etherpad.mozilla.org/11
|
/* From: https://ethereum.etherpad.mozilla.org/11
|
||||||
Genesis block is:
|
Genesis block is:
|
||||||
(
|
(
|
||||||
|
@ -149,32 +147,11 @@ public class BlockTest {
|
||||||
B32(sha3(B(42)))
|
B32(sha3(B(42)))
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
|
Block genesis = new Genesis();
|
||||||
byte[] parentHash = new byte[32]; System.out.println(Hex.toHexString(parentHash));
|
assertEquals(CPP_PoC5_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(genesis.getEncoded()));
|
||||||
byte[] unclesHash = HashUtil.sha3(new byte[0]); System.out.println(Hex.toHexString(unclesHash));
|
assertEquals(CPP_PoC5_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
|
||||||
byte[] coinbase = new byte[20]; System.out.println(Hex.toHexString(coinbase));
|
|
||||||
byte[] stateRoot = Hex.decode(CPP_PoC5_GENESIS_STATE_ROOT_HEX_HASH); System.out.println(Hex.toHexString(stateRoot));
|
|
||||||
byte[] txTrieRoot = new byte[32]; System.out.println(Hex.toHexString(txTrieRoot));
|
|
||||||
byte[] difficulty = doubleToByteArray(Math.pow(2, 22)); System.out.println(Hex.toHexString(difficulty));
|
|
||||||
long timestamp = 0; System.out.println(Long.toHexString(timestamp));
|
|
||||||
long number = 0; System.out.println(Long.toHexString(number));
|
|
||||||
long minGasPrice = 0; System.out.println(Long.toHexString(minGasPrice));
|
|
||||||
long gasLimit = 1000000; System.out.println(Long.toHexString(gasLimit));
|
|
||||||
long gasUsed = 0; System.out.println(Long.toHexString(gasUsed));
|
|
||||||
byte[] extraData = new byte[0]; System.out.println(Hex.toHexString(extraData));
|
|
||||||
byte[] nonce = HashUtil.sha3(new byte[]{42}); System.out.println(Hex.toHexString(nonce));
|
|
||||||
Block block = new Block(parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, difficulty, number, minGasPrice, gasLimit, gasUsed, timestamp, extraData, nonce, null, null);
|
|
||||||
assertEquals(CPP_PoC5_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(block.getEncoded()));
|
|
||||||
assertEquals(CPP_PoC5_GENESIS_HEX_HASH, block.hash());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] doubleToByteArray(double d) {
|
|
||||||
byte[] output = new byte[8];
|
|
||||||
long lng = Double.doubleToLongBits(d);
|
|
||||||
for(int i = 0; i < 8; i++) output[i] = (byte)((lng >> ((7 - i) * 8)) & 0xff);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test /* create BlockData from part of real RLP BLOCKS message */
|
@Test /* create BlockData from part of real RLP BLOCKS message */
|
||||||
public void test3(){
|
public void test3(){
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue