mirror of https://github.com/hyperledger/besu
Implement max message size rather than limiting with fixed number of transactions (#1271)
* Implement max message size rather then cap with fixed number of transactions Adding transactions to the RLP until the message size exceeds the limit and then send that. * fix final variables * Update AbstractRLPOutput.java add javadoc * pr discussion put this factory method on LimitedTransactionsMessages rather than TransactionsMessage since it returns a LimitedTransactionsMessages. * SpotlessApply * fix PR discussion - simplify design - remove useless code * Update LimitedTransactionsMessages.java * fix PR discussion - simplify logic - add tests * Update AbstractRLPOutput.java * Update ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/messages/LimitedTransactionsMessages.java Co-Authored-By: abdelhamidbakhta <45264458+abdelhamidbakhta@users.noreply.github.com> * Update Transaction.java * fix PR discussion * fix PR discussion - add tests * Update BlockDataGenerator.java * Update LimitedTransactionsMessagesTest.java fix PR unit test * Update LimitedTransactionsMessagesTest.java * Update LimitedTransactionsMessagesTest.java Use LinkedHashSet to preserve order. * Update LimitedTransactionsMessagesTest.java Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
622a03350b
commit
fbf5db7828
@ -0,0 +1,69 @@ |
||||
/* |
||||
* Copyright 2019 ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.messages; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Transaction; |
||||
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
public final class LimitedTransactionsMessages { |
||||
|
||||
private static final int LIMIT = 1048576; |
||||
|
||||
private final TransactionsMessage transactionsMessage; |
||||
private final Set<Transaction> includedTransactions; |
||||
|
||||
public LimitedTransactionsMessages( |
||||
final TransactionsMessage transactionsMessage, final Set<Transaction> includedTransactions) { |
||||
this.transactionsMessage = transactionsMessage; |
||||
this.includedTransactions = includedTransactions; |
||||
} |
||||
|
||||
public static LimitedTransactionsMessages createLimited( |
||||
final Iterable<Transaction> transactions) { |
||||
final Set<Transaction> includedTransactions = new HashSet<>(); |
||||
final BytesValueRLPOutput message = new BytesValueRLPOutput(); |
||||
int messageSize = 0; |
||||
message.startList(); |
||||
for (final Transaction transaction : transactions) { |
||||
final BytesValueRLPOutput encodedTransaction = new BytesValueRLPOutput(); |
||||
transaction.writeTo(encodedTransaction); |
||||
BytesValue encodedBytes = encodedTransaction.encoded(); |
||||
// Break if individual transaction size exceeds limit
|
||||
if (encodedBytes.size() > LIMIT && (messageSize != 0)) { |
||||
break; |
||||
} |
||||
message.writeRLPUnsafe(encodedBytes); |
||||
includedTransactions.add(transaction); |
||||
// Check if last transaction to add to the message
|
||||
messageSize += encodedBytes.size(); |
||||
if (messageSize > LIMIT) { |
||||
break; |
||||
} |
||||
} |
||||
message.endList(); |
||||
return new LimitedTransactionsMessages( |
||||
new TransactionsMessage(message.encoded()), includedTransactions); |
||||
} |
||||
|
||||
public final TransactionsMessage getTransactionsMessage() { |
||||
return transactionsMessage; |
||||
} |
||||
|
||||
public final Set<Transaction> getIncludedTransactions() { |
||||
return includedTransactions; |
||||
} |
||||
} |
@ -0,0 +1,106 @@ |
||||
/* |
||||
* Copyright 2019 ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.messages; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator; |
||||
import tech.pegasys.pantheon.ethereum.core.Transaction; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
public class LimitedTransactionsMessagesTest { |
||||
|
||||
private static final int LIMIT = 1048576; |
||||
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator(); |
||||
private final Set<Transaction> sampleTxs = generator.transactions(1); |
||||
private final TransactionsMessage sampleTransactionMessages = |
||||
TransactionsMessage.create(sampleTxs); |
||||
private final LimitedTransactionsMessages sampleLimitedTransactionsMessages = |
||||
new LimitedTransactionsMessages(sampleTransactionMessages, sampleTxs); |
||||
|
||||
@Test |
||||
public void createLimited() { |
||||
final Set<Transaction> txs = generator.transactions(6000); |
||||
final LimitedTransactionsMessages firstMessage = LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(5219, firstMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(firstMessage.getIncludedTransactions()); |
||||
assertEquals(781, txs.size()); |
||||
final LimitedTransactionsMessages secondMessage = |
||||
LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(781, secondMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(secondMessage.getIncludedTransactions()); |
||||
assertEquals(0, txs.size()); |
||||
assertTrue( |
||||
(firstMessage.getTransactionsMessage().getSize() |
||||
+ secondMessage.getTransactionsMessage().getSize()) |
||||
< 2 * LIMIT); |
||||
} |
||||
|
||||
@Test |
||||
public void createLimitedWithFirstTransactionExceedingLimit() { |
||||
final Set<Transaction> txs = new HashSet<>(); |
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT - 180]))); |
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT - 180]))); |
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT - 180]))); |
||||
final LimitedTransactionsMessages firstMessage = LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(2, firstMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(firstMessage.getIncludedTransactions()); |
||||
assertEquals(1, txs.size()); |
||||
final LimitedTransactionsMessages secondMessage = |
||||
LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(1, secondMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(secondMessage.getIncludedTransactions()); |
||||
assertEquals(0, txs.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void createLimitedWithFirstTransactionExceedingLimit_2() { |
||||
final Set<Transaction> txs = new LinkedHashSet<>(); |
||||
|
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT - 180]))); |
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT + 100 - 180]))); |
||||
txs.add(generator.transaction(BytesValue.wrap(new byte[LIMIT - 180]))); |
||||
final LimitedTransactionsMessages firstMessage = LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(1, firstMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(firstMessage.getIncludedTransactions()); |
||||
assertEquals(2, txs.size()); |
||||
final LimitedTransactionsMessages secondMessage = |
||||
LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(1, secondMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(secondMessage.getIncludedTransactions()); |
||||
assertEquals(1, txs.size()); |
||||
final LimitedTransactionsMessages thirdMessage = LimitedTransactionsMessages.createLimited(txs); |
||||
assertEquals(1, thirdMessage.getIncludedTransactions().size()); |
||||
txs.removeAll(thirdMessage.getIncludedTransactions()); |
||||
assertEquals(0, txs.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void getTransactionsMessage() { |
||||
assertEquals( |
||||
sampleTransactionMessages, sampleLimitedTransactionsMessages.getTransactionsMessage()); |
||||
} |
||||
|
||||
@Test |
||||
public void getIncludedTransactions() { |
||||
assertEquals(sampleTxs, sampleLimitedTransactionsMessages.getIncludedTransactions()); |
||||
} |
||||
} |
Loading…
Reference in new issue