Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Denylist reload #101

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ public class LineaPluginTestBase extends AcceptanceTestBase {
public void setup() throws Exception {
minerNode =
createCliqueNodeWithExtraCliOptionsAndRpcApis(
"miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions(), Set.of("LINEA", "MINER"));
"miner1",
LINEA_CLIQUE_OPTIONS,
getTestCliOptions(),
Set.of("LINEA", "MINER", "PLUGINS"));
minerNode.setTransactionPoolConfiguration(
ImmutableTransactionPoolConfiguration.builder()
.from(TransactionPoolConfiguration.DEFAULT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright Consensys Software Inc.
*
* 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 linea.plugin.acc.test;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.tx.RawTransactionManager;
import org.web3j.utils.Convert;

public class TransactionPoolDenyListReloadTest extends LineaPluginTestBase {

private static final BigInteger GAS_PRICE = Convert.toWei("20", Convert.Unit.GWEI).toBigInteger();
private static final BigInteger GAS_LIMIT = BigInteger.valueOf(210000);
private static final BigInteger VALUE = BigInteger.ONE; // 1 wei

final Credentials notDenied = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
final Credentials willBeDenied = Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY);

@TempDir static Path tempDir;
static Path tempDenyList;

@Override
public List<String> getTestCliOptions() {
tempDenyList = tempDir.resolve("denyList.txt");
if (!Files.exists(tempDenyList)) {

try {
Files.createFile(tempDenyList);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return new TestCommandLineOptionsBuilder()
.set("--plugin-linea-deny-list-path=", tempDenyList.toString())
.build();
}

@Test
public void emptyDenyList() throws Exception {
final Web3j miner = minerNode.nodeRequests().eth();

RawTransactionManager transactionManager =
new RawTransactionManager(miner, willBeDenied, CHAIN_ID);
assertAddressAllowed(transactionManager, willBeDenied.getAddress());
}

@Test
public void emptyDenyList_thenDenySender_cannotAddTxToPool() throws Exception {
final Web3j miner = minerNode.nodeRequests().eth();
RawTransactionManager transactionManager =
new RawTransactionManager(miner, willBeDenied, CHAIN_ID);

assertAddressAllowed(transactionManager, willBeDenied.getAddress());

addAddressToDenyList(willBeDenied.getAddress());
reloadPluginConfig();

assertAddressNotAllowed(transactionManager, willBeDenied.getAddress());
}

private void addAddressToDenyList(final String address) throws IOException {
Files.writeString(tempDenyList, address);
}

private void assertAddressAllowed(
final RawTransactionManager transactionManager, final String address) throws IOException {
EthSendTransaction transactionResponse =
transactionManager.sendTransaction(GAS_PRICE, GAS_LIMIT, address, "", VALUE);
assertThat(transactionResponse.getTransactionHash()).isNotNull();
assertThat(transactionResponse.getError()).isNull();
}

private void assertAddressNotAllowed(
final RawTransactionManager transactionManager, final String address) throws IOException {
EthSendTransaction transactionResponse =
transactionManager.sendTransaction(GAS_PRICE, GAS_LIMIT, address, "", VALUE);

assertThat(transactionResponse.getTransactionHash()).isNull();
assertThat(transactionResponse.getError().getMessage())
.isEqualTo(
"sender "
+ address
+ " is blocked as appearing on the SDN or other legally prohibited list");
}

private void reloadPluginConfig() {
final var reqLinea = new ReloadPluginConfigRequest();
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
assertThat(respLinea).isEqualTo("Success");
}

static class ReloadPluginConfigRequest implements Transaction<String> {

public ReloadPluginConfigRequest() {}

@Override
public String execute(final NodeRequests nodeRequests) {
try {
// plugin name is class name
return new Request<>(
"plugins_reloadPluginConfig",
List.of(
"net.consensys.linea.sequencer.txpoolvalidation.LineaTransactionPoolValidatorPlugin"),
nodeRequests.getWeb3jService(),
ReloadPluginConfigResponse.class)
.send()
.getResult();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

static class ReloadPluginConfigResponse extends org.web3j.protocol.core.Response<String> {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public abstract class AbstractLineaRequiredPlugin extends AbstractLineaPrivateOp
public void register(final BesuContext context) {
super.register(context);
try {
log.info("Registering Linea plugin {}", this.getClass().getName());
log.info(
"Registering Linea plugin of type {} with name {}",
this.getClass().getName(),
this.getName());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if plugin name == class name this extra logging may not be useful


blockchainService =
context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

package net.consensys.linea.extradata;

import java.util.Optional;

import com.google.auto.service.AutoService;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.AbstractLineaRequiredPlugin;
Expand All @@ -29,15 +27,9 @@
@Slf4j
@AutoService(BesuPlugin.class)
public class LineaExtraDataPlugin extends AbstractLineaRequiredPlugin {
public static final String NAME = "linea";
private BesuContext besuContext;
private RpcEndpointService rpcEndpointService;

@Override
public Optional<String> getName() {
return Optional.of(NAME);
}

@Override
public void doRegister(final BesuContext context) {
besuContext = context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand All @@ -46,17 +47,11 @@
@Slf4j
@AutoService(BesuPlugin.class)
public class LineaTransactionPoolValidatorPlugin extends AbstractLineaRequiredPlugin {
public static final String NAME = "linea";
private BesuConfiguration besuConfiguration;
private TransactionPoolValidatorService transactionPoolValidatorService;
private TransactionSimulationService transactionSimulationService;
private Optional<JsonRpcManager> rejectedTxJsonRpcManager = Optional.empty();

@Override
public Optional<String> getName() {
return Optional.of(NAME);
}

@Override
public void doRegister(final BesuContext context) {
besuConfiguration =
Expand Down Expand Up @@ -87,7 +82,10 @@ public void doRegister(final BesuContext context) {
@Override
public void start() {
super.start();
loadDenyListAndRegisterPluginTxValidatorFactory();
}

private void loadDenyListAndRegisterPluginTxValidatorFactory() {
try (Stream<String> lines =
Files.lines(
Path.of(new File(transactionPoolValidatorConfiguration().denyListPath()).toURI()))) {
Expand Down Expand Up @@ -124,6 +122,12 @@ public void start() {
}
}

@Override
public CompletableFuture<Void> reloadConfiguration() {
loadDenyListAndRegisterPluginTxValidatorFactory();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to look at rejectedTxJsonRpcManager - may need to do something different with that re start/stop lifecycle

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

factory field in TransactionPoolValidatorServiceImpl is not volatile so the change could not be seen immediately by all thread, said that I prefer that the registration is done one, and the reload only updates the deny list, so for example restricting the moving pieces to a wrap of the deny list in a mutable container that is set at startup and updated on every reload, without having to redo all the initialization of the plugin

return CompletableFuture.completedFuture(null);
}

@Override
public void stop() {
super.stop();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,10 @@
@Slf4j
@AutoService(BesuPlugin.class)
public class LineaTransactionSelectorPlugin extends AbstractLineaRequiredPlugin {
public static final String NAME = "linea";
private TransactionSelectionService transactionSelectionService;
private Optional<JsonRpcManager> rejectedTxJsonRpcManager = Optional.empty();
private BesuConfiguration besuConfiguration;

@Override
public Optional<String> getName() {
return Optional.of(NAME);
}

@Override
public void doRegister(final BesuContext context) {
transactionSelectionService =
Expand Down
Loading