Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions framework/src/main/java/org/tron/core/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
import org.tron.core.exception.ItemNotFoundException;
import org.tron.core.exception.MaintenanceUnavailableException;
import org.tron.core.exception.NonUniqueObjectException;
import org.tron.core.exception.P2pException;
import org.tron.core.exception.PermissionException;
import org.tron.core.exception.SignatureFormatException;
import org.tron.core.exception.StoreException;
Expand Down Expand Up @@ -606,6 +607,18 @@ public GrpcAPI.Return broadcastTransaction(Transaction signedTransaction) {
return builder.setResult(false).setCode(response_code.TRANSACTION_EXPIRATION_ERROR)
.setMessage(ByteString.copyFromUtf8("Transaction expired"))
.build();
} catch (P2pException e) {
logger.info(BROADCAST_TRANS_FAILED, txID, e.getMessage());
if (e.getType() == P2pException.TypeEnum.BAD_MESSAGE) {
// Size cap check in new TransactionMessage(byte[]) above. Map back to
// the historical TOO_BIG_TRANSACTION_ERROR response code so callers
// that branch on it keep working after the check moved up from
// Manager.validateCommon.
return builder.setResult(false).setCode(response_code.TOO_BIG_TRANSACTION_ERROR)
.setMessage(ByteString.copyFromUtf8(e.getMessage())).build();
}
return builder.setResult(false).setCode(response_code.OTHER_ERROR)
.setMessage(ByteString.copyFromUtf8("Error: " + e.getMessage())).build();
} catch (Exception e) {
logger.warn("Broadcast transaction {} failed", txID, e);
return builder.setResult(false).setCode(response_code.OTHER_ERROR)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
package org.tron.core.net.message.adv;

import static org.tron.core.config.Parameter.ChainConstant.BLOCK_SIZE;

import com.google.protobuf.UnknownFieldSet;
import org.tron.common.overlay.message.Message;
import org.tron.common.utils.Sha256Hash;
import org.tron.core.Constant;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.BlockCapsule.BlockId;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.P2pException;
import org.tron.core.exception.P2pException.TypeEnum;
import org.tron.core.net.message.MessageTypes;
import org.tron.core.net.message.TronMessage;
import org.tron.protos.Protocol.Block;

public class BlockMessage extends TronMessage {

// Raw byte limit on the inbound wire frame. Matches the post-parse cap
// previously enforced in BlockMsgHandler (BLOCK_SIZE + ONE_THOUSAND).
static final int MAX_RAW_SIZE = BLOCK_SIZE + Constant.ONE_THOUSAND;

private BlockCapsule block;

public BlockMessage(byte[] data) throws Exception {
super(data);
if (data.length > MAX_RAW_SIZE) {
throw new P2pException(TypeEnum.BAD_MESSAGE, "block size over limit");
}
this.type = MessageTypes.BLOCK.asByte();
this.block = new BlockCapsule(getCodedInputStream(data));
Block parsed = Block.parseFrom(getCodedInputStream(data));
Block sanitized = sanitize(parsed);
this.block = new BlockCapsule(sanitized);
// Reuse the input bytes only when no canonicalization happened. parseFrom
// may strip unknown fields when shouldDiscardUnknownFields is set (via
// Message.isFilter()), so check that the parsed size still equals the
// raw input length.
this.data = (sanitized == parsed && parsed.getSerializedSize() == data.length)
? data : this.block.getData();
if (Message.isFilter()) {
Message.compareBytes(data, block.getInstance().toByteArray());
Message.compareBytes(data, this.data);
TransactionCapsule.validContractProto(block.getInstance().getTransactionsList());
}
}
Expand All @@ -28,6 +49,25 @@ public BlockMessage(BlockCapsule block) {
this.block = block;
}

private static Block sanitize(Block block) {
boolean blockHasUnknown = !block.getUnknownFields().asMap().isEmpty();
boolean headerHasUnknown = !block.getBlockHeader().getUnknownFields().asMap().isEmpty();
if (!blockHasUnknown && !headerHasUnknown) {
return block;
}
UnknownFieldSet empty = UnknownFieldSet.getDefaultInstance();
Block.Builder builder = block.toBuilder();
if (blockHasUnknown) {
builder.setUnknownFields(empty);
}
if (headerHasUnknown) {
builder.setBlockHeader(block.getBlockHeader().toBuilder()
.setUnknownFields(empty)
.build());
}
return builder.build();
}

public BlockId getBlockId() {
return getBlockCapsule().getBlockId();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.tron.core.net.message.adv;

import com.google.protobuf.UnknownFieldSet;
import org.tron.common.overlay.message.Message;
import org.tron.common.utils.Sha256Hash;
import org.tron.core.Constant;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.P2pException;
import org.tron.core.exception.P2pException.TypeEnum;
import org.tron.core.net.message.MessageTypes;
import org.tron.core.net.message.TronMessage;
import org.tron.protos.Protocol.Transaction;
Expand All @@ -12,12 +16,20 @@ public class TransactionMessage extends TronMessage {
private TransactionCapsule transactionCapsule;

public TransactionMessage(byte[] data) throws Exception {
super(data);
this.transactionCapsule = new TransactionCapsule(getCodedInputStream(data));
Transaction parsed = Transaction.parseFrom(getCodedInputStream(data));
checkSize(parsed);
Transaction sanitized = sanitize(parsed);
this.transactionCapsule = new TransactionCapsule(sanitized);
// Reuse the input bytes only when no canonicalization happened. parseFrom
// may also strip unknown fields when shouldDiscardUnknownFields is set
// (via Message.isFilter()), so check that the parsed size still equals
// the raw input length.
this.data = (sanitized == parsed && parsed.getSerializedSize() == data.length)
? data : this.transactionCapsule.getData();
this.type = MessageTypes.TRX.asByte();
if (Message.isFilter()) {
compareBytes(data, transactionCapsule.getInstance().toByteArray());
transactionCapsule
compareBytes(data, this.data);
TransactionCapsule
.validContractProto(transactionCapsule.getInstance().getRawData().getContract(0));
}
}
Expand All @@ -28,6 +40,39 @@ public TransactionMessage(Transaction trx) {
this.data = trx.toByteArray();
}

/**
* Mirror Manager.validateCommon's size check. Static helper, also invoked
* from {@link TransactionsMessage} for each tx in a batch.
*/
static void checkSize(Transaction transaction) throws P2pException {
TransactionCapsule capsule = new TransactionCapsule(transaction);
capsule.removeRedundantRet();
long generalBytesSize = capsule.getInstance().toBuilder().clearRet().build().getSerializedSize()
+ Constant.MAX_RESULT_SIZE_IN_TX + Constant.MAX_RESULT_SIZE_IN_TX;
if (generalBytesSize > Constant.TRANSACTION_MAX_BYTE_SIZE
|| capsule.getData().length > Constant.TRANSACTION_MAX_BYTE_SIZE) {
throw new P2pException(TypeEnum.BAD_MESSAGE, "transaction size over limit");
}
}

static Transaction sanitize(Transaction transaction) {
if (transaction.getUnknownFields().asMap().isEmpty()) {
return transaction;
}
return doSanitize(transaction);
}

/**
* Unconditional sanitize — strips Transaction top-level unknown fields.
* Callers that have already checked emptiness (e.g. {@link TransactionsMessage})
* skip {@link #sanitize}'s fast-path check and invoke this directly.
*/
static Transaction doSanitize(Transaction transaction) {
return transaction.toBuilder()
.setUnknownFields(UnknownFieldSet.getDefaultInstance())
.build();
}

@Override
public String toString() {
return new StringBuilder().append(super.toString())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.tron.core.net.message.adv;

import com.google.protobuf.UnknownFieldSet;
import java.util.List;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.P2pException;
import org.tron.core.net.message.MessageTypes;
import org.tron.core.net.message.TronMessage;
import org.tron.protos.Protocol;
Expand All @@ -20,15 +22,42 @@ public TransactionsMessage(List<Transaction> trxs) {
}

public TransactionsMessage(byte[] data) throws Exception {
super(data);
this.type = MessageTypes.TRXS.asByte();
this.transactions = Protocol.Transactions.parseFrom(getCodedInputStream(data));
Protocol.Transactions parsed = Protocol.Transactions.parseFrom(getCodedInputStream(data));
this.transactions = sanitize(parsed);
// Reuse the input bytes only when no canonicalization happened. parseFrom
// may strip unknown fields when shouldDiscardUnknownFields is set (via
// Message.isFilter()), so check that the parsed size still equals the
// raw input length.
this.data = (this.transactions == parsed && parsed.getSerializedSize() == data.length)
? data : this.transactions.toByteArray();
if (isFilter()) {
compareBytes(data, transactions.toByteArray());
compareBytes(data, this.data);
TransactionCapsule.validContractProto(transactions.getTransactionsList());
}
}

private static Protocol.Transactions sanitize(Protocol.Transactions raw)
throws P2pException {
Protocol.Transactions.Builder builder = null;
if (!raw.getUnknownFields().asMap().isEmpty()) {
builder = raw.toBuilder();
builder.setUnknownFields(UnknownFieldSet.getDefaultInstance());
}

for (int i = 0; i < raw.getTransactionsCount(); i++) {
Transaction tx = raw.getTransactions(i);
TransactionMessage.checkSize(tx);
if (!tx.getUnknownFields().asMap().isEmpty()) {
if (builder == null) {
builder = raw.toBuilder();
}
builder.setTransactions(i, TransactionMessage.doSanitize(tx));
}
}
return builder == null ? raw : builder.build();
}

public Protocol.Transactions getTransactions() {
return transactions;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package org.tron.core.net.messagehandler;

import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
import static org.tron.core.config.Parameter.ChainConstant.BLOCK_SIZE;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.common.prometheus.MetricKeys;
import org.tron.common.prometheus.Metrics;
import org.tron.core.Constant;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.BlockCapsule.BlockId;
import org.tron.core.config.args.Args;
Expand Down Expand Up @@ -51,8 +49,6 @@ public class BlockMsgHandler implements TronMsgHandler {
@Autowired
private WitnessProductBlockService witnessProductBlockService;

private int maxBlockSize = BLOCK_SIZE + Constant.ONE_THOUSAND;

private boolean fastForward = Args.getInstance().isFastForward();

@Override
Expand All @@ -62,11 +58,6 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep
BlockId blockId = blockMessage.getBlockId();

BlockCapsule blockCapsule = blockMessage.getBlockCapsule();
if (blockCapsule.getInstance().getSerializedSize() > maxBlockSize) {
logger.error("Receive bad block {} from peer {}, block size over limit",
blockMessage.getBlockId(), peer.getInetSocketAddress());
throw new P2pException(TypeEnum.BAD_MESSAGE, "block size over limit");
}
long gap = blockCapsule.getTimeStamp() - System.currentTimeMillis();
if (gap >= BLOCK_PRODUCED_INTERVAL) {
logger.error("Receive bad block {} from peer {}, block time error",
Expand Down
Loading
Loading