Change block and uncle reward with extra validation for poc6

This commit is contained in:
nicksavers 2014-09-18 10:32:44 +02:00
parent d45db5ada3
commit f1c5a42dce
4 changed files with 156 additions and 91 deletions

View File

@ -1,17 +1,13 @@
package org.ethereum.core;
import org.ethereum.crypto.HashUtil;
import org.ethereum.manager.WorldManager;
import org.ethereum.trie.Trie;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.FastByteComparisons;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPElement;
import org.ethereum.util.RLPList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.Arrays;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
@ -34,11 +30,11 @@ public class Block {
private static final Logger logger = LoggerFactory.getLogger("block");
/* A scalar value equal to the mininum limit of gas expenditure per block */
private static long MIN_GAS_LIMIT = 125000L;
public static BigInteger BLOCK_REWARD = BigInteger.valueOf(1500000000000000000L);
public static BigInteger UNCLE_REWARD = BLOCK_REWARD.multiply(
BigInteger.valueOf(3)).divide(BigInteger.valueOf(4));
BigInteger.valueOf(15)).divide(BigInteger.valueOf(16));
public static BigInteger INCLUSION_REWARD = Block.BLOCK_REWARD
.divide(BigInteger.valueOf(32));
private BlockHeader header;
@ -111,12 +107,28 @@ public class Block {
public byte[] getHash() {
if (!parsed) parseRLP();
return HashUtil.sha3(this.getEncoded());
return HashUtil.sha3(this.header.getEncoded());
}
public Block getParent() {
return WorldManager.getInstance().getBlockchain().getBlockByNumber(this.getNumber() - 1);
}
return this.header.getParent();
}
public byte[] calcDifficulty() {
if (!parsed) parseRLP();
return this.header.calcDifficulty();
}
public long calcGasLimit() {
if (!parsed) parseRLP();
return this.header.calcGasLimit();
}
public boolean validateNonce() {
if (!parsed) parseRLP();
return this.header.validateNonce();
}
public byte[] getParentHash() {
if (!parsed) parseRLP();
@ -167,10 +179,6 @@ public class Block {
if (!parsed) parseRLP();
return this.header.getMinGasPrice();
}
public boolean isGenesis() {
return this.getNumber() == Genesis.NUMBER;
}
public long getGasLimit() {
if (!parsed) parseRLP();
@ -323,72 +331,37 @@ public class Block {
* and expected time to the next block, is reduced.
*/
public boolean isValid() {
boolean isValid = true;
boolean isValid = false;
if(!this.isGenesis()) {
// verify difficulty meets requirements
isValid = this.getDifficulty() == this.calcDifficulty();
isValid = this.validateNonce();
// verify gasLimit meets requirements
isValid = this.getGasLimit() == this.calcGasLimit();
// verify timestamp meets requirements
isValid = this.getTimestamp() > this.getParent().getTimestamp();
// verify extraData doesn't exceed 1024 bytes
isValid = this.getExtraData() == null || this.getExtraData().length <= 1024;
isValid = this.header.isValid();
for (BlockHeader uncle : uncleList) {
// - They are valid headers (not necessarily valid blocks)
isValid = uncle.isValid();
// - Their parent is a kth generation ancestor for k in {2, 3, 4, 5, 6, 7}
long generationGap = this.getNumber() - uncle.getParent().getNumber();
isValid = generationGap > 1 && generationGap < 8;
// - They were not uncles of the kth generation ancestor for k in {1, 2, 3, 4, 5, 6}
generationGap = this.getNumber() - uncle.getNumber();
isValid = generationGap > 0 && generationGap < 7;
}
}
if(!isValid)
logger.warn("!!!Invalid block!!!");
return isValid;
}
/**
* Calculate GasLimit
* 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.isGenesis())
return Genesis.GAS_LIMIT;
else {
Block parent = this.getParent();
return Math.max(MIN_GAS_LIMIT, (parent.getGasLimit() * (1024 - 1) + (parent.getGasUsed() * 6 / 5)) / 1024);
}
public boolean isGenesis() {
return this.header.isGenesis();
}
/**
* 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.isGenesis())
return Genesis.DIFFICULTY;
else {
Block parent = this.getParent();
long parentDifficulty = new BigInteger(1, parent.getDifficulty()).longValue();
long newDifficulty = this.header.getTimestamp() < parent.getTimestamp() + 5 ? parentDifficulty - (parentDifficulty >> 10) : (parentDifficulty + (parentDifficulty >> 10));
return BigIntegers.asUnsignedByteArray(BigInteger.valueOf(newDifficulty));
}
}
/**
* Verify that block is valid for its difficulty
*
* @return
*/
public boolean validateNonce() {
BigInteger max = BigInteger.valueOf(2).pow(256);
byte[] target = BigIntegers.asUnsignedByteArray(32,
max.divide(new BigInteger(1, this.getDifficulty())));
byte[] hash = HashUtil.sha3(this.getEncodedWithoutNonce());
byte[] concat = Arrays.concatenate(hash, this.getNonce());
byte[] result = HashUtil.sha3(concat);
return FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0;
}
public byte[] getEncoded() {
if(rlpEncoded == null) {
this.rlpEncoded = this.header.getEncoded();
byte[] header = this.header.getEncoded();
byte[] transactions = RLP.encodeList();
byte[] uncles = RLP.encodeList();
this.rlpEncoded = RLP.encodeList(header, transactions, uncles);
}
return rlpEncoded;
}
@ -397,7 +370,7 @@ public class Block {
if (!parsed) parseRLP();
byte[] header = this.header.getEncodedWithoutNonce();
byte[] transactions = RLP.encodeList();
byte[] uncles = RLP.encodeList();
byte[] uncles = RLP.encodeList();
return RLP.encodeList(header, transactions, uncles);
}

View File

@ -3,10 +3,16 @@ package org.ethereum.core;
import java.math.BigInteger;
import static org.ethereum.util.ByteUtil.*;
import org.ethereum.crypto.HashUtil;
import org.ethereum.manager.WorldManager;
import org.ethereum.util.FastByteComparisons;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.ethereum.util.Utils;
import org.spongycastle.util.Arrays;
import org.spongycastle.util.BigIntegers;
/**
* Block header is a value object containing
@ -14,6 +20,9 @@ import org.ethereum.util.Utils;
*/
public class BlockHeader {
/* A scalar value equal to the mininum limit of gas expenditure per block */
private static long MIN_GAS_LIMIT = 125000L;
/* The SHA3 256-bit hash of the parent block, in its entirety */
private byte[] parentHash;
/* The SHA3 256-bit hash of the uncles list portion of this block */
@ -97,7 +106,74 @@ public class BlockHeader {
this.extraData = extraData;
this.nonce = nonce;
}
public boolean isValid() {
boolean isValid = false;
// verify difficulty meets requirements
isValid = this.getDifficulty() == this.calcDifficulty();
isValid = this.validateNonce();
// verify gasLimit meets requirements
isValid = this.getGasLimit() == this.calcGasLimit();
// verify timestamp meets requirements
isValid = this.getTimestamp() > this.getParent().getTimestamp();
// verify extraData doesn't exceed 1024 bytes
isValid = this.getExtraData() == null || this.getExtraData().length <= 1024;
return isValid;
}
/**
* 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.isGenesis())
return Genesis.DIFFICULTY;
else {
Block parent = this.getParent();
long parentDifficulty = new BigInteger(1, parent.getDifficulty()).longValue();
long newDifficulty = this.getTimestamp() < parent.getTimestamp() + 5 ? parentDifficulty - (parentDifficulty >> 10) : (parentDifficulty + (parentDifficulty >> 10));
return BigIntegers.asUnsignedByteArray(BigInteger.valueOf(newDifficulty));
}
}
/**
* Calculate GasLimit
* 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.isGenesis())
return Genesis.GAS_LIMIT;
else {
Block parent = this.getParent();
return Math.max(MIN_GAS_LIMIT, (parent.getGasLimit() * (1024 - 1) + (parent.getGasUsed() * 6 / 5)) / 1024);
}
}
/**
* Verify that block is valid for its difficulty
*
* @return
*/
public boolean validateNonce() {
BigInteger max = BigInteger.valueOf(2).pow(256);
byte[] target = BigIntegers.asUnsignedByteArray(32,
max.divide(new BigInteger(1, this.getDifficulty())));
byte[] hash = HashUtil.sha3(this.getEncodedWithoutNonce());
byte[] concat = Arrays.concatenate(hash, this.getNonce());
byte[] result = HashUtil.sha3(concat);
return FastByteComparisons.compareTo(result, 0, 32, target, 0, 32) < 0;
}
public boolean isGenesis() {
return this.getNumber() == Genesis.NUMBER;
}
public Block getParent() {
return WorldManager.getInstance().getBlockchain().getBlockByNumber(this.getNumber() - 1);
}
public byte[] getParentHash() {
return parentHash;
}

View File

@ -168,8 +168,10 @@ public class BlockchainImpl implements Blockchain {
}
/**
* Add reward to block- and every uncle coinbase
* assuming the entire block is valid.
*
* @param block
* @param block object containing the header and uncles
*/
private void addReward(Block block) {
// Create coinbase if doesn't exist yet
@ -177,19 +179,17 @@ public class BlockchainImpl implements Blockchain {
repository.createAccount(block.getCoinbase());
// Add standard block reward
repository.addBalance(block.getCoinbase(), Block.BLOCK_REWARD);
BigInteger totalBlockReward = Block.BLOCK_REWARD;
// Add extra rewards based on number of uncles
if(block.getUncleList().size() > 0) {
BigInteger partialReward = Block.BLOCK_REWARD
.multiply(BigInteger.valueOf(1 * block.getUncleList().size()))
.divide(BigInteger.valueOf(8));
repository.addBalance(block.getCoinbase(), partialReward);
for (BlockHeader uncle : block.getUncleList()) {
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
}
totalBlockReward.add(Block.INCLUSION_REWARD
.multiply(BigInteger.valueOf(block.getUncleList().size())));
}
repository.addBalance(block.getCoinbase(), totalBlockReward);
}
public void storeBlock(Block block) {

View File

@ -5,6 +5,7 @@ import java.math.BigInteger;
import org.ethereum.manager.WorldManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.spongycastle.util.encoders.Hex;
import org.junit.Test;
@ -24,15 +25,9 @@ public class BlockTest {
}
// https://ethereum.etherpad.mozilla.org/12
private String CPP_PoC5_GENESIS_HEX_RLP_ENCODED = "f8abf8a7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a08dbd704eb38d1c2b73ee4788715ea5828a030650829703f077729b2b613dd20680834000008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
private String CPP_PoC5_GENESIS_HEX_HASH = "a7722d611450de26f55026b6544e34d9431b0a67a829e1794ac36fa4f079208f";
private String PoC6_GENESIS_HEX_RLP_ENCODED = "f8abf8a7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a008bf6a98374f333b84e7d063d607696ac7cbbd409bd20fbe6a741c2dfc0eb28580830200008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
private String PoC6_GENESIS_HEX_HASH = "08436a4d33c77e6acf013e586a3333ad152f25d31df8b68749d85046810e1f4b";
String block_1 = "f8abf8a7a0000000000000000000000000000000000000000000000000000000"
+ "0000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d"
+ "49347940000000000000000000000000000000000000000a08dbd704eb38d1c2b73ee47"
+ "88715ea5828a030650829703f077729b2b613dd20680834000008080830f4240808080a"
+ "004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
String block_2 = "f8b5f8b1a0cf4b25b08b39350304fe12a16e4216c01a426f8f3dbf0d392b5b45"
+ "8ffb6a399da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a1"
+ "42fd40d493479476f5eabe4b342ee56b8ceba6ab2a770c3e2198e7a08a22d58b"
@ -62,7 +57,7 @@ public class BlockTest {
@Test /* got from go guy */
public void testGenesisFromRLP() {
// from RLP encoding
byte[] genesisBytes = Hex.decode(CPP_PoC5_GENESIS_HEX_RLP_ENCODED);
byte[] genesisBytes = Hex.decode(PoC6_GENESIS_HEX_RLP_ENCODED);
Block genesisFromRLP = new Block(genesisBytes);
Block genesis = Genesis.getInstance();
assertEquals(Hex.toHexString(genesis.getHash()), Hex.toHexString(genesisFromRLP.getHash()));
@ -91,8 +86,8 @@ public class BlockTest {
)
*/
Block genesis = Genesis.getInstance();
assertEquals(CPP_PoC5_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(genesis.getEncoded()));
assertEquals(CPP_PoC5_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
assertEquals(PoC6_GENESIS_HEX_HASH, Hex.toHexString(genesis.getHash()));
assertEquals(PoC6_GENESIS_HEX_RLP_ENCODED, Hex.toHexString(genesis.getEncoded()));
}
@Test /* block without transactions - block#32 in PoC5 cpp-chain */
@ -111,7 +106,7 @@ public class BlockTest {
@Test /* large block with 5 transactions -block#1 in PoC5 cpp-chain */
public void testBlockWithContractCreation() {
byte[] payload = Hex.decode(block_1);
byte[] payload = Hex.decode(PoC6_GENESIS_HEX_RLP_ENCODED);
Block block = new Block(payload);
System.out.println(block.toString());
}
@ -127,7 +122,7 @@ public class BlockTest {
// Storing genesis because the parent needs to be in the DB for calculation.
WorldManager.getInstance().getBlockchain().add(genesis);
Block block1 = new Block(Hex.decode(block_1));
Block block1 = new Block(Hex.decode(PoC6_GENESIS_HEX_RLP_ENCODED));
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
BigInteger actualDifficulty = new BigInteger(1, block1.getDifficulty());
System.out.println("Block#1 actual difficulty = " + actualDifficulty.toString());
@ -147,11 +142,32 @@ public class BlockTest {
WorldManager.getInstance().getBlockchain().add(genesis);
// Test with block
Block block1 = new Block(Hex.decode(block_1));
Block block1 = new Block(Hex.decode(PoC6_GENESIS_HEX_RLP_ENCODED));
long calcGasLimit = block1.calcGasLimit();
long actualGasLimit = block1.getGasLimit();
System.out.println("Block#1 actual gasLimit = " + actualGasLimit);
System.out.println("Block#1 calculated gasLimit = " + calcGasLimit);
assertEquals(actualGasLimit, calcGasLimit);
}
@Test
@Ignore
public void testUncleValidGenerationGap() {
// TODO
fail("Not yet implemented");
}
@Test
@Ignore
public void testUncleInvalidGenerationGap() {
// TODO
fail("Not yet implemented");
}
@Test
@Ignore
public void testUncleInvalidParentGenerationGap() {
// TODO
fail("Not yet implemented");
}
}