diff --git a/src/main/java/org/tron/common/overlay/client/PeerClient.java b/src/main/java/org/tron/common/overlay/client/PeerClient.java index 747b2fd24c6..ef9d9400729 100644 --- a/src/main/java/org/tron/common/overlay/client/PeerClient.java +++ b/src/main/java/org/tron/common/overlay/client/PeerClient.java @@ -2,25 +2,27 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultMessageSizeEstimator; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import org.tron.common.overlay.discover.Node; +import org.tron.common.overlay.discover.NodeHandler; +import org.tron.common.overlay.message.ReasonCode; import org.tron.common.overlay.server.TronChannelInitializer; import org.tron.core.config.args.Args; import org.tron.core.net.node.NodeImpl; -import java.io.IOException; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - @Component public class PeerClient { @@ -54,6 +56,20 @@ public void connect(String host, int port, String remoteId) { } } + public ChannelFuture connectAsync(NodeHandler nodeHandler, boolean discoveryMode) { + Node node = nodeHandler.getNode(); + return connectAsync(node.getHost(), node.getPort(), node.getHexId(), discoveryMode) + .addListener((ChannelFutureListener) future -> { + if (!future.isSuccess()) { + logger.error("connect to {}:{} fail,cause:{}", node.getHost(), node.getPort(), + future.cause().getMessage()); + nodeHandler.getNodeStatistics().nodeDisconnectedLocal(ReasonCode.CONNECT_FAIL); + nodeHandler.getNodeStatistics().notifyDisconnect(); + future.channel().close(); + } + }); + } + public ChannelFuture connectAsync(String host, int port, String remoteId, boolean discoveryMode) { logger.info("connect peer {} {} {}", host, port, remoteId); diff --git a/src/main/java/org/tron/common/overlay/discover/NodeStatistics.java b/src/main/java/org/tron/common/overlay/discover/NodeStatistics.java index 9dae871591b..294003c7aae 100644 --- a/src/main/java/org/tron/common/overlay/discover/NodeStatistics.java +++ b/src/main/java/org/tron/common/overlay/discover/NodeStatistics.java @@ -21,13 +21,13 @@ import static java.lang.Math.min; import java.util.concurrent.atomic.AtomicLong; - import org.tron.common.overlay.message.ReasonCode; public class NodeStatistics { public final static int REPUTATION_PREDEFINED = 100000; public final static long TOO_MANY_PEERS_PENALIZE_TIMEOUT = 60 * 1000; + private static final long CLEAR_CYCLE_TIME = 60 * 60 * 1000; public class StatHandler { @@ -92,7 +92,7 @@ private int getSessionFairReputation() { int discoverReput = 0; discoverReput += - min(discoverInPong.get(), 1) * (discoverOutPing.get() == discoverInPong.get() ? 50 : 1); + min(discoverInPong.get(), 1) * (discoverOutPing.get() == discoverInPong.get() ? 51 : 1); discoverReput += min(discoverInNeighbours.get(), 10) * 10; discoverReput += min(discoverInFind.get(), 50); @@ -117,7 +117,10 @@ private int getSessionFairReputation() { } } } - return discoverReput + 10 * reput; + int score = + discoverReput + 10 * reput - (int) Math.pow(2, disconnectTimes) * (disconnectTimes > 0 ? 10 + : 0); + return score > 0 ? score : 0; } public int getReputation() { @@ -146,7 +149,15 @@ public boolean isReputationPenalized() { return true; } - return tronLastLocalDisconnectReason == ReasonCode.NULL_IDENTITY || + if (lastDisconnectedTime > 0 + && (System.currentTimeMillis() - lastDisconnectedTime) > CLEAR_CYCLE_TIME) { + tronLastLocalDisconnectReason = null; + tronLastRemoteDisconnectReason = null; + disconnectTimes = 0; + persistedReputation = 0; + } + + if (tronLastLocalDisconnectReason == ReasonCode.NULL_IDENTITY || tronLastRemoteDisconnectReason == ReasonCode.NULL_IDENTITY || tronLastLocalDisconnectReason == ReasonCode.INCOMPATIBLE_PROTOCOL || tronLastRemoteDisconnectReason == ReasonCode.INCOMPATIBLE_PROTOCOL || @@ -163,26 +174,38 @@ public boolean isReputationPenalized() { tronLastLocalDisconnectReason == ReasonCode.INCOMPATIBLE_VERSION || tronLastRemoteDisconnectReason == ReasonCode.INCOMPATIBLE_VERSION || tronLastLocalDisconnectReason == ReasonCode.INCOMPATIBLE_CHAIN || - tronLastRemoteDisconnectReason == ReasonCode.INCOMPATIBLE_CHAIN; + tronLastRemoteDisconnectReason == ReasonCode.INCOMPATIBLE_CHAIN || + tronLastRemoteDisconnectReason == ReasonCode.SYNC_FAIL || + tronLastLocalDisconnectReason == ReasonCode.SYNC_FAIL) { + persistedReputation = 0; + return true; + } + return false; } public boolean isPenalized() { return tronLastLocalDisconnectReason == ReasonCode.NULL_IDENTITY || - tronLastRemoteDisconnectReason == ReasonCode.NULL_IDENTITY || - tronLastLocalDisconnectReason == ReasonCode.BAD_PROTOCOL || - tronLastRemoteDisconnectReason == ReasonCode.BAD_PROTOCOL; + tronLastRemoteDisconnectReason == ReasonCode.NULL_IDENTITY || + tronLastLocalDisconnectReason == ReasonCode.BAD_PROTOCOL || + tronLastRemoteDisconnectReason == ReasonCode.BAD_PROTOCOL || + tronLastLocalDisconnectReason == ReasonCode.SYNC_FAIL || + tronLastRemoteDisconnectReason == ReasonCode.SYNC_FAIL; } public void nodeDisconnectedRemote(ReasonCode reason) { lastDisconnectedTime = System.currentTimeMillis(); tronLastRemoteDisconnectReason = reason; - disconnectTimes++; } public void nodeDisconnectedLocal(ReasonCode reason) { lastDisconnectedTime = System.currentTimeMillis(); tronLastLocalDisconnectReason = reason; + } + + public void notifyDisconnect() { + lastDisconnectedTime = System.currentTimeMillis(); disconnectTimes++; + persistedReputation = persistedReputation / 2; } public boolean wasDisconnected() { diff --git a/src/main/java/org/tron/common/overlay/message/Message.java b/src/main/java/org/tron/common/overlay/message/Message.java index e3a6b50ae55..33c20bad39e 100644 --- a/src/main/java/org/tron/common/overlay/message/Message.java +++ b/src/main/java/org/tron/common/overlay/message/Message.java @@ -9,10 +9,9 @@ import org.tron.common.utils.Sha256Hash; import org.tron.core.net.message.MessageTypes; - public abstract class Message { - protected static final Logger logger = LoggerFactory.getLogger("Net"); + protected static final Logger logger = LoggerFactory.getLogger("Message"); protected boolean unpacked; protected byte[] data; @@ -32,7 +31,6 @@ public Message(byte type, byte[] packed) { unpacked = false; } - public ByteBuf getSendData(){ return Unpooled.wrappedBuffer(ArrayUtils.add(this.getData(), 0 ,type)); } @@ -41,13 +39,25 @@ public Sha256Hash getMessageId() { return Sha256Hash.of(getData()); } - public abstract byte[] getData(); + public byte[] getData(){ + return this.data; + } + + public MessageTypes getType(){ + return MessageTypes.fromByte(this.type); + } + + public abstract Class getAnswerMessage(); + @Override public String toString() { return "[Message Type: " + getType() + ", Message Hash: " + getMessageId() + "]"; } - public abstract Class getAnswerMessage(); + @Override + public int hashCode() { + return Arrays.hashCode(data); + } @Override public boolean equals(Object o) { @@ -60,12 +70,4 @@ public boolean equals(Object o) { Message message = (Message) o; return Arrays.equals(data, message.data); } - - @Override - public int hashCode() { - return Arrays.hashCode(data); - } - - public abstract MessageTypes getType(); - } \ No newline at end of file diff --git a/src/main/java/org/tron/common/overlay/message/MessageFactory.java b/src/main/java/org/tron/common/overlay/message/MessageFactory.java index 431d227ce65..bb02f375980 100644 --- a/src/main/java/org/tron/common/overlay/message/MessageFactory.java +++ b/src/main/java/org/tron/common/overlay/message/MessageFactory.java @@ -16,9 +16,6 @@ public abstract class MessageFactory { - public static String ERR_NO_SUCH_MSG = "No such message"; - public static String ERR_PARSE_FAILED = "parse message failed"; - protected abstract Message create(byte[] data) throws Exception; } diff --git a/src/main/java/org/tron/common/overlay/message/ReasonCode.java b/src/main/java/org/tron/common/overlay/message/ReasonCode.java index 2d79a754e76..c35de758a2b 100644 --- a/src/main/java/org/tron/common/overlay/message/ReasonCode.java +++ b/src/main/java/org/tron/common/overlay/message/ReasonCode.java @@ -89,6 +89,8 @@ public enum ReasonCode { TIME_OUT(0x20), + CONNECT_FAIL(0x21), + /** * [0xFF] Reason not specified */ diff --git a/src/main/java/org/tron/common/overlay/server/Channel.java b/src/main/java/org/tron/common/overlay/server/Channel.java index 70f15f52a40..32b421be8cc 100644 --- a/src/main/java/org/tron/common/overlay/server/Channel.java +++ b/src/main/java/org/tron/common/overlay/server/Channel.java @@ -162,7 +162,7 @@ public void processException(Throwable throwable){ if (throwable instanceof ReadTimeoutException){ logger.error("Read timeout, {}", address); }else if(baseThrowable instanceof P2pException){ - logger.error("type: {}, info: {}, {}", ((P2pException) throwable).getType(), errMsg, address); + logger.error("type: {}, info: {}, {}", ((P2pException) baseThrowable).getType(), errMsg, address); }else if (errMsg != null && errMsg.contains("Connection reset by peer")){ logger.error("{}, {}", errMsg, address); }else { diff --git a/src/main/java/org/tron/common/overlay/server/ChannelManager.java b/src/main/java/org/tron/common/overlay/server/ChannelManager.java index 6ee3b78f323..04805421cc9 100644 --- a/src/main/java/org/tron/common/overlay/server/ChannelManager.java +++ b/src/main/java/org/tron/common/overlay/server/ChannelManager.java @@ -150,6 +150,7 @@ public void notifyDisconnect(Channel channel) { || channel.getChannelHandlerContext().channel() == null) { return; } + channel.getNodeStatistics().notifyDisconnect(); InetSocketAddress socketAddress = (InetSocketAddress) channel.getChannelHandlerContext() .channel().remoteAddress(); recentlyDisconnected.put(socketAddress.getAddress(), new Date()); diff --git a/src/main/java/org/tron/common/overlay/server/MessageQueue.java b/src/main/java/org/tron/common/overlay/server/MessageQueue.java index de25643a107..bcae990f558 100644 --- a/src/main/java/org/tron/common/overlay/server/MessageQueue.java +++ b/src/main/java/org/tron/common/overlay/server/MessageQueue.java @@ -26,7 +26,7 @@ public class MessageQueue { private static final Logger logger = LoggerFactory.getLogger("MessageQueue"); - private boolean sendMsgFlag = false; + private volatile boolean sendMsgFlag = false; private Thread sendMsgThread; @@ -70,7 +70,7 @@ public void activate(ChannelHandlerContext ctx) { Message msg = msgQueue.take(); ctx.writeAndFlush(msg.getSendData()).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); }catch (Exception e) { - logger.error("send message failed, {}, error info: {}", ctx.channel().remoteAddress(), e.getMessage()); + logger.error("Send message failed, {}, error info: {}", ctx.channel().remoteAddress(), e.getMessage()); } } }); @@ -102,6 +102,15 @@ public void close() { sendMsgFlag = false; if(sendTask != null && !sendTask.isCancelled()){ sendTask.cancel(false); + sendTask = null; + } + if (sendMsgThread != null){ + try{ + sendMsgThread.join(20); + sendMsgThread = null; + }catch (Exception e){ + logger.warn("Join send thread failed, peer {}", ctx.channel().remoteAddress()); + } } } diff --git a/src/main/java/org/tron/common/overlay/server/SyncPool.java b/src/main/java/org/tron/common/overlay/server/SyncPool.java index a284ba8f6ea..404ad071fdd 100644 --- a/src/main/java/org/tron/common/overlay/server/SyncPool.java +++ b/src/main/java/org/tron/common/overlay/server/SyncPool.java @@ -102,8 +102,7 @@ private void fillUp() { nodesInUse.add(nodeManager.getPublicHomeNode().getHexId()); List newNodes = nodeManager.getNodes(new NodeSelector(nodesInUse), lackSize); - newNodes.forEach(n -> peerClient.connectAsync(n.getNode().getHost(), n.getNode().getPort(), - n.getNode().getHexId(), false)); + newNodes.forEach(n -> peerClient.connectAsync(n, false)); } // for test only diff --git a/src/main/java/org/tron/common/utils/ByteUtil.java b/src/main/java/org/tron/common/utils/ByteUtil.java index 54409846778..255ecf85c1f 100644 --- a/src/main/java/org/tron/common/utils/ByteUtil.java +++ b/src/main/java/org/tron/common/utils/ByteUtil.java @@ -155,10 +155,6 @@ public static int byteArrayToInt(byte[] b) { return new BigInteger(1, b).intValue(); } - public static boolean isNullOrZeroArray(byte[] array) { - return (array == null) || (array.length == 0); - } - public static boolean isSingleZero(byte[] array) { return (array.length == 1 && array[0] == 0); } diff --git a/src/main/java/org/tron/core/Wallet.java b/src/main/java/org/tron/core/Wallet.java index be38e5cfcdc..f45b43c0e2b 100755 --- a/src/main/java/org/tron/core/Wallet.java +++ b/src/main/java/org/tron/core/Wallet.java @@ -23,6 +23,7 @@ import java.util.Objects; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -38,7 +39,6 @@ import org.tron.common.overlay.message.Message; import org.tron.common.utils.Base58; import org.tron.common.utils.ByteArray; -import org.tron.common.utils.ByteUtil; import org.tron.common.utils.Utils; import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.AssetIssueCapsule; @@ -112,7 +112,7 @@ public static void setAddressPreFixByte(byte addressPreFixByte) { } public static boolean addressValid(byte[] address) { - if (ByteUtil.isNullOrZeroArray(address)) { + if (ArrayUtils.isEmpty(address)) { logger.warn("Warning: Address is empty !!"); return false; } diff --git a/src/main/java/org/tron/core/actuator/TransferActuator.java b/src/main/java/org/tron/core/actuator/TransferActuator.java index f79351f02e3..3ebefb9d5f4 100755 --- a/src/main/java/org/tron/core/actuator/TransferActuator.java +++ b/src/main/java/org/tron/core/actuator/TransferActuator.java @@ -44,6 +44,14 @@ public boolean execute(TransactionResultCapsule ret) throws ContractExeException long fee = calcFee(); try { + // if account with to_address is not existed, create it. + AccountCapsule toAccount = dbManager.getAccountStore() + .get(transferContract.getToAddress().toByteArray()); + if (toAccount == null) { + toAccount = new AccountCapsule(ByteString.copyFrom(toAddress), AccountType.Normal, + dbManager.getHeadBlockTimeStamp()); + dbManager.getAccountStore().put(toAddress, toAccount); + } dbManager.adjustBalance(transferContract.getOwnerAddress().toByteArray(), -calcFee()); ret.setStatus(fee, code.SUCESS); dbManager.adjustBalance(transferContract.getOwnerAddress().toByteArray(), @@ -113,9 +121,6 @@ public boolean validate() throws ContractValidateException { throw new ContractValidateException( "For a non-existent account transfer, the minimum amount is 1 TRX"); } - toAccount = new AccountCapsule(ByteString.copyFrom(toAddress), AccountType.Normal, - dbManager.getHeadBlockTimeStamp()); - dbManager.getAccountStore().put(toAddress, toAccount); } else { //check to account balance if overflow balance = Math.addExact(toAccount.getBalance(), amount); diff --git a/src/main/java/org/tron/core/actuator/TransferAssetActuator.java b/src/main/java/org/tron/core/actuator/TransferAssetActuator.java index fb89f6d09d1..bc67702fb3d 100644 --- a/src/main/java/org/tron/core/actuator/TransferAssetActuator.java +++ b/src/main/java/org/tron/core/actuator/TransferAssetActuator.java @@ -54,17 +54,17 @@ public boolean execute(TransactionResultCapsule ret) throws ContractExeException AccountStore accountStore = this.dbManager.getAccountStore(); byte[] ownerKey = transferAssetContract.getOwnerAddress().toByteArray(); byte[] toKey = transferAssetContract.getToAddress().toByteArray(); - ByteString assertName = transferAssetContract.getAssetName(); + ByteString assetName = transferAssetContract.getAssetName(); long amount = transferAssetContract.getAmount(); AccountCapsule ownerAccountCapsule = accountStore.get(ownerKey); - if (!ownerAccountCapsule.reduceAssetAmount(assertName, amount)) { + if (!ownerAccountCapsule.reduceAssetAmount(assetName, amount)) { throw new ContractExeException("reduceAssetAmount failed !"); } accountStore.put(ownerKey, ownerAccountCapsule); AccountCapsule toAccountCapsule = accountStore.get(toKey); - toAccountCapsule.addAssetAmount(assertName, amount); + toAccountCapsule.addAssetAmount(assetName, amount); accountStore.put(toKey, toAccountCapsule); ret.setStatus(fee, code.SUCESS); diff --git a/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java b/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java index 4121fd182cf..d4694922561 100755 --- a/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java +++ b/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java @@ -10,6 +10,7 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.TransactionResultCapsule; import org.tron.core.capsule.WitnessCapsule; +import org.tron.core.capsule.utils.TransactionUtil; import org.tron.core.db.Manager; import org.tron.core.exception.BalanceInsufficientException; import org.tron.core.exception.ContractExeException; @@ -55,6 +56,11 @@ public boolean validate() throws ContractValidateException { if (!Wallet.addressValid(contract.getOwnerAddress().toByteArray())) { throw new ContractValidateException("Invalidate address"); } + + if (!TransactionUtil.validUrl(contract.getUrl().toByteArray())) { + throw new ContractValidateException("Invalidate url"); + } + Preconditions.checkArgument( this.dbManager.getAccountStore().has(contract.getOwnerAddress().toByteArray()), "account[" + readableOwnerAddress + "] not exists"); @@ -70,7 +76,6 @@ public boolean validate() throws ContractValidateException { accountCapsule.getBalance() >= dbManager.getDynamicPropertiesStore() .getAccountUpgradeCost(), "balance < AccountUpgradeCost"); - } catch (final Exception ex) { ex.printStackTrace(); throw new ContractValidateException(ex.getMessage()); @@ -112,8 +117,5 @@ private void createWitness(final WitnessCreateContract witnessCreateContract) { } catch (BalanceInsufficientException e) { throw new RuntimeException(e); } - - } - } diff --git a/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java b/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java index 68ecec6aa8f..5f6a903ed82 100644 --- a/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java +++ b/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java @@ -17,7 +17,7 @@ import com.google.protobuf.ByteString; import lombok.extern.slf4j.Slf4j; -import org.tron.common.utils.ByteUtil; +import org.apache.commons.lang3.ArrayUtils; import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; import org.tron.protos.Contract.TransferContract; @@ -51,7 +51,7 @@ private static boolean checkBalance(long totalBalance, long totalSpent) { } public static boolean validAccountName(byte[] accountName) { - if (ByteUtil.isNullOrZeroArray(accountName)) { + if (ArrayUtils.isEmpty(accountName)) { return false; } if (accountName.length > 32) { @@ -68,6 +68,17 @@ public static boolean validAccountName(byte[] accountName) { } return true; } + + public static boolean validUrl(byte[] url) { + if (ArrayUtils.isEmpty(url)) { + return false; + } + if (url.length > 256) { + return false; + } + // other rules. + return true; + } /** * Get sender. */ diff --git a/src/main/java/org/tron/core/db/DynamicPropertiesStore.java b/src/main/java/org/tron/core/db/DynamicPropertiesStore.java index de194d08cfb..6ab364438a0 100755 --- a/src/main/java/org/tron/core/db/DynamicPropertiesStore.java +++ b/src/main/java/org/tron/core/db/DynamicPropertiesStore.java @@ -61,6 +61,8 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] NON_EXISTENT_ACCOUNT_TRANSFER_MIN = "NON_EXISTENT_ACCOUNT_TRANSFER_MIN" .getBytes(); + private static final byte[] OPERATING_TIME_INTERVAL = "OPERATING_TIME_INTERVAL".getBytes(); + @Autowired private DynamicPropertiesStore(@Qualifier("properties") String dbName) { super(dbName); @@ -172,6 +174,14 @@ private DynamicPropertiesStore(@Qualifier("properties") String dbName) { this.saveNonExistentAccountTransferLimit(1_000_000L); } + + try { + this.getOperatingTimeInterval(); + } catch (IllegalArgumentException e) { + this.saveOperatingTimeInterval(10_000L); + } + + try { this.getBlockFilledSlotsNumber(); } catch (IllegalArgumentException e) { @@ -402,6 +412,22 @@ public long getNonExistentAccountTransferMin() { () -> new IllegalArgumentException("not found NON_EXISTENT_ACCOUNT_TRANSFER_MIN")); } + + public void saveOperatingTimeInterval(long time) { + logger.debug("NON_EXISTENT_ACCOUNT_TRANSFER_MIN:" + time); + this.put(OPERATING_TIME_INTERVAL, + new BytesCapsule(ByteArray.fromLong(time))); + } + + public long getOperatingTimeInterval() { + return Optional.ofNullable(this.dbSource.getData(OPERATING_TIME_INTERVAL)) + .map(ByteArray::toLong) + .orElseThrow( + () -> new IllegalArgumentException("not found OPERATING_TIME_INTERVAL")); + } + + + public void saveBlockFilledSlots(int[] blockFilledSlots) { logger.debug("blockFilledSlots:" + intArrayToString(blockFilledSlots)); this.put(BLOCK_FILLED_SLOTS, diff --git a/src/main/java/org/tron/core/db/Manager.java b/src/main/java/org/tron/core/db/Manager.java index 739952fc6f3..f2f73df2663 100644 --- a/src/main/java/org/tron/core/db/Manager.java +++ b/src/main/java/org/tron/core/db/Manager.java @@ -33,6 +33,7 @@ import org.tron.core.actuator.Actuator; import org.tron.core.actuator.ActuatorFactory; import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.AssetIssueCapsule; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.capsule.BytesCapsule; @@ -59,6 +60,7 @@ import org.tron.core.exception.ValidateScheduleException; import org.tron.core.exception.ValidateSignatureException; import org.tron.core.witness.WitnessController; +import org.tron.protos.Contract.TransferAssetContract; import org.tron.protos.Protocol.AccountType; import org.tron.protos.Protocol.Transaction; @@ -453,7 +455,7 @@ public boolean pushTransactions(final TransactionCapsule trx) } - public void consumeBandwidth(TransactionCapsule trx) throws ValidateBandwidthException { + private void consumeBandwidth(TransactionCapsule trx) throws ValidateBandwidthException { List contracts = trx.getInstance().getRawData().getContractList(); for (Transaction.Contract contract : contracts) { @@ -462,20 +464,41 @@ public void consumeBandwidth(TransactionCapsule trx) throws ValidateBandwidthExc if (accountCapsule == null) { throw new ValidateBandwidthException("account not exists"); } - long bandwidth = accountCapsule.getBandwidth(); long now = getHeadBlockTimeStamp(); long latestOperationTime = accountCapsule.getLatestOperationTime(); //10 * 1000 - if (now - latestOperationTime >= 10_000L) { + if (now - latestOperationTime >= dynamicPropertiesStore.getOperatingTimeInterval()) { accountCapsule.setLatestOperationTime(now); this.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); return; } long bandwidthPerTransaction = getDynamicPropertiesStore().getBandwidthPerTransaction(); - if (bandwidth < bandwidthPerTransaction) { - throw new ValidateBandwidthException("bandwidth is not enough"); + long bandwidth; + if (contract.getType() == TransferAssetContract) { + AccountCapsule issuerAccountCapsule; + try { + ByteString assetName + = contract.getParameter().unpack(TransferAssetContract.class).getAssetName(); + AssetIssueCapsule assetIssueCapsule + = this.getAssetIssueStore().get(assetName.toByteArray()); + issuerAccountCapsule = this.getAccountStore() + .get(assetIssueCapsule.getOwnerAddress().toByteArray()); + bandwidth = issuerAccountCapsule.getBandwidth(); + } catch (Exception ex) { + throw new ValidateBandwidthException(ex.getMessage()); + } + if (bandwidth < bandwidthPerTransaction) { + throw new ValidateBandwidthException("bandwidth is not enough"); + } + issuerAccountCapsule.setBandwidth(bandwidth - bandwidthPerTransaction); + this.getAccountStore().put(issuerAccountCapsule.createDbKey(), issuerAccountCapsule); + } else { + bandwidth = accountCapsule.getBandwidth(); + if (bandwidth < bandwidthPerTransaction) { + throw new ValidateBandwidthException("bandwidth is not enough"); + } + accountCapsule.setBandwidth(bandwidth - bandwidthPerTransaction); } - accountCapsule.setBandwidth(bandwidth - bandwidthPerTransaction); accountCapsule.setLatestOperationTime(now); this.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule); } diff --git a/src/main/java/org/tron/core/net/message/TransactionsMessage.java b/src/main/java/org/tron/core/net/message/TransactionsMessage.java index 690b7de9dba..5e7417c9fb4 100644 --- a/src/main/java/org/tron/core/net/message/TransactionsMessage.java +++ b/src/main/java/org/tron/core/net/message/TransactionsMessage.java @@ -1,41 +1,36 @@ package org.tron.core.net.message; -import com.google.protobuf.InvalidProtocolBufferException; -import java.util.ArrayList; import java.util.List; -import org.tron.protos.Protocol.Items; + +import org.tron.core.exception.P2pException; +import org.tron.protos.Protocol; import org.tron.protos.Protocol.Transaction; public class TransactionsMessage extends TronMessage { - private List trxs = new ArrayList(); + private Protocol.Transactions transactions; public TransactionsMessage(List trxs) { - this.trxs = trxs; - unpacked = true; - this.type = MessageTypes.TRXS.asByte(); - } - - public TransactionsMessage(byte[] packed) { - super(packed); + Protocol.Transactions.Builder builder = Protocol.Transactions.newBuilder(); + trxs.forEach(trx -> builder.addTransactions(trx)); + this.transactions = builder.build(); this.type = MessageTypes.TRXS.asByte(); + this.data = this.transactions.toByteArray(); } - public TransactionsMessage() { + public TransactionsMessage(byte[] data) throws Exception{ this.type = MessageTypes.TRXS.asByte(); + this.data = data; + this.transactions = Protocol.Transactions.parseFrom(data); } - @Override - public byte[] getData() { - if (data == null) { - pack(); - } - return data; + public Protocol.Transactions getTransactions() { + return transactions; } @Override public String toString() { - return null; + return "trx_size:" + this.transactions.getTransactionsList().size(); } @Override @@ -43,36 +38,4 @@ public Class getAnswerMessage() { return null; } - @Override - public MessageTypes getType() { - return MessageTypes.fromByte(this.type); - } - - public List getTransactions() { - unPack(); - return trxs; - } - - private void pack() { - Items.Builder itemsBuilder = Items.newBuilder(); - itemsBuilder.setType(Items.ItemType.TRX); - itemsBuilder.addAllTransactions(this.trxs); - this.data = itemsBuilder.build().toByteArray(); - } - - private synchronized void unPack() { - if (unpacked) { - return; - } - try { - Items items = Items.parseFrom(data); - if (items.getType() == Items.ItemType.TRX) { - trxs = items.getTransactionsList(); - } - } catch (InvalidProtocolBufferException e) { - logger.debug(e.getMessage()); - } - - unpacked = true; - } } diff --git a/src/main/java/org/tron/core/net/message/TronMessageFactory.java b/src/main/java/org/tron/core/net/message/TronMessageFactory.java index 38bf1737f59..4d9aba5ec80 100644 --- a/src/main/java/org/tron/core/net/message/TronMessageFactory.java +++ b/src/main/java/org/tron/core/net/message/TronMessageFactory.java @@ -27,7 +27,7 @@ public TronMessage create(byte[] data) throws Exception{ private TronMessage create(byte type, byte[] packed) throws Exception{ MessageTypes receivedTypes = MessageTypes.fromByte(type); if (receivedTypes == null){ - throw new RuntimeException(MessageFactory.ERR_NO_SUCH_MSG + ", type=" + type); + throw new P2pException(P2pException.TypeEnum.NO_SUCH_MESSAGE, "type=" + type); } switch (receivedTypes) { case TRX: diff --git a/src/main/java/org/tron/core/net/node/NodeImpl.java b/src/main/java/org/tron/core/net/node/NodeImpl.java index 9162af06788..263113bd0a4 100644 --- a/src/main/java/org/tron/core/net/node/NodeImpl.java +++ b/src/main/java/org/tron/core/net/node/NodeImpl.java @@ -8,6 +8,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import io.netty.util.internal.ConcurrentSet; import java.util.*; @@ -40,24 +41,17 @@ import org.tron.core.config.Parameter.ChainConstant; import org.tron.core.config.Parameter.NetConstants; import org.tron.core.config.Parameter.NodeConstant; +import org.tron.core.db.api.pojo.Transaction; import org.tron.core.exception.BadBlockException; import org.tron.core.exception.BadTransactionException; import org.tron.core.exception.StoreException; import org.tron.core.exception.TraitorPeerException; import org.tron.core.exception.TronException; import org.tron.core.exception.UnLinkedBlockException; -import org.tron.core.net.message.BlockInventoryMessage; -import org.tron.core.net.message.BlockMessage; -import org.tron.core.net.message.ChainInventoryMessage; -import org.tron.core.net.message.FetchInvDataMessage; -import org.tron.core.net.message.InventoryMessage; -import org.tron.core.net.message.ItemNotFound; -import org.tron.core.net.message.MessageTypes; -import org.tron.core.net.message.SyncBlockChainMessage; -import org.tron.core.net.message.TransactionMessage; -import org.tron.core.net.message.TronMessage; +import org.tron.core.net.message.*; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerConnectionDelegate; +import org.tron.protos.Protocol; import org.tron.protos.Protocol.Inventory.InventoryType; @Slf4j @@ -67,14 +61,18 @@ public class NodeImpl extends PeerConnectionDelegate implements Node { @Autowired private SyncPool pool; - Cache TrxCache = CacheBuilder.newBuilder() + private Cache TrxCache = CacheBuilder.newBuilder() .maximumSize(10000).expireAfterWrite(600, TimeUnit.SECONDS) .recordStats().build(); - Cache BlockCache = CacheBuilder.newBuilder() + private Cache BlockCache = CacheBuilder.newBuilder() .maximumSize(10).expireAfterWrite(60, TimeUnit.SECONDS) .recordStats().build(); + private int maxTrxsSize = 1_000_000; + + private int maxTrxsCnt = 100; + class InvToSend { private HashMap>> send @@ -118,8 +116,6 @@ void sendFetch() { } } - - private ScheduledExecutorService logExecutor = Executors.newSingleThreadScheduledExecutor(); //public @@ -213,6 +209,8 @@ public void onMessage(PeerConnection peer, TronMessage msg) { break; case TRX: onHandleTransactionMessage(peer, (TransactionMessage) msg); + case TRXS: + onHandleTransactionsMessage(peer, (TransactionsMessage) msg); break; case SYNC_BLOCK_CHAIN: onHandleSyncBlockChainMessage(peer, (SyncBlockChainMessage) msg); @@ -768,6 +766,13 @@ private void onHandleTransactionMessage(PeerConnection peer, TransactionMessage } } + private void onHandleTransactionsMessage(PeerConnection peer, TransactionsMessage msg){ + logger.info("onHandleTransactionsMessage, size = {}, peer {}", + msg.getTransactions().getTransactionsList().size(), peer.getNode().getHost()); + msg.getTransactions().getTransactionsList().forEach(transaction -> + onHandleTransactionMessage(peer, new TransactionMessage(transaction))); + } + private void onHandleSyncBlockChainMessage(PeerConnection peer, SyncBlockChainMessage syncMsg) { //logger.info("on handle sync block chain message"); peer.setTronState(TronState.SYNCING); @@ -818,6 +823,10 @@ private void onHandleFetchDataMessage(PeerConnection peer, FetchInvDataMessage f BlockCapsule block = null; + List transactions = Lists.newArrayList(); + + int size = 0; + for (Sha256Hash hash : fetchInvDataMsg.getHashList()) { Message msg; @@ -832,20 +841,32 @@ private void onHandleFetchDataMessage(PeerConnection peer, FetchInvDataMessage f msg = del.getData(hash, type); } - if (msg != null) { - if (type.equals(MessageTypes.BLOCK)) { - block = ((BlockMessage) msg).getBlockCapsule(); - } - peer.sendMessage(msg); - } else { + if (msg == null){ logger.error("fetch message {} {} failed.", type, hash); peer.sendMessage(new ItemNotFound()); + return; + } + + if (type.equals(MessageTypes.BLOCK)) { + block = ((BlockMessage) msg).getBlockCapsule(); + peer.sendMessage(msg); + }else { + transactions.add(((TransactionMessage)msg).getTransaction()); + size += ((TransactionMessage)msg).getTransaction().getSerializedSize(); + if (transactions.size() % maxTrxsCnt == 0 || size > maxTrxsSize) { + peer.sendMessage(new TransactionsMessage(transactions)); + transactions = Lists.newArrayList(); + size = 0; + } } } if (block != null) { updateBlockWeBothHave(peer, block); } + if (transactions.size() > 0){ + peer.sendMessage(new TransactionsMessage(transactions)); + } } private void banTraitorPeer(PeerConnection peer, ReasonCode reason) { diff --git a/src/main/protos/core/Tron.proto b/src/main/protos/core/Tron.proto index c24f8e9a783..069db511ec8 100644 --- a/src/main/protos/core/Tron.proto +++ b/src/main/protos/core/Tron.proto @@ -177,6 +177,10 @@ message Transaction { repeated Result ret = 5; } +message Transactions { + repeated Transaction transactions = 1; +} + message BlockHeader { message raw { int64 timestamp = 1; diff --git a/src/main/resources/config.conf b/src/main/resources/config.conf index d529301a5ac..2130bf066b2 100644 --- a/src/main/resources/config.conf +++ b/src/main/resources/config.conf @@ -108,57 +108,57 @@ genesis.block = { { address: 27cEZa99jVaDkujPwzZuHYgkYNqv6zzYLSP url = "http://Mercury.org", - voteCount = 0 + voteCount = 105 }, { address: 27anh4TDZJGYpsn4BjXzb7uEArNALxwiZZW url = "http://Venus.org", - voteCount = 0 + voteCount = 104 }, { address: 27Wkfa5iEJtsKAKdDzSmF1b2gDm5s49kvdZ url = "http://Earth.org", - voteCount = 0 + voteCount = 103 }, { address: 27bqKYX9Bgv7dgTY7xBw5SUHZ8EGaPSikjx url = "http://Mars.org", - voteCount = 0 + voteCount = 102 }, { address: 27fASUY6qKtsaAEPz6QxhZac2KYVz2ZRTXW url = "http://Jupiter.org", - voteCount = 0 + voteCount = 101 }, { address: 27Q3RSbiqm59VXcF8shQWHKbyztfso5FwvP url = "http://Saturn.org", - voteCount = 0 + voteCount = 100 }, { address: 27YkUVSuvCK3K84DbnFnxYUxozpi793PTqZ url = "http://Uranus.org", - voteCount = 0 + voteCount = 99 }, { address: 27kdTBTDJ16hK3Xqr8PpCuQJmje1b94CDJU url = "http://Neptune.org", - voteCount = 0 + voteCount = 98 }, { address: 27mw9UpRy7inTMQ5kUzsdTc2QZ6KvtCX4uB url = "http://Pluto.org", - voteCount = 0 + voteCount = 97 }, { address: 27QzC4PeQZJ2kFMUXiCo4S8dx3VWN5U9xcg url = "http://Altair.org", - voteCount = 0 + voteCount = 96 }, { address: 27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh url = "http://AlphaLyrae.org", - voteCount = 0 + voteCount = 95 } ] diff --git a/src/test/java/org/tron/core/actuator/TransferActuatorTest.java b/src/test/java/org/tron/core/actuator/TransferActuatorTest.java index 16761c70eb0..b88d3aabd9d 100755 --- a/src/test/java/org/tron/core/actuator/TransferActuatorTest.java +++ b/src/test/java/org/tron/core/actuator/TransferActuatorTest.java @@ -311,11 +311,10 @@ public void noExitToAccount() { .get(ByteArray.fromHexString(To_ACCOUNT_INVALIATE)); Assert.assertTrue(null == noExitAccount); actuator.validate(); + actuator.execute(ret); noExitAccount = dbManager.getAccountStore() .get(ByteArray.fromHexString(To_ACCOUNT_INVALIATE)); Assert.assertFalse(null == noExitAccount); //Had created. - Assert.assertEquals(noExitAccount.getBalance(), 0); - actuator.execute(ret); AccountCapsule owner = dbManager.getAccountStore() .get(ByteArray.fromHexString(OWNER_ADDRESS)); AccountCapsule toAccount = dbManager.getAccountStore() diff --git a/src/test/java/org/tron/core/actuator/WitnessCreateActuatorTest.java b/src/test/java/org/tron/core/actuator/WitnessCreateActuatorTest.java index 0904c7ca1c3..c161cea8bf7 100644 --- a/src/test/java/org/tron/core/actuator/WitnessCreateActuatorTest.java +++ b/src/test/java/org/tron/core/actuator/WitnessCreateActuatorTest.java @@ -104,6 +104,14 @@ private Any getContract(String address, String url) { .build()); } + private Any getContract(String address, ByteString url) { + return Any.pack( + Contract.WitnessCreateContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(address))) + .setUrl(url) + .build()); + } + /** * first createWitness,result is success. */ @@ -142,11 +150,10 @@ public void secondCreateAccount() { try { actuator.validate(); actuator.execute(ret); - + Assert.assertFalse(true); } catch (ContractValidateException e) { Assert.assertTrue(e instanceof ContractValidateException); Assert.assertEquals("Witness[" + OWNER_ADDRESS_SECOND + "] has existed", e.getMessage()); - } catch (ContractExeException e) { Assert.assertFalse(e instanceof ContractExeException); } @@ -164,16 +171,86 @@ public void invalidateAddress() { actuator.validate(); actuator.execute(ret); fail("Invalidate address"); + } catch (ContractValidateException e) { + Assert.assertTrue(e instanceof ContractValidateException); + Assert.assertEquals("Invalidate address", e.getMessage()); + } catch (ContractExeException e) { + Assert.assertFalse(e instanceof ContractExeException); + } + } + /** + * use Invalidate url createWitness,result is failed,exception is "Invalidate url". + */ + @Test + public void invalidateUrlTest() { + TransactionResultCapsule ret = new TransactionResultCapsule(); + //Url cannot empty + try { + WitnessCreateActuator actuator = new WitnessCreateActuator( + getContract(OWNER_ADDRESS_FRIST, ByteString.EMPTY), dbManager); + actuator.validate(); + actuator.execute(ret); + fail("Invalidate url"); } catch (ContractValidateException e) { Assert.assertTrue(e instanceof ContractValidateException); + Assert.assertEquals("Invalidate url", e.getMessage()); + } catch (ContractExeException e) { + Assert.assertFalse(e instanceof ContractExeException); + } - Assert.assertEquals("Invalidate address", e.getMessage()); + //256 bytes + String url256Bytes = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + //Url length can not greater than 256 + try { + WitnessCreateActuator actuator = new WitnessCreateActuator( + getContract(OWNER_ADDRESS_FRIST, ByteString.copyFromUtf8(url256Bytes + "0")), dbManager); + actuator.validate(); + actuator.execute(ret); + fail("Invalidate url"); + } catch (ContractValidateException e) { + Assert.assertTrue(e instanceof ContractValidateException); + Assert.assertEquals("Invalidate url", e.getMessage()); + } catch (ContractExeException e) { + Assert.assertFalse(e instanceof ContractExeException); + } + // 1 byte url is ok. + try { + WitnessCreateActuator actuator = new WitnessCreateActuator( + getContract(OWNER_ADDRESS_FRIST, "0"), dbManager); + actuator.validate(); + actuator.execute(ret); + Assert.assertEquals(ret.getInstance().getRet(), code.SUCESS); + WitnessCapsule witnessCapsule = + dbManager.getWitnessStore().get(ByteArray.fromHexString(OWNER_ADDRESS_FRIST)); + Assert.assertNotNull(witnessCapsule); + Assert.assertEquals(witnessCapsule.getInstance().getUrl(), "0"); + Assert.assertTrue(true); + } catch (ContractValidateException e) { + Assert.assertFalse(e instanceof ContractValidateException); } catch (ContractExeException e) { Assert.assertFalse(e instanceof ContractExeException); } + dbManager.getWitnessStore().delete(ByteArray.fromHexString(OWNER_ADDRESS_FRIST)); + // 256 bytes url is ok. + try { + WitnessCreateActuator actuator = new WitnessCreateActuator( + getContract(OWNER_ADDRESS_FRIST, url256Bytes), dbManager); + actuator.validate(); + actuator.execute(ret); + Assert.assertEquals(ret.getInstance().getRet(), code.SUCESS); + WitnessCapsule witnessCapsule = + dbManager.getWitnessStore().get(ByteArray.fromHexString(OWNER_ADDRESS_FRIST)); + Assert.assertNotNull(witnessCapsule); + Assert.assertEquals(witnessCapsule.getInstance().getUrl(), url256Bytes); + Assert.assertTrue(true); + } catch (ContractValidateException e) { + Assert.assertFalse(e instanceof ContractValidateException); + } catch (ContractExeException e) { + Assert.assertFalse(e instanceof ContractExeException); + } } /** @@ -189,7 +266,6 @@ public void noAccount() { actuator.validate(); actuator.execute(ret); fail("account[+OWNER_ADDRESS_NOACCOUNT+] not exists"); - } catch (ContractValidateException e) { Assert.assertTrue(e instanceof ContractValidateException); Assert.assertEquals("account[" + OWNER_ADDRESS_NOACCOUNT + "] not exists", e.getMessage()); @@ -202,7 +278,7 @@ public void noAccount() { /** * use Account ,result is failed,exception is "account not exists". */ -// @Test + @Test public void balanceNotSufficient() { AccountCapsule balanceNotSufficientCapsule = new AccountCapsule( @@ -221,11 +297,9 @@ public void balanceNotSufficient() { actuator.execute(ret); fail("witnessAccount has balance[" + balanceNotSufficientCapsule.getBalance() + "] < MIN_BALANCE[100]"); - } catch (ContractValidateException e) { Assert.assertTrue(e instanceof ContractValidateException); - Assert.assertEquals("witnessAccount has balance[" + balanceNotSufficientCapsule.getBalance() - + "] < MIN_BALANCE[100]", e.getMessage()); + Assert.assertEquals("balance < AccountUpgradeCost", e.getMessage()); } catch (ContractExeException e) { Assert.assertFalse(e instanceof ContractExeException); }