diff --git a/besu-plugin/core/src/main/java/samba/BesuSambaPlugin.java b/besu-plugin/core/src/main/java/samba/BesuSambaPlugin.java index b32dced9..90bcf7e2 100644 --- a/besu-plugin/core/src/main/java/samba/BesuSambaPlugin.java +++ b/besu-plugin/core/src/main/java/samba/BesuSambaPlugin.java @@ -3,9 +3,13 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import com.google.auto.service.AutoService; +import com.sun.tools.sjavac.Log; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.ServiceManager; import org.hyperledger.besu.plugin.services.BesuConfiguration; @@ -14,150 +18,178 @@ import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.RpcEndpointService; import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import picocli.CommandLine; +import samba.api.HistoryService; import samba.rpc.GetBlockBodyByBlockHash; +import samba.rpc.GetBlockHeaderByBlockHash; +import samba.rpc.GetTransactionReceiptByBlockHash; @AutoService(BesuPlugin.class) -public class BesuSambaPlugin implements BesuPlugin { - private static final Logger LOG = LoggerFactory.getLogger(BesuSambaPlugin.class); - public static final String PLUGIN_NAME = "samba"; - private static final String CLI_OPTIONS_PREFIX = "--plugin-" + PLUGIN_NAME + "-"; - - private ServiceManager serviceManager; - private MetricCategoryRegistry metricCategoryRegistryService; - private BesuConfiguration besuConfigurationService; - private PicoCLIOptions picoCLIOptionsService; - protected MetricsSystem metricsSystemService; - private RpcEndpointService rpcEndpointService; - - private static final AtomicBoolean registrationTaskDone = new AtomicBoolean(false); - private static final AtomicBoolean startingTasksDone = new AtomicBoolean(false); - - @CommandLine.Option(names = CLI_OPTIONS_PREFIX + "host") - public String host = "0.0.0.0"; - - @CommandLine.Option(names = {"--plugin-samba-logging"}) - public String loggingLevel; - - @CommandLine.Option(names = {"--plugin-samba-data-path"}) - public String dataPath; - - private final CompletableFuture sambaSDKFuture = new CompletableFuture<>(); - - @Override - public void register(ServiceManager serviceManager) { - LOG.debug("Registering Samba plugin"); - this.serviceManager = serviceManager; - if (registrationTaskDone.compareAndSet(false, true)) { - this.metricCategoryRegistryService = - this.getBesuService(this.serviceManager, MetricCategoryRegistry.class); - this.besuConfigurationService = - this.getBesuService(this.serviceManager, BesuConfiguration.class); - this.picoCLIOptionsService = this.getBesuService(this.serviceManager, PicoCLIOptions.class); - // TODO create a function - this.picoCLIOptionsService.addPicoCLIOptions(PLUGIN_NAME, this); - this.rpcEndpointService = this.getBesuService(this.serviceManager, RpcEndpointService.class); - this.starRpcEndpoints(); // TODO use a completable future till samba is fully initialized. +public class BesuSambaPlugin implements BesuPlugin, HistoryService { + private static final Logger LOG = LoggerFactory.getLogger(BesuSambaPlugin.class); + public static final String PLUGIN_NAME = "samba"; + private static final String CLI_OPTIONS_PREFIX = "--plugin-" + PLUGIN_NAME + "-"; + + private ServiceManager serviceManager; + private MetricCategoryRegistry metricCategoryRegistryService; + private BesuConfiguration besuConfigurationService; + private PicoCLIOptions picoCLIOptionsService; + protected MetricsSystem metricsSystemService; + private RpcEndpointService rpcEndpointService; + + private static final AtomicBoolean registrationTaskDone = new AtomicBoolean(false); + private static final AtomicBoolean startingTasksDone = new AtomicBoolean(false); + + @CommandLine.Option(names = CLI_OPTIONS_PREFIX + "host") + public String host = "0.0.0.0"; + + @CommandLine.Option(names = {"--plugin-samba-logging"}) + public String loggingLevel; + + @CommandLine.Option(names = {"--plugin-samba-data-path"}) + public String dataPath; + + private final CompletableFuture sambaSDKFuture = new CompletableFuture<>(); + + @Override + public void register(ServiceManager serviceManager) { + LOG.debug("Registering Samba plugin"); + this.serviceManager = serviceManager; + if (registrationTaskDone.compareAndSet(false, true)) { + this.metricCategoryRegistryService = + this.getBesuService(this.serviceManager, MetricCategoryRegistry.class); + this.besuConfigurationService = + this.getBesuService(this.serviceManager, BesuConfiguration.class); + this.picoCLIOptionsService = this.getBesuService(this.serviceManager, PicoCLIOptions.class); + // TODO create a function + this.picoCLIOptionsService.addPicoCLIOptions(PLUGIN_NAME, this); + this.rpcEndpointService = this.getBesuService(this.serviceManager, RpcEndpointService.class); + this.starRpcEndpoints(); // TODO use a completable future till samba is fully initialized. + } } - } - - @Override - public Optional getName() { - return Optional.of(PLUGIN_NAME); - } - - @Override - public void start() { - LOG.info("Starting Samba plugin"); - if (startingTasksDone.compareAndSet(false, true)) { - SambaSDK sdk = this.initSamba(); - sambaSDKFuture.complete(sdk); - this.metricsSystemService = this.getBesuService(this.serviceManager, MetricsSystem.class); + + @Override + public Optional getName() { + return Optional.of(PLUGIN_NAME); } - } - - @Override - public void stop() { - LOG.info("Stopping Samba plugin"); - registrationTaskDone.set(false); - startingTasksDone.set(false); - this.besuConfigurationService = null; - this.metricCategoryRegistryService = null; - this.rpcEndpointService = null; - this.picoCLIOptionsService = null; - // TODO should we do something with Samba | its db ? - // TODO call samba to stop - } - - private SambaSDK initSamba() { - try { - String[] options = { - "--portal-subnetworks=history-network", - "--p2p-advertised-ip=" + host, - "--disable-json-rpc-server", - "--disable-rest--server", - "--logging=" + loggingLevel, - "--data-path=" + dataPath - }; - return Samba.init(options); - - } catch (Exception e) { - LOG.error("Halting Besu startup: exception in plugin startup: ", e); - e.printStackTrace(); - System.exit(1); - return null; // unreachable, but required to compile + + @Override + public void start() { + LOG.info("Starting Samba plugin"); + if (startingTasksDone.compareAndSet(false, true)) { + SambaSDK sdk = this.initSamba(); + sambaSDKFuture.complete(sdk); + this.metricsSystemService = this.getBesuService(this.serviceManager, MetricsSystem.class); + } + } + + @Override + public void stop() { + LOG.info("Stopping Samba plugin"); + registrationTaskDone.set(false); + startingTasksDone.set(false); + this.besuConfigurationService = null; + this.metricCategoryRegistryService = null; + this.rpcEndpointService = null; + this.picoCLIOptionsService = null; + // TODO should we do something with Samba | its db ? + // TODO call samba to stop + } + + private SambaSDK initSamba() { + try { + String[] options = { + "--portal-subnetworks=history-network", + "--p2p-advertised-ip=" + host, + "--disable-json-rpc-server", + "--disable-rest--server", + "--logging=" + loggingLevel, + "--data-path=" + dataPath + }; + return Samba.init(options); + + } catch (Exception e) { + LOG.error("Halting Besu startup: exception in plugin startup: ", e); + e.printStackTrace(); + System.exit(1); + return null; // unreachable, but required to compile + } + } + + private void starRpcEndpoints() { + var methods = List.of(new GetBlockBodyByBlockHash(this.sambaSDKFuture), + new GetBlockHeaderByBlockHash(this.sambaSDKFuture), + new GetTransactionReceiptByBlockHash(this.sambaSDKFuture), + new GetTransactionReceiptByBlockHash(this.sambaSDKFuture)); + methods.forEach( + method -> { + LOG.info( + "Registering RPC plugin endpoint {}_{}", method.getNamespace(), method.getName()); + rpcEndpointService.registerRPCEndpoint( + method.getNamespace(), method.getName(), method::execute); + }); + } + + private T getBesuService(ServiceManager context, Class clazz) { + return context + .getService(clazz) + .orElseThrow( + () -> + new RuntimeException( + "Unable to find given Besu service. Please ensure %s is registered." + .formatted(clazz.getName()))); + } + + + @Override + public Optional getBlockHeaderByBlockHash(Hash blockHash) { + try { + return this.sambaSDKFuture + .get().historyAPI().flatMap(history -> history.getBlockHeaderByBlockHash(blockHash)); + } catch (InterruptedException | ExecutionException e) { + LOG.debug("Error when executing GetBlockHeaderByBlockHash operation"); + } + return Optional.empty(); + } + + @Override + public Optional getBlockBodyByBlockHash(Hash blockHash) { + try { + return this.sambaSDKFuture.get() + .historyAPI() + .flatMap(history -> history.getBlockBodyByBlockHash(blockHash)); + } catch (InterruptedException | ExecutionException e) { + LOG.debug("Error when executing GetBlockBodyByBlockHash operation"); + } + return Optional.empty(); + } + + @Override + public Optional> getTransactionReceiptByBlockHash(Hash blockHash) { + try { + return this.sambaSDKFuture.get().historyAPI().flatMap(history -> history.getTransactionReceiptByBlockHash(blockHash)); + } catch (InterruptedException | ExecutionException e) { + LOG.debug("Error when executing GetTransactionReceiptByBlockHash operation"); + } + return Optional.empty(); } - } - - private void starRpcEndpoints() { - var methods = List.of(new GetBlockBodyByBlockHash(this.sambaSDKFuture)); - methods.forEach( - method -> { - LOG.info( - "Registering RPC plugin endpoint {}_{}", method.getNamespace(), method.getName()); - rpcEndpointService.registerRPCEndpoint( - method.getNamespace(), method.getName(), method::execute); - }); - } - - private T getBesuService(ServiceManager context, Class clazz) { - return context - .getService(clazz) - .orElseThrow( - () -> - new RuntimeException( - "Unable to find given Besu service. Please ensure %s is registered." - .formatted(clazz.getName()))); - } -} -/* - @Override - public Optional getBlockHeaderByBlockHash(Hash blockHash) { - return this.sambaSDK - .historyAPI() - .flatMap(history -> history.getBlockHeaderByBlockHash(blockHash)); - } - - @Override - public Optional getBlockBodyByBlockHash(Hash blockHash) { - return this.sambaSDK - .historyAPI() - .flatMap(history -> history.getBlockBodyByBlockHash(blockHash)); - } - - @Override - public Optional> getTransactionReceiptByBlockHash(Hash blockHash) { - return this.sambaSDK.historyAPI().flatMap(history -> history.getReceiptByBlockHash(blockHash)); - } - - @Override - public Optional getBlockHeaderByBlockNumber(long blockNumber) { - return this.sambaSDK - .historyAPI() - .flatMap(history -> history.getBlockHeaderByBlockNumber(blockNumber)); - } + @Override + public Optional getBlockHeaderByBlockNumber(String blockNumber) { + try { + return this.sambaSDKFuture.get() + .historyAPI() + .flatMap(history -> history.getBlockHeaderByBlockNumber(blockNumber)); + } catch (InterruptedException | ExecutionException e) { + LOG.debug("Error when executing GetBlockHeaderByBlockNumber operation"); + } + return Optional.empty(); + } } -*/ + diff --git a/besu-plugin/core/src/main/java/samba/api/HistoryService.java b/besu-plugin/core/src/main/java/samba/api/HistoryService.java index 87255518..2bccc60f 100644 --- a/besu-plugin/core/src/main/java/samba/api/HistoryService.java +++ b/besu-plugin/core/src/main/java/samba/api/HistoryService.java @@ -18,5 +18,6 @@ public interface HistoryService { Optional> getTransactionReceiptByBlockHash(Hash blockHash); - Optional getBlockHeaderByBlockNumber(long blockNumber); + //The characters in the string must all be decimal digits + Optional getBlockHeaderByBlockNumber(String blockNumber); } diff --git a/besu-plugin/core/src/main/java/samba/rpc/GetBlockBodyByBlockHash.java b/besu-plugin/core/src/main/java/samba/rpc/GetBlockBodyByBlockHash.java index c302f1e5..316627c9 100644 --- a/besu-plugin/core/src/main/java/samba/rpc/GetBlockBodyByBlockHash.java +++ b/besu-plugin/core/src/main/java/samba/rpc/GetBlockBodyByBlockHash.java @@ -5,7 +5,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; -import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; import samba.BesuSambaPlugin; import samba.SambaSDK; @@ -25,7 +25,7 @@ public String getNamespace() { @Override public String getName() { - return RpcMethod.GET_BLOCK_BODY_BY_HASH.getMethodName(); + return RpcMethod.GET_BLOCK_BODY_BY_BLOCK_HASH.getMethodName(); } @Override @@ -36,8 +36,8 @@ public Object execute(PluginRpcRequest rpcRequest) { return this.sambaSDKFuture .get() .historyAPI() - .flatMap(history -> history.getBlockHeaderByBlockHash(blockHash)) - .map(BlockHeader::toString) + .flatMap(history -> history.getBlockBodyByBlockHash(blockHash)) + .map(BlockBody::toString) .orElse(""); } catch (JsonRpcParameter.JsonRpcParameterException | ExecutionException diff --git a/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockHash.java b/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockHash.java new file mode 100644 index 00000000..04cac5cf --- /dev/null +++ b/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockHash.java @@ -0,0 +1,48 @@ +package samba.rpc; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import samba.BesuSambaPlugin; +import samba.SambaSDK; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class GetBlockHeaderByBlockHash implements PluginRpcMethod { + + private final CompletableFuture sambaSDKFuture; + + public GetBlockHeaderByBlockHash(CompletableFuture sambaSDKFuture) { + this.sambaSDKFuture = sambaSDKFuture; + } + + @Override + public String getNamespace() { + return BesuSambaPlugin.PLUGIN_NAME; + } + + @Override + public String getName() { + return RpcMethod.GET_BLOCK_HEADER_BY_BLOCK_HASH.getMethodName(); + } + + @Override + public Object execute(PluginRpcRequest rpcRequest) { + try { + String rawParam = new JsonRpcParameter().required(rpcRequest.getParams(), 0, String.class); + Hash blockHash = Hash.fromHexString(rawParam); + return this.sambaSDKFuture + .get() + .historyAPI() + .flatMap(history -> history.getBlockHeaderByBlockHash(blockHash)) + .map(BlockHeader::toString) + .orElse(""); + } catch (JsonRpcParameter.JsonRpcParameterException + | ExecutionException + | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockNumber.java b/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockNumber.java new file mode 100644 index 00000000..7cc7437e --- /dev/null +++ b/besu-plugin/core/src/main/java/samba/rpc/GetBlockHeaderByBlockNumber.java @@ -0,0 +1,48 @@ +package samba.rpc; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import samba.BesuSambaPlugin; +import samba.SambaSDK; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class GetBlockHeaderByBlockNumber implements PluginRpcMethod { + + private final CompletableFuture sambaSDKFuture; + + public GetBlockHeaderByBlockNumber(CompletableFuture sambaSDKFuture) { + this.sambaSDKFuture = sambaSDKFuture; + } + + @Override + public String getNamespace() { + return BesuSambaPlugin.PLUGIN_NAME; + } + + @Override + public String getName() { + return RpcMethod.GET_BLOCK_HEADER_BY_BLOCK_NUMBER.getMethodName(); + } + + @Override + public Object execute(PluginRpcRequest rpcRequest) { + try { + String blockNumber = new JsonRpcParameter().required(rpcRequest.getParams(), 0, String.class); + return this.sambaSDKFuture + .get() + .historyAPI() + .flatMap(history -> history.getBlockHeaderByBlockNumber(blockNumber)) + .map(BlockHeader::toString) + .orElse(""); + } catch (JsonRpcParameter.JsonRpcParameterException + | ExecutionException + | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/besu-plugin/core/src/main/java/samba/rpc/GetTransactionReceiptByBlockHash.java b/besu-plugin/core/src/main/java/samba/rpc/GetTransactionReceiptByBlockHash.java new file mode 100644 index 00000000..5d2d74ed --- /dev/null +++ b/besu-plugin/core/src/main/java/samba/rpc/GetTransactionReceiptByBlockHash.java @@ -0,0 +1,53 @@ +package samba.rpc; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.data.TransactionReceipt; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import samba.BesuSambaPlugin; +import samba.SambaSDK; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +public class GetTransactionReceiptByBlockHash implements PluginRpcMethod { + + private final CompletableFuture sambaSDKFuture; + + public GetTransactionReceiptByBlockHash(CompletableFuture sambaSDKFuture) { + this.sambaSDKFuture = sambaSDKFuture; + } + + @Override + public String getNamespace() { + return BesuSambaPlugin.PLUGIN_NAME; + } + + @Override + public String getName() { + return RpcMethod.GET_TRANSACTION_RECEIPT_BY_BLOCK_HASH.getMethodName(); + } + + @Override + public Object execute(PluginRpcRequest rpcRequest) { + try { + String rawParam = new JsonRpcParameter().required(rpcRequest.getParams(), 0, String.class); + Hash blockHash = Hash.fromHexString(rawParam); + return this.sambaSDKFuture + .get() + .historyAPI() + .flatMap(history -> history.getTransactionReceiptByBlockHash(blockHash)) + .map(transactionReceipts -> transactionReceipts.stream() + .map(TransactionReceipt::toString) + .collect(Collectors.toList())) + .orElse(Collections.emptyList()); + } catch (JsonRpcParameter.JsonRpcParameterException + | ExecutionException + | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/besu-plugin/core/src/main/java/samba/rpc/RpcMethod.java b/besu-plugin/core/src/main/java/samba/rpc/RpcMethod.java index ec1d281f..8b475bec 100644 --- a/besu-plugin/core/src/main/java/samba/rpc/RpcMethod.java +++ b/besu-plugin/core/src/main/java/samba/rpc/RpcMethod.java @@ -5,7 +5,10 @@ public enum RpcMethod { GET_VERSION("getVersion"), - GET_BLOCK_BODY_BY_HASH("getBlockBodyByBlockHash"); + GET_BLOCK_BODY_BY_BLOCK_HASH("getBlockBodyByBlockHash"), + GET_BLOCK_HEADER_BY_BLOCK_HASH("getBlockHeaderByBlockHash"), + GET_TRANSACTION_RECEIPT_BY_BLOCK_HASH("getBlockHeaderByBlockHash"), + GET_BLOCK_HEADER_BY_BLOCK_NUMBER("getBlockHeaderByBlockNumber"); private final String methodName; diff --git a/besu-plugin/docker/Dockerfile b/besu-plugin/docker/Dockerfile index 11bc5d50..568378aa 100644 --- a/besu-plugin/docker/Dockerfile +++ b/besu-plugin/docker/Dockerfile @@ -1,5 +1,6 @@ #FROM meldsun/besu:discovery -FROM meldsun/besu:discovery-arm64 +#FROM meldsun/besu:discovery-arm64 +FROM hyperledger/besu:25.6.0 COPY --chown=besu:besu core/build/libs/*.jar /opt/besu/plugins/ EXPOSE 30303 8545 8551 8546 8547 8888 9545 8008 5051 9090 9000/udp 9000 diff --git a/core/src/main/java/samba/api/HistoryAPI.java b/core/src/main/java/samba/api/HistoryAPI.java index 21ed0e50..b2e60f02 100644 --- a/core/src/main/java/samba/api/HistoryAPI.java +++ b/core/src/main/java/samba/api/HistoryAPI.java @@ -50,13 +50,11 @@ Optional offer( Optional>> getRoutingTable(); - // For Besu - Optional getBlockHeaderByBlockHash(Hash blockHash); Optional getBlockBodyByBlockHash(Hash blockHash); - Optional> getReceiptByBlockHash(Hash blockHash); + Optional> getTransactionReceiptByBlockHash(Hash blockHash); - Optional getBlockHeaderByBlockNumber(long blockNumber); + Optional getBlockHeaderByBlockNumber(String blockNumber); } diff --git a/core/src/main/java/samba/api/HistoryAPIClient.java b/core/src/main/java/samba/api/HistoryAPIClient.java index 129eff8d..2127efa9 100644 --- a/core/src/main/java/samba/api/HistoryAPIClient.java +++ b/core/src/main/java/samba/api/HistoryAPIClient.java @@ -109,13 +109,13 @@ public Optional getBlockBodyByBlockHash(Hash blockHash) { } @Override - public Optional> getReceiptByBlockHash(Hash blockHash) { + public Optional> getTransactionReceiptByBlockHash(Hash blockHash) { return GetTransactionReceiptByBlockHash.execute(this.historyNetworkInternalAPI, blockHash); } @Override - public Optional getBlockHeaderByBlockNumber(long blockNumber) { + public Optional getBlockHeaderByBlockNumber(String blockNumber) { return GetBlockHeaderByBlockNumber.execute( - this.historyNetworkInternalAPI, UInt64.fromLongBits(blockNumber)); + this.historyNetworkInternalAPI, UInt64.valueOf(blockNumber)); } }