diff --git a/pom.xml b/pom.xml index 63c577df0..a5ed19a75 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.semux semux - 1.5.1 + 1.5.2 jar Semux is an experimental high-performance blockchain platform that powers decentralized application. diff --git a/src/main/java/org/semux/api/v2/TypeFactory.java b/src/main/java/org/semux/api/v2/TypeFactory.java index 206e960de..c9e71b8bf 100644 --- a/src/main/java/org/semux/api/v2/TypeFactory.java +++ b/src/main/java/org/semux/api/v2/TypeFactory.java @@ -67,6 +67,9 @@ public static BlockType blockType(Block block, Transaction coinbaseTransaction) .resultsRoot(Hex.encode0x(block.getResultsRoot())) .stateRoot(Hex.encode0x(block.getStateRoot())) .data(Hex.encode0x(block.getData())) + .gasUsed(String.valueOf(block.getResults().stream() + .mapToLong(TransactionResult::getGasUsed) + .sum())) .transactions(txs.stream() .map(tx -> transactionType(block.getNumber(), tx)) .collect(Collectors.toList())); diff --git a/src/main/java/org/semux/config/Constants.java b/src/main/java/org/semux/config/Constants.java index 5ea417e40..d5cee5c43 100644 --- a/src/main/java/org/semux/config/Constants.java +++ b/src/main/java/org/semux/config/Constants.java @@ -41,7 +41,7 @@ public class Constants { /** * Version of this client. */ - public static final String CLIENT_VERSION = "1.5.1"; + public static final String CLIENT_VERSION = "1.5.2"; /** * Capability of this client. diff --git a/src/main/java/org/semux/consensus/SemuxBft.java b/src/main/java/org/semux/consensus/SemuxBft.java index ee84efc49..460b747bb 100644 --- a/src/main/java/org/semux/consensus/SemuxBft.java +++ b/src/main/java/org/semux/consensus/SemuxBft.java @@ -472,7 +472,7 @@ protected void onNewHeight(long newHeight) { activeValidators = channelMgr.getActiveChannels(validators); // Pick 2/3th active validator's height as sync target. The sync will not be - // started if there are less than 2 active validators. + // started if there are less than 2 active validators OptionalLong target = activeValidators.stream() .mapToLong(c -> c.getRemotePeer().getLatestBlockNumber() + 1) .sorted() @@ -489,7 +489,7 @@ protected void onNewView(Proof p) { logger.trace("On new_view: {}", p); if (p.getHeight() == height // at same height - && p.getView() > view && state != State.COMMIT && state != State.FINALIZE) {// larger view + && p.getView() == view + 1 && state != State.COMMIT && state != State.FINALIZE) {// larger view // check proof-of-unlock VoteSet vs = new VoteSet(VoteType.PRECOMMIT, p.getHeight(), p.getView() - 1, validators); @@ -499,7 +499,7 @@ protected void onNewView(Proof p) { } // switch view - logger.debug("Switching view because of NEW_VIEW message"); + logger.debug("Switching view because of NEW_VIEW message: {}", p.getView()); jumpToView(p.getView(), p, null); } } @@ -509,7 +509,7 @@ protected void onProposal(Proposal p) { if (p.getHeight() == height // at the same height && (p.getView() == view && proposal == null && (state == State.NEW_HEIGHT || state == State.PROPOSE) // expecting - || p.getView() > view && state != State.COMMIT && state != State.FINALIZE) // larger view + || p.getView() == view + 1 && state != State.COMMIT && state != State.FINALIZE) // larger view && isPrimary(p.getHeight(), p.getView(), Hex.encode(p.getSignature().getAddress()))) { // check proof-of-unlock @@ -681,6 +681,12 @@ protected void updateValidators() { } activeValidators = channelMgr.getActiveChannels(validators); lastUpdate = TimeUtil.currentTimeMillis(); + + if (logger.isDebugEnabled()) { + logger.debug( + "Max validators = {}, Number of validators = {}, validators = {}, Number of active validators = {}, Active validators = {}", + maxValidators, validators.size(), String.join(",", validators), activeValidators.size()); + } } /** @@ -790,9 +796,10 @@ protected Block proposeBlock() { for (PendingManager.PendingTransaction tx : pending) { if (tx.transaction.getType() == TransactionType.CALL || tx.transaction.getType() == TransactionType.CREATE) { - long maxGasForTransaction = tx.transaction.getGas() + gasUsed; + long pendingGasForBlock = tx.transaction.getGas() + gasUsed; + if (tx.transaction.getGasPrice() >= config.vmMinGasPrice() - && maxGasForTransaction < config.vmBlockGasLimit()) { + && pendingGasForBlock < config.vmBlockGasLimit()) { TransactionResult result = exec.execute(tx.transaction, as, ds, semuxBlock); gasUsed += result.getGasUsed(); @@ -883,7 +890,7 @@ protected boolean validateBlock(BlockHeader header, List transactio // against our own local limit, only // when proposing List results = exec.execute(transactions, as, ds, - new SemuxBlock(header, config.vmMaxBlockGasLimit())); + new SemuxBlock(header, Long.MAX_VALUE)); if (!Block.validateResults(header, results)) { logger.warn("Invalid transactions"); return false; @@ -928,6 +935,9 @@ protected List getUnvalidatedTransactions(List transac * @param block */ protected void applyBlock(Block block) { + + long t1 = TimeUtil.currentTimeMillis(); + BlockHeader header = block.getHeader(); List transactions = block.getTransactions(); long number = header.getNumber(); @@ -946,7 +956,7 @@ protected void applyBlock(Block block) { // [3] evaluate all transactions List results = exec.execute(transactions, as, ds, - new SemuxBlock(block.getHeader(), config.vmMaxBlockGasLimit())); + new SemuxBlock(block.getHeader(), Long.MAX_VALUE)); if (!Block.validateResults(header, results)) { logger.debug("Invalid transactions"); return; @@ -977,6 +987,10 @@ protected void applyBlock(Block block) { } finally { lock.unlock(); } + + long t2 = TimeUtil.currentTimeMillis(); + logger.debug("Block apply: # txs = {}, time = {} ms", transactions.size(), t2 - t1); + } public enum State { diff --git a/src/main/java/org/semux/consensus/SemuxSync.java b/src/main/java/org/semux/consensus/SemuxSync.java index 69daf388c..cf89d84c3 100644 --- a/src/main/java/org/semux/consensus/SemuxSync.java +++ b/src/main/java/org/semux/consensus/SemuxSync.java @@ -38,6 +38,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.ethereum.vm.client.BlockStore; import org.semux.Kernel; +import org.semux.Network; import org.semux.config.Config; import org.semux.config.Constants; import org.semux.core.Amount; @@ -513,6 +514,16 @@ protected boolean validateBlock(Block block, AccountState asSnapshot, DelegateSt BlockHeader header = block.getHeader(); List transactions = block.getTransactions(); + // a workaround to ensure testnet clients ignore bad block, can remove at later + // date + if (config.network() == Network.TESTNET) { + String badBlock = "0x1a472841464b9bea9e530d73a95e1213f29adef11a3661f3e98df87a9a230b7d"; + String blockHash = Hex.encode0x(block.getHash()); + if (badBlock.equals(blockHash)) { + return false; + } + } + // [1] check block header Block latest = chain.getLatestBlock(); if (!Block.validateHeader(latest.getHeader(), header)) { @@ -572,8 +583,16 @@ protected boolean validateBlock(Block block, AccountState asSnapshot, DelegateSt } protected boolean validateBlockVotes(Block block) { - Set validators = new HashSet<>(chain.getValidators()); - int twoThirds = (int) Math.ceil(validators.size() * 2.0 / 3.0); + int maxValidators = config.getNumberOfValidators(block.getNumber()); + + List validatorList = chain.getValidators(); + + if (validatorList.size() > maxValidators) { + validatorList = validatorList.subList(0, maxValidators); + } + Set validators = new HashSet<>(validatorList); + + int twoThirds = (int) Math.round(validators.size() * 2.0 / 3.0); Vote vote = new Vote(VoteType.PRECOMMIT, Vote.VALUE_APPROVE, block.getNumber(), block.getView(), block.getHash()); @@ -590,7 +609,7 @@ protected boolean validateBlockVotes(Block block) { if (block.getVotes().stream() .map(sig -> new ByteArray(sig.getA())) .collect(Collectors.toSet()).size() < twoThirds) { - logger.warn("Not enough votes, needs 2/3+"); + logger.warn("Not enough votes, needs 2/3+ twoThirds = {}, block = {}", twoThirds, block); return false; } diff --git a/src/main/java/org/semux/consensus/VoteSet.java b/src/main/java/org/semux/consensus/VoteSet.java index 8b4ca14ea..a98be0d35 100644 --- a/src/main/java/org/semux/consensus/VoteSet.java +++ b/src/main/java/org/semux/consensus/VoteSet.java @@ -53,7 +53,7 @@ public VoteSet(VoteType type, long height, int view, List validators) { this.view = view; this.validators = new HashSet<>(validators); - this.twoThirds = (int) Math.ceil(validators.size() * 2.0 / 3.0); + this.twoThirds = (int) Math.round(validators.size() * 2.0 / 3.0); } /** diff --git a/src/main/java/org/semux/core/TransactionExecutor.java b/src/main/java/org/semux/core/TransactionExecutor.java index b15afd33e..f1ae1bf59 100644 --- a/src/main/java/org/semux/core/TransactionExecutor.java +++ b/src/main/java/org/semux/core/TransactionExecutor.java @@ -226,7 +226,9 @@ public List execute(List txs, AccountState as, D result.setCode(Code.SUCCESS); } else { executeVmTransaction(result, tx, as, block, gasUsedInBlock); - gasUsedInBlock += result.getGasUsed(); + if (result.getCode().isAccepted()) { + gasUsedInBlock += result.getGasUsed(); + } } break; @@ -259,7 +261,7 @@ private void executeVmTransaction(TransactionResult result, Transaction tx, Acco TransactionReceipt summary = executor.run(); if (summary == null) { - result.setCode(Code.FAILURE); + result.setCode(Code.INVALID_GAS); result.setGasUsed(tx.getGas()); } else { for (LogInfo log : summary.getLogs()) { diff --git a/src/main/resources/org/semux/api/swagger/v2.2.0.json b/src/main/resources/org/semux/api/swagger/v2.2.0.json index dbf0572a0..739ed69e6 100644 --- a/src/main/resources/org/semux/api/swagger/v2.2.0.json +++ b/src/main/resources/org/semux/api/swagger/v2.2.0.json @@ -1782,6 +1782,11 @@ "type" : "string", "pattern" : "^(0x)?[0-9a-fA-F]*$" }, + "gasUsed" : { + "type" : "string", + "format" : "int64", + "pattern" : "^\\d+$" + }, "transactions" : { "type" : "array", "items" : {