From 30458411516bc42a477adc0a9a60b9d9ebd1ad94 Mon Sep 17 00:00:00 2001 From: catbref Date: Mon, 17 Dec 2018 14:18:23 +0000 Subject: [PATCH] added block timestamp too-soon check and relaxed PoS for too-old blocks --- src/qora/block/Block.java | 60 ++++++++++++++++++++++++---------- src/qora/block/BlockChain.java | 7 ++-- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/qora/block/Block.java b/src/qora/block/Block.java index 2c14e22d..f670851d 100644 --- a/src/qora/block/Block.java +++ b/src/qora/block/Block.java @@ -74,6 +74,7 @@ public class Block { TIMESTAMP_OLDER_THAN_PARENT(20), TIMESTAMP_IN_FUTURE(21), TIMESTAMP_MS_INCORRECT(22), + TIMESTAMP_TOO_SOON(23), VERSION_INCORRECT(30), FEATURE_NOT_YET_RELEASED(31), GENERATING_BALANCE_INCORRECT(40), @@ -207,21 +208,35 @@ public class Block { } long timestamp = parentBlock.calcNextBlockTimestamp(version, generatorSignature, generator); + long maximumTimestamp = parentBlock.getBlockData().getTimestamp() + BlockChain.getInstance().getMaxBlockTime(); + if (timestamp > maximumTimestamp) + timestamp = maximumTimestamp; + int transactionCount = 0; byte[] transactionsSignature = null; int height = parentBlockData.getHeight() + 1; - this.executeATs(); + this.transactions = new ArrayList(); - int atCount = this.ourAtStates.size(); - BigDecimal atFees = this.ourAtFees; + int atCount = 0; + BigDecimal atFees = BigDecimal.ZERO.setScale(8); BigDecimal totalFees = atFees; + // This instance used for AT processing this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance, generator.getPublicKey(), generatorSignature, atCount, atFees); - this.transactions = new ArrayList(); + // Requires this.blockData and this.transactions, sets this.ourAtStates and this.ourAtFees + this.executeATs(); + + atCount = this.ourAtStates.size(); this.atStates = this.ourAtStates; + atFees = this.ourAtFees; + totalFees = atFees; + + // Rebuild blockData using post-AT-execute data + this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance, + generator.getPublicKey(), generatorSignature, atCount, atFees); } // Getters/setters @@ -681,6 +696,10 @@ public class Block { if (this.blockData.getTimestamp() % 1000 != parentBlockData.getTimestamp() % 1000) return ValidationResult.TIMESTAMP_MS_INCORRECT; + // Too early to forge block? + if (this.blockData.getTimestamp() < parentBlock.getBlockData().getTimestamp() + BlockChain.getInstance().getMinBlockTime()) + return ValidationResult.TIMESTAMP_TOO_SOON; + // Check block version if (this.blockData.getVersion() != parentBlock.getNextBlockVersion()) return ValidationResult.VERSION_INCORRECT; @@ -691,23 +710,26 @@ public class Block { if (this.blockData.getGeneratingBalance().compareTo(parentBlock.calcNextBlockGeneratingBalance()) != 0) return ValidationResult.GENERATING_BALANCE_INCORRECT; - // Check generator is allowed to forge this block at this time - BigInteger hashValue = this.calcBlockHash(); - BigInteger target = parentBlock.calcGeneratorsTarget(this.generator); + // After maximum block period, then generator checks are relaxed + if (this.blockData.getTimestamp() < parentBlock.getBlockData().getTimestamp() + BlockChain.getInstance().getMaxBlockTime()) { + // Check generator is allowed to forge this block + BigInteger hashValue = this.calcBlockHash(); + BigInteger target = parentBlock.calcGeneratorsTarget(this.generator); - // Multiply target by guesses - long guesses = (this.blockData.getTimestamp() - parentBlockData.getTimestamp()) / 1000; - BigInteger lowerTarget = target.multiply(BigInteger.valueOf(guesses - 1)); - target = target.multiply(BigInteger.valueOf(guesses)); + // Multiply target by guesses + long guesses = (this.blockData.getTimestamp() - parentBlockData.getTimestamp()) / 1000; + BigInteger lowerTarget = target.multiply(BigInteger.valueOf(guesses - 1)); + target = target.multiply(BigInteger.valueOf(guesses)); - // Generator's target must exceed block's hashValue threshold - if (hashValue.compareTo(target) >= 0) - return ValidationResult.GENERATOR_NOT_ACCEPTED; + // Generator's target must exceed block's hashValue threshold + if (hashValue.compareTo(target) >= 0) + return ValidationResult.GENERATOR_NOT_ACCEPTED; - // Odd gen1 comment: "CHECK IF FIRST BLOCK OF USER" - // Each second elapsed allows generator to test a new "target" window against hashValue - if (hashValue.compareTo(lowerTarget) < 0) - return ValidationResult.GENERATOR_NOT_ACCEPTED; + // Odd gen1 comment: "CHECK IF FIRST BLOCK OF USER" + // Each second elapsed allows generator to test a new "target" window against hashValue + if (hashValue.compareTo(lowerTarget) < 0) + return ValidationResult.GENERATOR_NOT_ACCEPTED; + } // CIYAM ATs if (this.blockData.getATCount() != 0) { @@ -720,6 +742,8 @@ public class Block { } else { // Generate local AT states for comparison this.executeATs(); + + // XXX do we need to revalidate signatures if transactions list has changed? } // Check locally generated AT states against ones received from elsewhere diff --git a/src/qora/block/BlockChain.java b/src/qora/block/BlockChain.java index d8fe490e..e8e00aae 100644 --- a/src/qora/block/BlockChain.java +++ b/src/qora/block/BlockChain.java @@ -6,7 +6,6 @@ import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -157,9 +156,9 @@ public class BlockChain { long maxBytesPerUnitFee = (Long) Settings.getTypedJson(json, "maxBytesPerUnitFee", Long.class); BigDecimal maxBalance = Settings.getJsonBigDecimal(json, "coinSupply"); int blockDifficultyInterval = ((Long) Settings.getTypedJson(json, "blockDifficultyInterval", Long.class)).intValue(); - long minBlockTime = (Long) Settings.getTypedJson(json, "minBlockTime", Long.class); - long maxBlockTime = (Long) Settings.getTypedJson(json, "maxBlockTime", Long.class); - long blockTimestampMargin = (Long) Settings.getTypedJson(json, "blockTimestampMargin", Long.class); + long minBlockTime = 1000L * (Long) Settings.getTypedJson(json, "minBlockTime", Long.class); // config entry in seconds + long maxBlockTime = 1000L * (Long) Settings.getTypedJson(json, "maxBlockTime", Long.class); // config entry in seconds + long blockTimestampMargin = (Long) Settings.getTypedJson(json, "blockTimestampMargin", Long.class); // config entry in milliseconds // blockchain feature triggers Map> featureTriggers = new HashMap<>();