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" : {