Skip to content
This repository has been archived by the owner on Sep 13, 2024. It is now read-only.

feat: Implement RPC method #2

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The plugin jar will be available at `build/libs/finalization-updater-besu-plugin
Drop the `finalization-updater-besu-plugin-<version>.jar` in the `/plugins` folder under Besu installation. This plugin
will expose following RPC methods:
```shell
linea_updateFinalizedBlockV1(finalizedBlockNumber: Long): Boolean
linea_ipc_updateFinalizedBlockV1(finalizedBlockNumber: Long): Boolean
```

## License
Expand Down
56 changes: 43 additions & 13 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,76 @@ plugins {
project.group = "net.consensys.linea.besu.plugin"

repositories {
mavenLocal() // for testing locally build Besu plugin

// Use Maven Central for resolving dependencies.
mavenCentral()

// For Besu plugin dependencies
// for linea-besu plugin dependencies
maven {
url = uri("https://artifacts.consensys.net/public/linea-besu/maven/")
content { includeGroupByRegex("io\\.consensys\\..*") }
}

// For Besu dependencies
maven {
url = uri("https://hyperledger.jfrog.io/artifactory/besu-maven/")
content { includeGroupByRegex("org\\.hyperledger\\.besu($|\\..*)") }
}

// for consensys dependencies
maven {
url = uri("https://artifacts.consensys.net/public/maven/maven/")
content { includeGroupByRegex("tech\\.pegasys(\\..*)?") }
}
}

// project dependencies. Testing dependencies are declared in the testing block.
dependencies {
// This project jar is not supposed to be used as compilation dependency.
// `api` is used here to distinguish between dependencies which should be used IF it is to be used
// as a dependency during compiling some other library that depends on this project.
api(libs.besu.plugin.api)
api(libs.besu.internal.api) {
exclude(group = "org.apache.logging.log4j", module = "log4j-slf4j2-impl")
}

// https://github.com/google/auto/tree/main/service
annotationProcessor(libs.google.auto.service)
implementation(libs.google.auto.service.annotations)
implementation(libs.slf4j.api)
implementation(libs.picocli)

// testing dependencies
testImplementation(libs.assertj.core)
testImplementation(libs.junit.jupiter)
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly(libs.slf4j.simple)

// errorprone dependencies
errorprone(libs.google.error.prone)
}

// Apply a specific Java toolchain to ease working on different environments.
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
testing {
suites {
// shared with every JVM test suite
withType<JvmTestSuite> {
useJUnitJupiter()
dependencies {
implementation(libs.assertj.core)
runtimeOnly(libs.slf4j.simple)
}
}

// default test suite
val test by getting(JvmTestSuite::class)

val integrationTest by
registering(JvmTestSuite::class) {
dependencies { implementation(project()) }

// make integrationTest suite run after the default test suite
targets { all { testTask.configure { shouldRunAfter(test) } } }
}
}
}

tasks.named("check") { dependsOn(testing.suites.named("integrationTest")) }

spotless {
java {
importOrder()
Expand Down
5 changes: 3 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[versions]
assertj = "3.26.3"
besu = "24.6.0"
linea-besu = "24.7-develop-26f89ac"
google-auto-service = "1.1.1"
google-error-prone = "2.29.2"
gradle-errorprone-plugin = "4.0.1"
Expand All @@ -18,7 +18,8 @@ spotless = "6.25.0"

[libraries]
assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" }
besu-plugin-api = {module = "org.hyperledger.besu:plugin-api", version.ref = "besu"}
besu-plugin-api = {module = "io.consensys.linea-besu:plugin-api", version.ref = "linea-besu"}
besu-internal-api = {module = "io.consensys.linea-besu.internal:api", version.ref = "linea-besu"}
google-auto-service = { module = "com.google.auto.service:auto-service", version.ref = "google-auto-service" }
google-auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "google-auto-service" }
google-error-prone = { module = "com.google.errorprone:error_prone_core", version.ref = "google-error-prone" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2024, Consensys Software Inc.
// SPDX-License-Identifier: Apache-2.0
package net.consensys.linea;

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

import org.junit.jupiter.api.Test;

public class FinalizationUpdaterPluginIntegrationTest {
@Test
void getVersionIsValid() {
FinalizationUpdaterPlugin plugin = new FinalizationUpdaterPlugin();
assertThat(plugin.getVersion()).startsWith("FinalizationUpdaterPlugin");
}
}
25 changes: 25 additions & 0 deletions src/main/java/net/consensys/linea/FinalizationUpdaterPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.google.auto.service.AutoService;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.RpcEndpointService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -19,6 +21,29 @@ public class FinalizationUpdaterPlugin implements BesuPlugin {
@Override
public void register(final BesuContext besuContext) {
LOG.trace("Registering plugin ...");

final RpcEndpointService rpcEndpointService =
besuContext
.getService(RpcEndpointService.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain RpcEndpointService from the BesuContext."));

final BlockchainService blockchainService =
besuContext
.getService(BlockchainService.class)
.orElseThrow(
() ->
new RuntimeException(
"Failed to obtain BlockchainService from the BesuContext."));

final FinalizationUpdaterRpcMethod finalizationUpdaterRpcMethod =
new FinalizationUpdaterRpcMethod(blockchainService);
rpcEndpointService.registerRPCEndpoint(
FinalizationUpdaterRpcMethod.RPC_NAMESPACE,
FinalizationUpdaterRpcMethod.RPC_METHOD_NAME,
finalizationUpdaterRpcMethod::execute);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2024, Consensys Software Inc.
// SPDX-License-Identifier: Apache-2.0
package net.consensys.linea;

import java.util.Optional;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.plugin.data.BlockContext;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.exception.PluginRpcEndpointException;
import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Implementation of the RPC method: {@code linea_updateFinalizedBlockV1(finalizedBlockNumber:
* Long):Boolean }
*/
public class FinalizationUpdaterRpcMethod {
private static final Logger LOG = LoggerFactory.getLogger(FinalizationUpdaterRpcMethod.class);
static final String RPC_NAMESPACE = "linea_ipc";
static final String RPC_METHOD_NAME = "updateFinalizedBlockV1";

private final BlockchainService blockchainService;
private final JsonRpcParameter parameterParser = new JsonRpcParameter();

/**
* Constructor for the FinalizationUpdaterRpcMethod.
*
* @param blockchainService An instance of the BlockchainService.
*/
public FinalizationUpdaterRpcMethod(final BlockchainService blockchainService) {
LOG.trace("FinalizationUpdaterRpcMethod constructor called");
this.blockchainService = blockchainService;
}

/**
* Executes the RPC method. This method will be called by Besu RPC service.
*
* @param request Instance of PluginRpcRequest. The request object contains the parameters passed.
* @return Boolean.TRUE if the finalized block number is successfully updated.
* @throws PluginRpcEndpointException if the block number is invalid or the block is not found.
*/
public Boolean execute(final PluginRpcRequest request) {
LOG.trace("FinalizationUpdaterRpcMethod execute called");

final Long finalizedBlockNumber = parseResult(request);

// lookup finalized block by number in local chain
final Optional<BlockContext> finalizedBlock =
blockchainService.getBlockByNumber(finalizedBlockNumber);
if (finalizedBlock.isEmpty()) {
throw new PluginRpcEndpointException(
RpcErrorType.BLOCK_NOT_FOUND,
"Block not found in the local chain: " + finalizedBlockNumber);
}

// Persist the finalized block number
blockchainService.setFinalizedBlock(finalizedBlock.get().getBlockHeader().getBlockHash());

return Boolean.TRUE;
}

private Long parseResult(final PluginRpcRequest request) {
Long blockNumber;
try {
final Object[] params = request.getParams();
blockNumber = parameterParser.required(params, 0, Long.class);
} catch (final Exception e) {
throw new PluginRpcEndpointException(RpcErrorType.INVALID_PARAMS, e.getMessage());
}

if (blockNumber <= 0) {
throw new PluginRpcEndpointException(
RpcErrorType.INVALID_PARAMS, "Block number must be greater than 0");
}

return blockNumber;
}
}
Loading