Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
### Additions and Improvements
- Update log4j [#9131](https://github.com/hyperledger/besu/pull/9131)
- Expose new method to query hardfork by block number Plugin API [#9115](https://github.com/hyperledger/besu/pull/9115)
- Support loading multiple transaction selector plugins [#8743](https://github.com/hyperledger/besu/pull/9139)

#### Performance
- Add jmh benchmarks for some compute-related opcodes [#9069](https://github.com/hyperledger/besu/pull/9069)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright contributors to Besu.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.plugins;

import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;

import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.ServiceManager;
import org.hyperledger.besu.plugin.data.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext;

public abstract class AbstractTestTransactionSelectorPlugin implements BesuPlugin {
private ServiceManager serviceManager;

private final int pluginNum;
private final int preMultiple;
private final int postMultiple;

public AbstractTestTransactionSelectorPlugin(
final int pluginNum, final int preMultiple, final int postMultiple) {
this.pluginNum = pluginNum;
this.preMultiple = preMultiple;
this.postMultiple = postMultiple;
}

@Override
public void register(final ServiceManager serviceManager) {
this.serviceManager = serviceManager;
serviceManager
.getService(PicoCLIOptions.class)
.orElseThrow()
.addPicoCLIOptions("tx-selector" + pluginNum, this);
}

protected abstract boolean isEnabled();

@Override
public void beforeExternalServices() {
if (isEnabled()) {
serviceManager
.getService(TransactionSelectionService.class)
.orElseThrow()
.registerPluginTransactionSelectorFactory(
new PluginTransactionSelectorFactory() {
@Override
public PluginTransactionSelector create(
final SelectorsStateManager selectorsStateManager) {
return new PluginTransactionSelector() {

@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext) {
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.getValue()
.getAsBigInteger()
.longValue()
% preMultiple
== 0) {
return TransactionSelectionResult.invalid(
"value multiple of " + preMultiple);
}
return SELECTED;
}

@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult) {
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.getValue()
.getAsBigInteger()
.longValue()
% postMultiple
== 0) {
return TransactionSelectionResult.invalid(
"value multiple of " + postMultiple);
}
return SELECTED;
}
};
}
});
}
}

@Override
public void start() {}

@Override
public void stop() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;

import java.io.File;
import java.io.IOException;
Expand All @@ -33,6 +34,7 @@
import com.google.auto.service.AutoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;

@AutoService(BesuPlugin.class)
public class TestBlockchainServicePlugin implements BesuPlugin {
Expand All @@ -41,35 +43,46 @@ public class TestBlockchainServicePlugin implements BesuPlugin {
private ServiceManager serviceManager;
private File callbackDir;

@CommandLine.Option(names = "--plugin-blockchain-service-test-enabled")
boolean enabled = false;

@Override
public void register(final ServiceManager serviceManager) {
LOG.info("Registering TestBlockchainServicePlugin");
this.serviceManager = serviceManager;
serviceManager
.getService(PicoCLIOptions.class)
.orElseThrow()
.addPicoCLIOptions("blockchain-service", this);

callbackDir = new File(System.getProperty("besu.plugins.dir", "plugins"));
}

@Override
public void start() {
LOG.info("Starting TestBlockchainServicePlugin");
final var blockchainService = serviceManager.getService(BlockchainService.class).orElseThrow();

serviceManager
.getService(BesuEvents.class)
.orElseThrow()
.addBlockAddedListener(
addedBlockContext -> {
LOG.info("Block added: {}", addedBlockContext);
final var hardforkSeen =
queryHardfork(blockchainService, addedBlockContext.getBlockHeader());
seenHardforks.add(
queryHardfork(blockchainService, addedBlockContext.getBlockHeader()));
if (hardforkSeen.current.equals(HardforkId.MainnetHardforkId.LONDON)) {
LOG.info("Writing seen hardforks: {}", seenHardforks);
writeSeenHardforks();
}
});

seenHardforks.add(queryHardfork(blockchainService, blockchainService.getChainHeadHeader()));
if (enabled) {
LOG.info("Starting TestBlockchainServicePlugin");
final var blockchainService =
serviceManager.getService(BlockchainService.class).orElseThrow();

serviceManager
.getService(BesuEvents.class)
.orElseThrow()
.addBlockAddedListener(
addedBlockContext -> {
LOG.info("Block added: {}", addedBlockContext);
final var hardforkSeen =
queryHardfork(blockchainService, addedBlockContext.getBlockHeader());
seenHardforks.add(
queryHardfork(blockchainService, addedBlockContext.getBlockHeader()));
if (hardforkSeen.current.equals(HardforkId.MainnetHardforkId.LONDON)) {
LOG.info("Writing seen hardforks: {}", seenHardforks);
writeSeenHardforks();
}
});

seenHardforks.add(queryHardfork(blockchainService, blockchainService.getChainHeadHeader()));
}
}

private HardforkSeen queryHardfork(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright contributors to Besu.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.plugins;

import org.hyperledger.besu.plugin.BesuPlugin;

import com.google.auto.service.AutoService;
import picocli.CommandLine.Option;

@AutoService(BesuPlugin.class)
public class TestTransactionSelectorPlugin1 extends AbstractTestTransactionSelectorPlugin {

@Option(names = "--plugin-tx-selector1-test-enabled")
boolean enabled = false;

public TestTransactionSelectorPlugin1() {
super(1, 3, 5);
}

@Override
public boolean isEnabled() {
return enabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright contributors to Besu.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.plugins;

import org.hyperledger.besu.plugin.BesuPlugin;

import com.google.auto.service.AutoService;
import picocli.CommandLine.Option;

@AutoService(BesuPlugin.class)
public class TestTransactionSelectorPlugin2 extends AbstractTestTransactionSelectorPlugin {

@Option(names = "--plugin-tx-selector2-test-enabled")
boolean enabled = false;

public TestTransactionSelectorPlugin2() {
super(2, 7, 11);
}

@Override
public boolean isEnabled() {
return enabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void setUp() throws Exception {
besu.createQbftPluginsNode(
"pluginNode",
Collections.singletonList("testPlugins"),
Collections.singletonList("--plugin-tx-validator-test-enabled=true"),
Collections.singletonList("--plugin-blockchain-service-test-enabled=true"),
"DEBUG");

cluster.start(pluginNode);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright contributors to Besu.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.plugins;

import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;

import java.math.BigInteger;
import java.util.Collections;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class TransactionSelectorPluginTest extends AcceptanceTestBase {
private BesuNode node;

@BeforeEach
public void setUp() throws Exception {
node =
besu.createQbftPluginsNode(
"node",
Collections.singletonList("testPlugins"),
List.of(
"--plugin-tx-selector1-test-enabled=true",
"--plugin-tx-selector2-test-enabled=true"),
"DEBUG");
cluster.start(node);
}

@Test
public void transactionIsMined() {
final Account recipient = accounts.createAccount("recipient");

// selector plugins reject values that are multiple of: 3,5,7,11
final var transferTx =
accountTransactions.createTransfer(recipient, Amount.wei(BigInteger.ONE));

final var txHash = node.execute(transferTx);

node.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
}

@ParameterizedTest
// selector plugins reject amount that are multiple of: 3,5,7,11
@ValueSource(longs = {3, 5, 7, 11, 3 * 5})
public void badMultipleOfValueTransactionIsNotSelectedByPlugin(final long amount) {
final Account recipient = accounts.createAccount("recipient");

final var badTx =
accountTransactions.createTransfer(recipient, Amount.wei(BigInteger.valueOf(amount)));

final var sameSenderGoodTx =
accountTransactions.createTransfer(recipient, Amount.wei(BigInteger.ONE));

final var anotherSenderGoodTx =
accountTransactions.createTransfer(
accounts.getSecondaryBenefactor(), recipient, Amount.wei(BigInteger.ONE));

final var badHash = node.execute(badTx);
final var sameSenderGoodHash = node.execute(sameSenderGoodTx);
final var anotherSenderGoodHash = node.execute(anotherSenderGoodTx);

node.verify(eth.expectSuccessfulTransactionReceipt(anotherSenderGoodHash.toHexString()));
node.verify(eth.expectNoTransactionReceipt(badHash.toHexString()));
// good tx from the same sender is not mined due to the nonce gap
node.verify(eth.expectNoTransactionReceipt(sameSenderGoodHash.toHexString()));
}
}
Loading